80 Commits

Author SHA1 Message Date
401a76da84 Add middle mouse navigation on map 2023-05-13 15:00:16 -07:00
694eb006b9 Make map editor more like pico 8 2023-05-13 12:15:59 -07:00
f8c1cebedb Some slight refactoring 2023-05-10 20:36:18 -07:00
04f206814c Add pi and directions 2023-05-10 20:10:32 -07:00
ae3fa89a8b Note that we have printh as log 2023-05-10 20:02:25 -07:00
79738cfb79 Add pset 2023-05-10 19:59:14 -07:00
d7fec98714 Add ellipse drawing 2023-05-10 19:58:01 -07:00
6dc5127926 Add circle drawing 2023-05-10 19:23:36 -07:00
e955a4c00d Camera and outline rect functions 2023-05-10 00:06:08 -07:00
b394f81477 Fix map stuff, and implement builtin funcs for it 2023-05-09 23:50:27 -07:00
ef8cb2c4cf Reorder 2023-05-09 23:08:07 -07:00
d68a207df6 Comparing to pico-8 builtins 2023-05-09 23:06:09 -07:00
26c0ff590c Remove log 2023-05-09 20:29:57 -07:00
38eabd380a Spritesheets get 2 pages 2023-05-09 20:29:25 -07:00
90b97a30bd Slight UI improvements 2023-05-09 19:48:51 -07:00
292d1d365e Remove console log 2023-05-09 19:42:30 -07:00
f8b3f5d645 Basic map editor! 2023-05-09 19:42:02 -07:00
194209f18f Make code mouse more reliable with scroll position 2023-05-09 08:21:34 -07:00
1211891f53 Use alphabetic characters, so button symbols can be used as identifiers 2023-05-09 08:19:20 -07:00
2ac5f3dff7 dumb thing 2023-05-08 23:22:41 -07:00
b248a016a9 Make mouse behavior better 2023-05-08 23:14:01 -07:00
7f9e873323 remove extra line 2023-05-08 22:27:39 -07:00
e5276de775 Add basic mouse support in code editor 2023-05-08 22:20:58 -07:00
a7ed7b87f2 fix undo I think 2023-05-08 22:13:01 -07:00
8c750ac2dc Fix builtins syntax highlighting 2023-05-08 21:48:16 -07:00
ad5acdeb12 Allow variable width in font 2023-05-08 21:39:08 -07:00
107a5370b1 Fix up arrowing to top line always shifting right by one 2023-05-07 14:05:52 -07:00
5ed92b5ff4 Improved color palette 2023-05-07 13:58:55 -07:00
c53373ea47 Make it work on windows 2023-05-07 13:00:04 -07:00
87d3e6fc9b Trash button only shows up on sheet page 2023-05-07 10:04:50 -07:00
cc03a0e765 Add more to the font 2023-05-07 10:03:19 -07:00
fcf78f3d56 Add cross-compile options to deno.json 2023-05-06 18:00:22 -07:00
5760a3f03b Allow compilation by using cdn 2023-05-06 17:18:49 -07:00
6bd5f7ae01 Encountered undo error, but can't repro so adding logs 2023-05-06 15:24:29 -07:00
3518538b39 switch to empty initial cart 2023-05-06 15:16:53 -07:00
86d8a8b166 allow saving and loading carts 2023-05-06 15:12:42 -07:00
13b600eb95 Clean up comments 2023-05-06 15:01:26 -07:00
9f67a59033 Add trash button to delete sheet 2023-05-06 15:01:01 -07:00
64c889e16a Add some instructional text 2023-05-06 14:53:37 -07:00
471fa9e0b6 Add sheet viewer page 2023-05-06 14:49:46 -07:00
0adfdabffa Draw full text selection 2023-05-06 12:24:40 -07:00
2e3b689c16 Remove completed todo comment 2023-05-06 12:19:40 -07:00
24bed7bd89 remove console.logs 2023-05-06 12:19:17 -07:00
5d4db0a914 toggle comments with ctrl+slash 2023-05-06 12:18:37 -07:00
60542b63c0 Improved syntax highlighting 2023-05-06 12:05:02 -07:00
7bf0838e4e Remove the todo 2023-05-06 11:46:09 -07:00
550f1b44b2 Make enter play nicer with indentation 2023-05-06 11:45:59 -07:00
3ad23f3a91 Add undo-redo 2023-05-06 11:35:02 -07:00
9685568f90 Clipboard! 2023-05-06 10:54:27 -07:00
b02d5155bd Add a todo comment 2023-05-06 10:13:49 -07:00
4b99599b31 Remove completed todo comment 2023-05-06 10:12:48 -07:00
12bc0cb385 Syntax highlighting 2023-05-06 10:00:41 -07:00
1fa58961fc Remove done todo comment 2023-05-06 08:56:27 -07:00
99eb6b82f2 Implement scrolling in code tab 2023-05-05 20:08:54 -07:00
5e4b76ebb3 set default back to code tab 2023-05-05 16:42:03 -07:00
9d2dc99a32 Make tabs for code and sprite editors 2023-05-05 16:39:51 -07:00
a3abb0d2d3 small sprite editor improvements 2023-05-05 16:17:55 -07:00
6c9710d4d9 Mouse! 2023-05-05 16:02:23 -07:00
f2b5978cae Starting on sprite editor 2023-05-05 14:59:52 -07:00
b87529bf56 Add a bunch of TODOs 2023-05-05 12:28:43 -07:00
b58a0d8cb1 Make changes playable 2023-05-05 12:21:14 -07:00
99655e663c delete key 2023-05-05 12:10:01 -07:00
2e8923e2e7 fix selection on tab key 2023-05-05 12:05:02 -07:00
e52c8c69a0 tab key works 2023-05-05 12:01:48 -07:00
1e91232bd6 very basic code editing! 2023-05-05 11:52:08 -07:00
6b90d883e9 Fix sample cart, and get ready for edit mode 2023-05-04 20:27:01 -07:00
2a7003b443 Improving repl more 2023-05-04 20:14:48 -07:00
dca54e76ec Add lowercase 2023-05-03 21:29:11 -07:00
ce7da27cc3 More repl improvements 2023-05-03 16:47:09 -07:00
1482288b0c almost a real repl 2023-05-03 15:17:27 -07:00
7de521bd39 Show caret, backspace, and left-right arrows 2023-05-03 13:44:28 -07:00
99a8c500c7 Better typing 2023-05-02 18:44:27 -07:00
253b8e9567 Starting on keyboard stuff 2023-05-02 18:17:31 -07:00
fdc8f97aee some more cart infrastructure 2023-05-02 17:06:54 -07:00
a7b675d541 Able to eval js with my scope! 2023-05-01 18:42:55 -07:00
5d742d5964 split stuff out 2023-05-01 11:12:08 -07:00
1781ae3bba adding text 2023-04-29 20:07:06 -07:00
078b7806dd it kinda works! 2023-04-29 15:16:35 -07:00
e85dbb1a33 graphics 2023-04-29 14:34:26 -07:00
d7c2d5adb9 draw triangle 2023-04-28 20:01:48 -07:00
41 changed files with 4428 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"deno.enable": true,
"deno.unstable": true
}

View File

@ -1,2 +1,13 @@
# fantasy-console
To run,
```
deno task run
```
To compile
```
deno task build
```
You may need to install `xsel` on Linux machines to use this.

195
builtins.ts Normal file
View File

@ -0,0 +1,195 @@
import {
setPixelsInRect,
clearScreen,
fillRect,
cameraPos,
fillCircle,
outlineCircle,
fillEllipse,
outlineEllipse,
setPixelColor,
} from "./window.ts";
import { CHAR, Font, font } from "./font.ts";
import { K, keyDown, keyPressed, keyReleased } from "./keyboard.ts";
import { addToContext, runCode } from "./runcode.ts";
import { resetRepl } from "./repl.ts";
import { COLOR } from "./colors.ts";
import { getSheet, getCodeSheet, getMapSheet } from "./sheet.ts";
import { saveCart, loadCart } from "./cart.ts";
import { outlineRect } from "./util.ts";
let spritesheet: number | null = null;
export const getSpritesheet = () => {
return spritesheet;
}
export const useSpritesheet = (sheet: number) => {
spritesheet = sheet;
}
export const drawSprite = (x: number, y: number, spr: number) => {
if (!spritesheet) {
return;
}
const {sheet_type, value: sprites} = getSheet(spritesheet);
if (sheet_type !== "spritesheet") {
throw "Trying to run a non-code sheet as code."
}
setPixelsInRect(x, y, 8, sprites[spr]);
}
export const drawIcon = (x: number, y: number, icon: Array<number>, color: number) => {
setPixelsInRect(x, y, 8, icon.map(n => n*color));
}
export const measureCharFont = (char: string, fnt: Font) => {
return (fnt.chars[char]?.length ?? 0)/fnt.height;
}
export const drawCharFont = (x: number, y: number, char: string, fnt: Font, color: number) => {
const w = measureCharFont(char, fnt);
if (!fnt.chars[char]) {
return 0;
}
setPixelsInRect(x, y, w, fnt.chars[char].map(n => n*color));
return w;
}
export const drawTextFont = (x: number, y: number, text: string, fnt: Font, color?: number) => {
let dx = 0;
[...text].forEach((char) => {
dx += 1+drawCharFont(x+dx, y, char, fnt, color ?? COLOR.WHITE);
});
return dx-1;
}
export const measureTextFont = (text: string, fnt: Font) => {
let w = 0;
[...text].forEach((char) => {
w += measureCharFont(char, fnt)+1;
});
return Math.max(0, w-1);
}
export const drawText = (x: number, y: number, text: string, color?: number) => {
return drawTextFont(x, y, text, font, color);
}
export const measureText = (text: string) => {
return measureTextFont(text, font);
}
export const camera = (x: number, y: number) => {
cameraPos.x = x;
cameraPos.y = y;
};
const faux = {
// Graphics
cls: () => {
resetRepl();
clearScreen();
},
camera,
sprsht: useSpritesheet,
spr: drawSprite,
txt: drawText,
rectfill: fillRect,
rect: outlineRect,
circfill: fillCircle,
circ: outlineCircle,
ovalfill: fillEllipse,
oval: outlineEllipse,
pset: setPixelColor,
map: (mapSheet: number, tileX: number, tileY: number, screenX: number, screenY: number, tileW: number, tileH: number) => {
const originalSpritesheet = getSpritesheet() ?? 0;
getMapSheet(mapSheet).values.forEach(([sprSheet, spr], i) => {
const x = i%64;
const y = Math.floor(i/64);
if (x >= tileX && y >= tileY && x < tileX + tileW && y < tileY + tileH) {
useSpritesheet(sprSheet);
drawSprite(screenX + (x-tileX)*8, screenY + (y-tileY)*8, spr);
}
});
useSpritesheet(originalSpritesheet);
},
// Map
mgetsht: (mapSheet: number, x: number, y: number) => {
if (x < 0 || x >= 64 || y < 0 || y >= 64) {
return undefined;
}
return getMapSheet(mapSheet).get(x, y)[0];
},
mgetspr: (mapSheet: number, x: number, y: number) => {
if (x < 0 || x >= 64 || y < 0 || y >= 64) {
return undefined;
}
return getMapSheet(mapSheet).get(x, y)[1];
},
mset: (mapSheet: number, x: number, y: number, sprSheet: number, spr: number) => {
if (x < 0 || x >= 64 || y < 0 || y >= 64) {
return;
}
getMapSheet(mapSheet).set(x, y, [sprSheet, spr]);
},
// Input
[CHAR.UP]: K.ARROW_UP,
[CHAR.DOWN]: K.ARROW_DOWN,
[CHAR.LEFT]: K.ARROW_LEFT,
[CHAR.RIGHT]: K.ARROW_RIGHT,
btn: keyDown,
btnp: keyPressed,
btnr: keyReleased,
// Cart
save: saveCart,
load: loadCart,
// JS
Array,
BigInt: BigInt,
Boolean,
Date,
Error,
Function,
Infinity: Infinity,
JSON: JSON,
Map,
NaN: NaN,
Number,
Object,
Promise,
Proxy,
Reflect: Reflect,
RegExp,
Set,
String,
Symbol: Symbol,
WeakMap,
WeakRef,
WeakSet,
isFinite,
isNaN,
// Math
max: Math.max,
min: Math.min,
floor: Math.floor,
ceil: Math.ceil,
sin: Math.sin,
cos: Math.cos,
atan2: Math.atan2,
sqrt: Math.sqrt,
abs: Math.abs,
rand: Math.random,
[CHAR.PI]: Math.PI,
// Other
code: (n: number) => {
return runCode(getCodeSheet(n));
},
log: console.log,
};
for (const key in faux) {
addToContext(key, faux[key as keyof typeof faux]);
}
export default faux;

22
cart.ts Normal file
View File

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

1
carts/empty.fx Normal file
View File

@ -0,0 +1 @@
[{"sheet_type":"code","value":""},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null}]

707
carts/test.fx Normal file
View File

@ -0,0 +1,707 @@
[
{
"sheet_type": "code",
"value": "x = code(1);\nreturn {\n\tinit: () => {y = 0},\n\tupdate: () => {\n\t\ty += speed;\n\t\tif (y > 127) {\n\t\t\ty = -6\n\t\t}\n\t},\n\tdraw: () => {\n\t\tcls();\n\t\ttxt(x, y, 'hello world')\n\t}\n}"
},
{
"sheet_type": "code",
"value": "sprsht(15);\nspeed = 2;\nreturn 8;"
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "none",
"value": null
},
{
"sheet_type": "spritesheet",
"value": [
[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 2, 0, 0,
0, 0, 0, 2, 2, 0, 0, 0,
0, 0, 0, 2, 2, 0, 0, 0,
0, 0, 2, 0, 0, 2, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
],
[
1, 1, 1, 1, 1, 1, 1, 1,
1, 3, 3, 1, 1, 3, 3, 1,
1, 3, 3, 1, 1, 3, 3, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 3, 3, 1, 1, 3, 3, 1,
1, 3, 3, 1, 1, 3, 3, 1,
1, 1, 1, 1, 1, 1, 1, 1
],
[
6, 6, 6, 4, 4, 4, 5, 5,
6, 6, 4, 4, 4, 5, 5, 5,
6, 4, 4, 4, 5, 5, 5, 6,
4, 4, 4, 5, 5, 5, 6, 6,
4, 4, 5, 5, 5, 6, 6, 6,
4, 5, 5, 5, 6, 6, 6, 4,
5, 5, 5, 6, 6, 6, 4, 4,
5, 5, 6, 6, 6, 4, 4, 4
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
]
]
}
]

1
carts/tmp/circ.fx Normal file
View File

@ -0,0 +1 @@
[{"sheet_type":"code","value":"// Sample\n\nlet r = 0;\nlet d = 1;\nreturn {\n\tinit() {},\n\tupdate() {\n\t\tif (r >= 20) {\n\t\t\td = -1;\n\t\t} else if (r < 0) {\n\t\t\td = 1;\n\t\t}\n\t\tr += d*0.1;\t\n\t},\n\tdraw() {\n\t\tcls();\n\t\tcirc(50, 50, 6, 17);\n\t\ttxt(10,10,\"Hello, World!\");\n\t}\n}"},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null}]

1
carts/tmp/cyandino.fx Normal file

File diff suppressed because one or more lines are too long

1
carts/tmp/maptest.fx Normal file

File diff suppressed because one or more lines are too long

1
carts/tmp/oval.fx Normal file
View File

@ -0,0 +1 @@
[{"sheet_type":"code","value":"// Sample\n\nlet r = 0;\nlet d = 1;\nreturn {\n\tinit() {},\n\tupdate() {\n\t\tif (r >= 20) {\n\t\t\td = -1;\n\t\t} else if (r < 0) {\n\t\t\td = 1;\n\t\t}\n\t\tr += d*0.1;\t\n\t},\n\tdraw() {\n\t\tcls();\n\t\trectfill(50,50,46,21,6);\n\t\toval(50, 50, 95, 70, 17);\n\t\ttxt(10,10,\"Hello, World!\");\n\t}\n}"},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null}]

1
carts/tmp/pa6.fx Normal file

File diff suppressed because one or more lines are too long

1
carts/tmp/pa7.fx Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
[{"sheet_type":"code","value":"\nx = 0;\nbool = true;\nstring = \"hi\";\n\nreturn {\n\tinit() {\n\t\t// init\n\t},\n\tupdate() {\n\t\t// update\n\t},\n\tdraw() {\n\t\t// draw\n\t\tcls();\n\t\ttxt(20, 20, \"hello, world\");\n\t}\n}"},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null}]

View File

@ -0,0 +1 @@
[{"sheet_type":"code","value":"\nx = 0;\nbool = true;\nstring = \"hi\";\ntext = code(1);\n\nreturn {\n\tinit() {\n\t\t// init\n\t},\n\tupdate() {\n\t\t// update\n\t},\n\tdraw() {\n\t\t// draw\n\t\tcls();\n\t\ttxt(20, 20, text);\n\t}\n}"},{"sheet_type":"code","value":"return \"hi from sheet 01.\";"},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

694
codetab.ts Normal file
View File

@ -0,0 +1,694 @@
import { clearScreen, fillRect } from "./window.ts";
import { CHAR, font } from "./font.ts";
import { drawText, measureText } from "./builtins.ts";
import { COLOR } from "./colors.ts";
import { getCodeSheet, setSheet } from "./sheet.ts";
import { K, ctrlKeyDown, getKeyboardString, keyPressed, shiftKeyDown } from "./keyboard.ts";
import { clipboard, tokenize } from "./deps.ts";
import { getBuiltins } from "./runcode.ts";
import { page } from "./viewsheets.ts";
import { mouseDown, mouseHeld, mousePos } from "./mouse.ts";
const historyDebounceFrames = 20;
const fontHeight = font.height;
const keywords = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"let",
"static",
"yield",
"await",
"enum",
"implements",
"interface",
"package",
"private",
"protected",
"public",
"=>",
];
const values = [
"false",
"null",
"true",
"undefined",
"NaN",
"Infinity",
CHAR.PI,
];
const operator = [
"&&",
"||",
"??",
"--",
"++",
".",
"?.",
"<",
"<=",
">",
">=",
"!=",
"!==",
"==",
"===",
"+",
"-",
"%",
"&",
"|",
"^",
"/",
"*",
"**",
"<<",
">>",
">>>",
"=",
"+=",
"-=",
"%=",
"&=",
"|=",
"^=",
"/=",
"*=",
"**=",
"<<=",
">>=",
">>>=",
"!",
"?",
"~",
"...",
];
const punctuation = [
"(",
")",
"[",
"]",
"{",
"}",
".",
":",
";",
",",
];
const builtinColor = COLOR.BLUE;
const keywordColor = COLOR.PURPLE;
const operatorColor = COLOR.CYAN;
const valueColor = COLOR.ORANGE;
const stringColor = COLOR.GREEN;
const regexColor = COLOR.PINK;
const punctuationColor = COLOR.LIGHTGRAY;
const commentColor = COLOR.DARKGREEN;
const identifierColor = COLOR.YELLOW;
const invalidColor = COLOR.RED;
const caretColor = COLOR.WHITE;
const selectionColor = COLOR.DARKBLUE;
const backgroundColor = COLOR.DARKERBLUE;
const tokenColors = {
"StringLiteral": stringColor,
"NoSubstitutionTemplate": stringColor,
"TemplateHead": stringColor,
"TemplateMiddle": stringColor,
"TemplateTail": stringColor,
"RegularExpressionLiteral": regexColor,
"MultiLineComment": commentColor,
"SingleLineComment": commentColor,
"IdentifierName": identifierColor,
"PrivateIdentifier": identifierColor,
"NumericLiteral": valueColor,
"Punctuator": punctuationColor,
"WhiteSpace": punctuationColor,
"LineTerminatorSequence": punctuationColor,
"Invalid": invalidColor,
}
const transformForCopy = (text: string) => {
text = text.replaceAll(CHAR.UP, "⬆️");
text = text.replaceAll(CHAR.LEFT, "⬅️");
text = text.replaceAll(CHAR.DOWN, "⬇️");
text = text.replaceAll(CHAR.RIGHT, "➡️");
return text;
}
const transformForPaste = (text: string) => {
let newstr = "";
text = text.replaceAll("⬆️", CHAR.UP);
text = text.replaceAll("⬅️", CHAR.LEFT);
text = text.replaceAll("⬇️", CHAR.DOWN);
text = text.replaceAll("➡️", CHAR.RIGHT);
for (const char of text) {
if (char in font.chars) {
newstr += char;
}
}
return newstr;
}
const state = {
doubleClickTimer: 0,
history: [] as Array<{code: string, anchor: number, focus: number}>,
historyDebounce: 0,
historyIndex: 0,
undo() {
console.log('undoing');
if (this.historyIndex === this.history.length && this.historyDebounce > 0) {
this.snapshot();
}
console.log('historyIndex', this.historyIndex);
if (this.historyIndex > 0) {
this.historyIndex -= 1;
const snap = this.history[this.historyIndex];
console.log('historyIndex', this.historyIndex);
this.code = snap.code;
this.setSelection(snap.anchor, snap.focus);
}
},
redo() {
console.log('redoing');
if (this.historyIndex < this.history.length-1) {
this.historyIndex += 1;
const snap = this.history[this.historyIndex];
this.code = snap.code;
this.setSelection(snap.anchor, snap.focus);
}
},
snapshot() {
const snap = {
code: this.code,
anchor: this.anchor,
focus: this.focus,
};
this.history.push(snap);
console.log('took snapshot', this.historyIndex, snap);
},
startSnapping() {
console.log('start snapping', this.historyIndex);
if (this.historyDebounce <= 0) {
this.historyIndex += 1;
}
if (this.history.length > this.historyIndex) {
this.history.length = this.historyIndex;
}
this.historyDebounce = historyDebounceFrames;
},
wordMode: false,
scrollX: 0,
scrollY: 0,
anchor: 0,
focus: 0,
get focusX() {return indexToGrid(this.code, this.focus).x;},
get focusY() {return indexToGrid(this.code, this.focus).y;},
get anchorX() {return indexToGrid(this.code, this.anchor).x;},
get anchorY() {return indexToGrid(this.code, this.anchor).y;},
get focusPixelX() {return indexToRect(this.code, this.focus).x;},
get focusPixelY() {return indexToRect(this.code, this.focus).y;},
get anchorPixelX() {return indexToRect(this.code, this.anchor).x;},
get anchorPixelY() {return indexToRect(this.code, this.anchor).y;},
isCollapsed() {
return this.anchor === this.focus;
},
clampInRange(n: number) {
return Math.max(0, Math.min(n, this.code.length))
},
findNearestWordBoundaryLeft(index: number) {
if (index === this.code.length-1) {
return index;
}
const words1 = this.code.slice(0, index+1).split(/\b/g);
if (words1[words1.length-1].length === 1) {
return index;
}
const words = this.code.slice(0, index).split(/\b/g);
if (!words.length) {
return 0;
}
return index-words[words.length-1].length;
},
findNearestWordBoundaryRight(index: number) {
if (index === 0) {
return index;
}
const words1 = this.code.slice(index-1).split(/\b/g);
if (words1[0].length === 1) {
return index;
}
const words = this.code.slice(index).split(/\b/g);
if (!words.length) {
return this.code.length;
}
return index+words[0].length;
},
setSelection(anchor: number | {x: number, y: number}, focus?: number | {x: number, y: number}) {
if (typeof anchor !== "number") {
anchor = gridToIndex(this.code, anchor.x, anchor.y);
}
focus = focus ?? anchor;
if (typeof focus !== "number") {
focus = gridToIndex(this.code, focus.x, focus.y);
}
this.anchor = this.clampInRange(anchor);
this.focus = this.clampInRange(focus);
if (this.wordMode) {
console.log('word mode', this.anchor, this.focus, this.findNearestWordBoundaryLeft(this.anchor), this.findNearestWordBoundaryRight(this.focus));
if (this.anchor <= this.focus) {
this.anchor = this.findNearestWordBoundaryLeft(this.anchor);
this.focus = this.findNearestWordBoundaryRight(this.focus);
} else {
this.anchor = this.findNearestWordBoundaryRight(this.anchor);
this.focus = this.findNearestWordBoundaryLeft(this.focus);
}
}
this.anchor = this.clampInRange(this.anchor);
this.focus = this.clampInRange(this.focus);
},
setFocus(focus: number | {x: number, y: number}) {
if (typeof focus !== "number") {
focus = gridToIndex(this.code, focus.x, focus.y);
}
this.focus = this.clampInRange(focus);
if (this.wordMode) {
if (this.anchor <= this.focus) {
this.anchor = this.findNearestWordBoundaryLeft(this.anchor);
this.focus = this.findNearestWordBoundaryRight(this.focus);
} else {
this.anchor = this.findNearestWordBoundaryRight(this.anchor);
this.focus = this.findNearestWordBoundaryLeft(this.focus);
}
}
this.focus = this.clampInRange(this.focus);
},
insertText(text: string) {
const {code, anchor, focus} = this;
this.code = code.slice(0, Math.min(anchor, focus)) + text + code.slice(Math.max(anchor, focus));
this.setSelection(Math.min(anchor, focus) + text.length);
this.startSnapping();
},
toggleComment() {
const lines = this.code.split("\n");
const {focusX, focusY, anchorX, anchorY} = this;
const lineInSelection = (i: number) => i >= Math.min(focusY, anchorY) && i <= Math.max(focusY, anchorY);
const allLinesAreCommented = lines.every((line, i) => {
if (lineInSelection(i) && !line.trim().startsWith("// ")) {
return false;
} else {
return true;
}
});
const newLines = lines.map((line, i) => {
if (lineInSelection(i)) {
if (allLinesAreCommented) {
return line.slice(3);
} else {
return "// "+line;
}
} else {
return line;
}
});
this.code = newLines.join("\n");
const shiftBy = allLinesAreCommented ? -3 : 3;
this.setSelection({x: anchorX+shiftBy, y: anchorY}, {x: focusX+shiftBy, y: focusY});
this.startSnapping();
},
indent(indentString: string) {
const lines = this.code.split("\n");
const {focusX, focusY, anchorX, anchorY} = this;
const newLines = lines.map((line, i) => {
if (i >= Math.min(focusY, anchorY) && i <= Math.max(focusY, anchorY)) {
return indentString+line;
} else {
return line;
}
});
this.code = newLines.join("\n");
this.setSelection({x: anchorX+1, y: anchorY}, {x: focusX+1, y: focusY});
this.startSnapping();
},
outdent(outdentRegex: RegExp) {
const lines = this.code.split("\n");
const {focusX, focusY, anchorX, anchorY} = this;
const newLines = lines.map((line, i) => {
const match = line.match(outdentRegex);
if (i >= Math.min(focusY, anchorY) && i <= Math.max(focusY, anchorY) && match) {
return line.slice(match[0].length);
} else {
return line;
}
});
this.code = newLines.join("\n");
this.setSelection({x: Math.max(0,anchorX-1), y: anchorY}, {x: Math.max(0,focusX-1), y: focusY});
this.startSnapping();
},
backspace() {
const {code, focus} = this;
if (this.isCollapsed()) {
if (focus > 0) {
this.code = code.slice(0, focus-1) + code.slice(focus);
this.setSelection(focus-1);
this.startSnapping();
}
} else {
this.insertText("");
}
},
delete() {
const {code, focus} = this;
if (this.isCollapsed()) {
if (focus < code.length) {
this.code = code.slice(0, focus) + code.slice(1+focus);
this.startSnapping();
}
} else {
this.insertText("");
}
},
async copy() {
const {code, anchor, focus} = this;
const selected = code.slice(Math.min(anchor,focus), Math.max(anchor,focus));
await clipboard.writeText(transformForCopy(selected));
},
async cut() {
await this.copy();
this.insertText("");
},
async paste() {
this.insertText(transformForPaste(await clipboard.readText()));
},
scrollToCursor() {
const {focusY, scrollY, scrollX, focus} = this;
const fh = fontHeight + 1;
const rect = indexToRect(this.code, focus);
if (focusY*fh < scrollY) {
this.scrollY = focusY*fh;
}
if (focusY*fh > scrollY+112-fh) {
this.scrollY = focusY*fh-112+fh;
}
if (rect.x < scrollX) {
this.scrollX = rect.x;
}
if (rect.x+rect.w > scrollX+128) {
this.scrollX = rect.x-128+rect.w+1;
}
},
currentIndentation() {
const lines = this.code.slice(0, this.focus).split("\n");
const line = lines[lines.length-1];
const match = line.match(/^\s*/);
if (!match) {
return "";
}
return match[0];
},
get code() {
return getCodeSheet(page.activeSheet);
},
set code(val) {
setSheet(page.activeSheet, "code", val);
}
}
const indexToGrid = (str: string, index: number) => {
const linesUpTo = str.slice(0,index).split("\n");
return {
x: linesUpTo[linesUpTo.length-1].length,
y: linesUpTo.length - 1,
}
}
const gridToIndex = (str: string, x: number, y: number) => {
const lines = str.split("\n");
if (y < 0) {
return 0;
}
if (y >= lines.length) {
return str.length;
}
return lines.slice(0, y).join("\n").length+Math.min(x, lines[y].length)+(y === 0 ? 0 : 1);
}
const indexToRect = (str: string, index: number) => {
const linesUpTo = str.slice(0,index).split("\n");
let extra = 0;
if (linesUpTo[linesUpTo.length-1].length > 0) {
extra = 1;
}
return {
x: measureText(linesUpTo[linesUpTo.length-1]) + extra,
y: (fontHeight + 1)*(linesUpTo.length - 1),
w: measureText(str[index] ?? "\n"),
h: fontHeight+1,
}
}
const pixelToIndex = (str: string, x: number, y: number) => {
const lines = str.split("\n");
if (y < 0) {
return 0;
}
if (y >= (fontHeight+1)*lines.length) {
return str.length;
}
const yy = Math.floor(y/(fontHeight+1));
const prefix = lines.slice(0, yy).join("\n").length+(yy === 0 ? 0 : 1);
const line = lines[yy];
let j = 0;
while (measureText(line.slice(0, j)) < x && j < line.length) {
j+=1;
}
if (measureText(line) < x) {
j+=1;
}
return prefix + Math.max(0, j-1);
}
const update = async () => {
const { focus } = state;
if (state.history.length === 0) {
state.snapshot();
}
if (state.historyDebounce > 0) {
state.historyDebounce -= 1;
if (state.historyDebounce <= 0) {
state.snapshot();
}
}
if (state.doubleClickTimer > 0) {
state.doubleClickTimer -= 1;
}
if (mouseDown() && !shiftKeyDown()) {
if (state.doubleClickTimer > 0) {
state.wordMode = true;
} else {
state.doubleClickTimer = 10;
}
const {x, y} = mousePos();
state.setSelection(pixelToIndex(state.code, x+state.scrollX, y+state.scrollY-8));
state.scrollToCursor();
} else if (mouseHeld()) {
const {x, y} = mousePos();
state.setFocus(pixelToIndex(state.code, x+state.scrollX, y+state.scrollY-8));
state.scrollToCursor();
} else {
state.wordMode = false;
}
const keyboardString = getKeyboardString();
if (keyboardString) {
state.insertText(keyboardString);
state.scrollToCursor();
}
if (keyPressed(K.ENTER)) {
state.insertText("\n"+state.currentIndentation());
state.scrollToCursor();
}
if (keyPressed(K.TAB)) {
if (!shiftKeyDown()) {
if (state.isCollapsed()) {
state.insertText("\t");
} else {
state.indent("\t");
}
} else {
state.outdent(/^(\t| )/);
}
state.scrollToCursor();
}
if (keyPressed(K.BACKSPACE)) {
state.backspace();
state.scrollToCursor();
}
if (keyPressed(K.DELETE)) {
state.delete();
state.scrollToCursor();
}
if (keyPressed(K.ARROW_RIGHT)) {
if (shiftKeyDown()) {
state.setFocus(focus+1);
} else {
state.setSelection(focus+1);
}
state.scrollToCursor();
}
if (keyPressed(K.ARROW_LEFT)) {
if (shiftKeyDown()) {
state.setFocus(focus-1);
} else {
state.setSelection(focus-1);
}
state.scrollToCursor();
}
if (keyPressed(K.ARROW_DOWN)) {
const rect = indexToRect(state.code, focus);
const newIndex = pixelToIndex(state.code, rect.x, rect.y+rect.h+1+1);
if (shiftKeyDown()) {
state.setFocus(newIndex);
} else {
state.setSelection(newIndex);
}
state.scrollToCursor();
}
if (keyPressed(K.ARROW_UP)) {
const rect = indexToRect(state.code, focus);
const newIndex = pixelToIndex(state.code, rect.x, rect.y-1-1);
if (shiftKeyDown()) {
state.setFocus(newIndex);
} else {
state.setSelection(newIndex);
}
state.scrollToCursor();
}
if (keyPressed("C") && ctrlKeyDown()) {
await state.copy();
state.scrollToCursor();
}
if (keyPressed("X") && ctrlKeyDown()) {
await state.cut();
state.scrollToCursor();
}
if (keyPressed("V") && ctrlKeyDown()) {
await state.paste();
state.scrollToCursor();
}
if (keyPressed("Z") && ctrlKeyDown()) {
if (shiftKeyDown()) {
state.redo();
} else {
state.undo();
}
}
if (keyPressed("Y") && ctrlKeyDown()) {
state.redo();
}
if (keyPressed("/") && ctrlKeyDown()) {
state.toggleComment();
}
}
const draw = () => {
clearScreen();
const {
scrollX,
scrollY,
anchor,
focus,
code,
} = state;
const x = 0;
const y = 8;
const w = 128;
const h = 112;
fillRect(x, y, w, h, backgroundColor);
if (anchor !== focus) {
for (let i = Math.min(anchor, focus); i < Math.max(anchor, focus); i++) {
const sel = indexToRect(code, i);
fillRect(x+sel.x-scrollX, y+sel.y-scrollY, sel.w+2, sel.h, selectionColor);
}
}
const rect = indexToRect(code, focus);
fillRect(x+rect.x-scrollX, y+rect.y-scrollY, 1, rect.h, caretColor);
const builtins = Object.keys(getBuiltins());
const tokens = [...tokenize(code)];
let cx = 0;
let cy = 0;
tokens.forEach((token) => {
if (token.type === "LineTerminatorSequence") {
cx=0;
cy+=fontHeight+1;
return;
}
const lines = token.value.split("\n");
lines.forEach((line, i) => {
let color = tokenColors[token.type];
if (builtins.includes(token.value)) {
color = builtinColor;
}
if (keywords.includes(token.value)) {
color = keywordColor;
}
if (values.includes(token.value)) {
color = valueColor;
}
if (operator.includes(token.value)) {
color = operatorColor;
}
if (punctuation.includes(token.value)) {
color = punctuationColor;
}
drawText(1+x+cx-scrollX, 1+y+cy-scrollY, line, color);
if (i === lines.length-1) {
cx += measureText(line)+1;
} else {
cx=0;
cy+=fontHeight+1;
}
});
})
}
export const codetab = {
update,
draw,
}

119
colors.ts Normal file
View File

@ -0,0 +1,119 @@
const hue_gray = 230;
const hue_brown = 30;
const hue_orange = 37;
const hue_purple = 280;
const hue_pink = 310;
const hue_red = 340;
const hue_red_shade = 335;
const hue_yellow = 57;
const hue_green = 130;
const hue_green_shade = 145;
const hue_sky = 203;
const hue_blue = 224;
const hue_blue_shade = 235;
const hue_blue_shade_2 = 240;
const saturation_max = 90;
const saturation_normal = 60;
const saturation_low = 40;
const saturation_min = 13;
const lightness_max = 95;
const lightness_light = 67;
const lightness_mediumlight = 62;
const lightness_medium = 50;
const lightness_mediumdark = 40;
const lightness_dark = 30;
const lightness_almostverydark = 25;
const lightness_verydark = 20;
const lightness_min = 5;
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
*/
function hsl(h: number, s: number, l: number): [number, number, number] {
h = h / 360;
s = s/100;
l = l/100;
let r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
const hue2rgb = (p: number, q: number, t: number) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [ r , g , b ];
}
const colors = {
TRANSPARENT: [0, 0, 0],
BLACK2: [0, 0, 0],
BLACK3: [0, 0, 0],
BLACK4: [0, 0, 0],
WHITE: hsl(hue_gray, saturation_min, lightness_max),
LIGHTGRAY: hsl(hue_gray, saturation_min, lightness_light),
DARKGRAY: hsl(hue_gray, saturation_min, lightness_dark),
BLACK: hsl(hue_gray, saturation_min, lightness_min),
ORANGE: hsl(hue_orange, saturation_normal, lightness_medium),
TAN: hsl(hue_brown, saturation_low, lightness_light),
BROWN: hsl(hue_brown, saturation_low, lightness_mediumdark),
DARKBROWN: hsl(hue_brown, saturation_low, lightness_almostverydark),
PURPLE: hsl(hue_purple, saturation_normal, lightness_medium),
PINK: hsl(hue_pink, saturation_normal, lightness_light),
RED: hsl(hue_red, saturation_normal, lightness_medium),
DARKRED: hsl(hue_red_shade, saturation_normal, lightness_dark),
YELLOW: hsl(hue_yellow, saturation_max, lightness_mediumlight),
GREEN: hsl(hue_green, saturation_normal, lightness_medium),
DARKGREEN: hsl(hue_green_shade, saturation_normal, lightness_dark),
DARKERGREEN: hsl(hue_green_shade, saturation_normal, lightness_verydark),
CYAN: hsl(hue_sky, saturation_max, lightness_light),
BLUE: hsl(hue_blue, saturation_normal, lightness_medium),
DARKBLUE: hsl(hue_blue_shade, saturation_normal, lightness_mediumdark),
DARKERBLUE: hsl(hue_blue_shade_2, saturation_normal, lightness_dark),
} as const;
// const colors = {
// TRANSPARENT: [0, 0, 0],
// BLACK: [0, 0, 0],
// WHITE: [1, 1, 1],
// RED: [1, 0, 0],
// YELLOW: [1, 1, 0],
// GREEN: [0, 1, 0],
// BLUE: [0.1, 0.5, 1],
// DARKBLUE: [0.05, 0.1, 0.3],
// BROWN: [0.6, 0.5, 0.4],
// GRAY: [0.5, 0.5, 0.5],
// PURPLE: [0.7, 0.1, 0.85],
// ORANGE: [0.95, 0.75, 0.25],
// CYAN: [0, 0.9, 0.9],
// LIGHTGRAY: [0.75, 0.75, 0.75],
// REDDISH: [216/255, 59/255, 113/255],
// DARKGREEN: [0, 0.6, 0.2],
// } as const;
export const palette: Array<[number, number, number, number]> = Object.values(colors).map(val => [...val, 1]);
export const COLOR = Object.fromEntries(Object.keys(colors).map((name, i) => [name, Number(i)])) as {[key in keyof typeof colors]: number};

11
deno.json Normal file
View File

@ -0,0 +1,11 @@
{
"fmt": {
"useTabs": true
},
"tasks": {
"run": "deno run -A --unstable index.ts",
"build": "deno compile --output build/faux -A --unstable index.ts",
"build_linux": "deno compile --output build/faux_linux --target x86_64-unknown-linux-gnu -A --unstable index.ts",
"build_windows": "deno compile --output build/faux_windows --target x86_64-pc-windows-msvc -A --unstable index.ts"
}
}

73
deno.lock generated Normal file
View File

@ -0,0 +1,73 @@
{
"version": "2",
"remote": {
"https://deno.land/std@0.186.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
"https://deno.land/std@0.186.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
"https://deno.land/std@0.186.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
"https://deno.land/std@0.186.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
"https://deno.land/std@0.186.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0",
"https://deno.land/std@0.186.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
"https://deno.land/std@0.186.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
"https://deno.land/std@0.186.0/path/mod.ts": "ee161baec5ded6510ee1d1fb6a75a0f5e4b41f3f3301c92c716ecbdf7dae910d",
"https://deno.land/std@0.186.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d",
"https://deno.land/std@0.186.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
"https://deno.land/std@0.186.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba",
"https://deno.land/std@0.97.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58",
"https://deno.land/std@0.97.0/_util/os.ts": "e282950a0eaa96760c0cf11e7463e66babd15ec9157d4c9ed49cc0925686f6a7",
"https://deno.land/std@0.97.0/encoding/base64.ts": "eecae390f1f1d1cae6f6c6d732ede5276bf4b9cd29b1d281678c054dc5cc009e",
"https://deno.land/std@0.97.0/encoding/hex.ts": "f952e0727bddb3b2fd2e6889d104eacbd62e92091f540ebd6459317a61932d9b",
"https://deno.land/std@0.97.0/fs/_util.ts": "f2ce811350236ea8c28450ed822a5f42a0892316515b1cd61321dec13569c56b",
"https://deno.land/std@0.97.0/fs/ensure_dir.ts": "b7c103dc41a3d1dbbb522bf183c519c37065fdc234831a4a0f7d671b1ed5fea7",
"https://deno.land/std@0.97.0/fs/exists.ts": "b0d2e31654819cc2a8d37df45d6b14686c0cc1d802e9ff09e902a63e98b85a00",
"https://deno.land/std@0.97.0/hash/_wasm/hash.ts": "cb6ad1ab429f8ac9d6eae48f3286e08236d662e1a2e5cfd681ba1c0f17375895",
"https://deno.land/std@0.97.0/hash/_wasm/wasm.js": "94b1b997ae6fb4e6d2156bcea8f79cfcd1e512a91252b08800a92071e5e84e1a",
"https://deno.land/std@0.97.0/hash/hasher.ts": "57a9ec05dd48a9eceed319ac53463d9873490feea3832d58679df6eec51c176b",
"https://deno.land/std@0.97.0/hash/mod.ts": "5d032bd34186cda2f8d17fc122d621430953a6030d4b3f11172004715e3e2441",
"https://deno.land/std@0.97.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853",
"https://deno.land/std@0.97.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4",
"https://deno.land/std@0.97.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b",
"https://deno.land/std@0.97.0/path/common.ts": "eaf03d08b569e8a87e674e4e265e099f237472b6fd135b3cbeae5827035ea14a",
"https://deno.land/std@0.97.0/path/glob.ts": "314ad9ff263b895795208cdd4d5e35a44618ca3c6dd155e226fb15d065008652",
"https://deno.land/std@0.97.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12",
"https://deno.land/std@0.97.0/path/posix.ts": "f56c3c99feb47f30a40ce9d252ef6f00296fa7c0fcb6dd81211bdb3b8b99ca3b",
"https://deno.land/std@0.97.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c",
"https://deno.land/std@0.97.0/path/win32.ts": "77f7b3604e0de40f3a7c698e8a79e7f601dc187035a1c21cb1e596666ce112f8",
"https://deno.land/x/cache@0.2.13/deps.ts": "6f14e76a1a09f329e3f3830c6e72bd10b53a89a75769d5ea886e5d8603e503e6",
"https://deno.land/x/cache@0.2.13/directories.ts": "ef48531cab3f827252e248596d15cede0de179a2fb15392ae24cf8034519994f",
"https://deno.land/x/dwm@0.3.3/mod.ts": "8a8ca602442d250eaa7cef67c14245a41aa7dc8de2fa337008e433a2ed1fe2f1",
"https://deno.land/x/dwm@0.3.3/src/core/common.ts": "dd4cd26f45fca187c5a6192bfb7b3b4a01a23c66dddf64e19e013de7748f988d",
"https://deno.land/x/dwm@0.3.3/src/core/event.ts": "715cce309021e9dcd45b7e3f41bddea961d4b157dcf6f29a21e910825aae90a8",
"https://deno.land/x/dwm@0.3.3/src/core/mod.ts": "4c54848365ea17b67e65d4d58c7c94378c9bbc6ea2a59d7d11f1d701e6a5483a",
"https://deno.land/x/dwm@0.3.3/src/core/monitor.ts": "b291ebad386095285fb7c430990be0a9f1f5f81fbcb338058ce72a538a75d667",
"https://deno.land/x/dwm@0.3.3/src/core/platform.ts": "6cab5f575198848673204673fe82d242c9c87549c16cbed9a64c838eb99dd2d4",
"https://deno.land/x/dwm@0.3.3/src/core/window.ts": "1ccd738d6e4e28836327a76480c3c47855f617fa7ed7552a035f2d7bf0fd6c6d",
"https://deno.land/x/dwm@0.3.3/src/platform/glfw/constants.ts": "832295fa6a4bd66aea638bd09ad2cc48b3756daff814c7c0ae2a544f228be9e5",
"https://deno.land/x/dwm@0.3.3/src/platform/glfw/ffi.ts": "0c6a521a8374aa8c912417edfa5c263423286363f4a7c827d4d26ead8b954ee6",
"https://deno.land/x/dwm@0.3.3/src/platform/glfw/monitor.ts": "05506c5d21c527a6ea456d24e00f88a01a9292a68f45a845499137ecb6fb80c7",
"https://deno.land/x/dwm@0.3.3/src/platform/glfw/platform.ts": "cca2684151f34be392e74a9828f89f8c3ed46db25e59aec1f638b4ccf82535e9",
"https://deno.land/x/dwm@0.3.3/src/platform/glfw/scancode_win.json": "711ee525f88fe92129acd7d66fd6a5665b68ce6a60f590ae24de67e1ee916b8f",
"https://deno.land/x/dwm@0.3.3/src/platform/glfw/window.ts": "a6dd426a95cd93ba4905da151a0d06e9376becca273ee8bc8d1c9a7bd16e0d81",
"https://deno.land/x/dwm@0.3.3/src/platform/mod.ts": "20572d937c62ec543e0ca87cf8ed32cd15505d254f97a6fae98b936e480f2157",
"https://deno.land/x/gluten@0.1.6/api/gles23.2.ts": "99cad13b74938ff987a1a707f73d72a82ddf66935e45b59d1980928cd3af3495",
"https://esm.sh/js-tokens@8.0.1": "7b08a51f91034b720edcde4f0b4314abc39ee14da57861e3e676f1ed1f4fb4ab",
"https://esm.sh/v119/js-tokens@8.0.1/deno/js-tokens.mjs": "0fa55e76fa97785f8f938db8ea55e0db57825ea81cdcad20bd925f9902f678c1",
"https://esm.sh/v119/js-tokens@8.0.1/index.d.ts": "9b178631a934bd5e4832b478d4f74083d4dc357615a0d1a632357dfafe898cdb",
"https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_darwin.js": "48911f26fff723a9c5f2f38e39be42fc65ed8dea6f2ba1f1acb464d3f0aa435b",
"https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_darwin_aarch64.js": "ae4d795d93830b8a27714ab6c20b69b67f3d4ad3544c50e344558756cf2e92f3",
"https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_linux.js": "b064aedb175fee1a977937f07584238f313a1958f9869273e7e672c42f09932d",
"https://glfw-binaries.deno.dev/3.4.0-patch2/glfw3_windows.js": "6ac603e03520c8c333e1475cb00f982adb1f8a99de7f4bb0b8953da66f210159",
"https://raw.githubusercontent.com/Nisgrak/deno-clipboard/fix-deno-1.0.0/mod.ts": "85282325a499c75c6f9ed3603fc5f8baf4bf661a616add43b4e6f033def52680",
"https://unpkg.com/js-tokens@8.0.1/index.js": "322c95254ceef0ac195f4e71adac2a90adf379a2d67fbb948402295a780fdbc0"
},
"npm": {
"specifiers": {
"js-tokens": "js-tokens@8.0.1"
},
"packages": {
"js-tokens@8.0.1": {
"integrity": "sha512-3AGrZT6tuMm1ZWWn9mLXh7XMfi2YtiLNPALCVxBCiUVq0LD1OQMxV/AdS/s7rLJU5o9i/jBZw/N4vXXL5dm29A==",
"dependencies": {}
}
}
}
}

43
deps.ts Normal file
View File

@ -0,0 +1,43 @@
// dwm
export {
createWindow,
getProcAddress,
mainloop,
} from "https://deno.land/x/dwm@0.3.3/mod.ts";
export * as gl from "https://deno.land/x/gluten@0.1.6/api/gles23.2.ts";
// jsTokens
import jsTokens from "https://esm.sh/js-tokens@8.0.1";
export function tokenize(input: string): Iterable<Token> {
// deno-lint-ignore no-explicit-any
return (jsTokens as any)(input);
}
type Token =
| { type: "StringLiteral"; value: string; closed: boolean }
| { type: "NoSubstitutionTemplate"; value: string; closed: boolean }
| { type: "TemplateHead"; value: string }
| { type: "TemplateMiddle"; value: string }
| { type: "TemplateTail"; value: string; closed: boolean }
| { type: "RegularExpressionLiteral"; value: string; closed: boolean }
| { type: "MultiLineComment"; value: string; closed: boolean }
| { type: "SingleLineComment"; value: string }
| { type: "IdentifierName"; value: string }
| { type: "PrivateIdentifier"; value: string }
| { type: "NumericLiteral"; value: string }
| { type: "Punctuator"; value: string }
| { type: "WhiteSpace"; value: string }
| { type: "LineTerminatorSequence"; value: string }
| { type: "Invalid"; value: string };
// clipboard
import { clipboard } from "https://raw.githubusercontent.com/Nisgrak/deno-clipboard/fix-deno-1.0.0/mod.ts";
export { clipboard } from "https://raw.githubusercontent.com/Nisgrak/deno-clipboard/fix-deno-1.0.0/mod.ts";
try {
await clipboard.readText();
} catch (err) {
console.log("If you are running this on linux, please make sure you have 'xsel' installed.");
throw err;
}
// path
export * as path from "https://deno.land/std@0.186.0/path/mod.ts";

95
editmode.ts Normal file
View File

@ -0,0 +1,95 @@
import { clearScreen, fillRect } from "./window.ts";
import { codetab } from "./codetab.ts";
import { spritetab } from "./spritetab.ts";
import { viewsheets, page } from "./viewsheets.ts";
import { COLOR } from "./colors.ts";
import { mouseClick, mousePos } from "./mouse.ts";
import { drawIcon } from "./builtins.ts";
import { inRect } from "./util.ts";
import { sheetsIcon, trashIcon } from "./icons.ts";
import { SheetType, setSheet } from "./sheet.ts";
import { nonetab } from "./nonetab.ts";
import { maptab } from "./maptab.ts";
type TabName = SheetType; // "code" | "sprite" | "map" | "sfx" | "music" | "sheet";
const buttons: Array<{update: () => void, draw: () => void}> = [];
const makeTabButton = (tabname: TabName | "sheet", x: number, y: number, icon: Array<number>) => {
buttons.push({
update() {
if (mouseClick()) {
const {x: mouseX, y: mouseY} = mousePos();
if (inRect(mouseX, mouseY, x, y, 8, 8)) {
page.tab = tabname;
}
}
},
draw() {
drawIcon(x, y, icon, page.tab === tabname ? COLOR.YELLOW : COLOR.WHITE);
}
})
}
const makeTrashButton = (x: number, y: number, icon: Array<number>) => {
buttons.push({
update() {
if (page.tab !== "sheet") {
return
}
if (mouseClick()) {
const {x: mouseX, y: mouseY} = mousePos();
if (inRect(mouseX, mouseY, x, y, 8, 8)) {
setSheet(page.activeSheet, "none", null);
page.tab = "sheet";
}
}
},
draw() {
if (page.tab !== "sheet") {
return
}
drawIcon(x, y, icon, COLOR.BLACK);
}
})
}
makeTabButton("sheet", 120, 0, sheetsIcon);
makeTrashButton(0, 0, trashIcon);
const update = () => {
buttons.forEach(button => button.update());
if (page.tab === "code") {
codetab.update();
} else if (page.tab === "spritesheet") {
spritetab.update();
} else if (page.tab === "map") {
maptab.update();
} else if (page.tab === "sheet") {
viewsheets.update();
} else if (page.tab === "none") {
nonetab.update();
}
}
const draw = () => {
clearScreen();
if (page.tab === "code") {
codetab.draw();
} else if (page.tab === "spritesheet") {
spritetab.draw();
} else if (page.tab === "map") {
maptab.draw();
} else if (page.tab === "sheet") {
viewsheets.draw();
} else if (page.tab === "none") {
nonetab.draw();
}
fillRect(0, 0, 128, 8, COLOR.RED);
fillRect(0, 120, 128, 8, COLOR.RED);
buttons.forEach(button => button.draw());
}
export const editmode = {
update,
draw,
}

854
font.ts Normal file
View File

@ -0,0 +1,854 @@
/**
* Perhaps fonts can be their own type of sheet. By the calculation below, we can fit ~4 fonts per fontsheet
*
* 3 bits for height
* 5 more metadata bits
* = 1 byte
*
* Per character:
* - 3 bits for width
* - 5 bits for metadata
* - 64 bits for pixels
* = 9 bytes per character
*
* 96 chars * 9 bytes =
*/
// export const fontWidth = 4;
// export const fontHeight = 6;
export const CHAR = {
UP: "À",
LEFT: "Á",
DOWN: "Â",
RIGHT: "Ã",
PI: "π"
}
export type Font = {
height: 6,
chars: {[key: string]: Array<number>},
};
// deno-fmt-ignore
export const font: Font = {
height: 6,
chars: {
"A": [
1, 1, 1,
1, 0, 1,
1, 1, 1,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"B": [
1, 1, 1,
1, 0, 1,
1, 1, 0,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"C": [
0, 1, 1,
1, 0, 0,
1, 0, 0,
1, 0, 0,
0, 1, 1,
0, 0, 0,
],
"D": [
1, 1, 0,
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 1, 0,
0, 0, 0,
],
"E": [
1, 1, 1,
1, 0, 0,
1, 1, 0,
1, 0, 0,
1, 1, 1,
0, 0, 0,
],
"F": [
1, 1, 1,
1, 0, 0,
1, 1, 0,
1, 0, 0,
1, 0, 0,
0, 0, 0,
],
"G": [
0, 1, 1,
1, 0, 0,
1, 0, 1,
1, 0, 1,
0, 1, 1,
0, 0, 0,
],
"H": [
1, 0, 1,
1, 0, 1,
1, 1, 1,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"I": [
1, 1, 1,
0, 1, 0,
0, 1, 0,
0, 1, 0,
1, 1, 1,
0, 0, 0,
],
"J": [
1, 1, 1,
0, 1, 0,
0, 1, 0,
0, 1, 0,
1, 1, 0,
0, 0, 0,
],
"K": [
1, 0, 1,
1, 0, 1,
1, 1, 0,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"L": [
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 1, 1,
0, 0, 0,
],
"M": [
1, 1, 1,
1, 1, 1,
1, 0, 1,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"N": [
1, 1, 0,
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"O": [
0, 1, 1,
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 1, 0,
0, 0, 0,
],
"P": [
1, 1, 1,
1, 0, 1,
1, 1, 1,
1, 0, 0,
1, 0, 0,
0, 0, 0,
],
"Q": [
0, 1, 1,
1, 0, 1,
1, 0, 1,
1, 1, 0,
0, 1, 1,
0, 0, 0,
],
"R": [
1, 1, 1,
1, 0, 1,
1, 1, 0,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"S": [
0, 1, 1,
1, 0, 0,
1, 1, 1,
0, 0, 1,
1, 1, 0,
0, 0, 0,
],
"T": [
1, 1, 1,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 0,
],
"U": [
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 0, 1,
0, 1, 1,
0, 0, 0,
],
"V": [
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 1, 1,
0, 1, 0,
0, 0, 0,
],
"W": [
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 1, 1,
1, 1, 1,
0, 0, 0,
],
"X": [
1, 0, 1,
1, 0, 1,
0, 1, 0,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"Y": [
1, 0, 1,
1, 0, 1,
1, 1, 1,
0, 1, 0,
1, 0, 0,
0, 0, 0,
],
"Z": [
1, 1, 1,
0, 0, 1,
0, 1, 0,
1, 0, 0,
1, 1, 1,
0, 0, 0,
],
"a": [
0, 0, 0,
1, 1, 1,
0, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"b": [
1, 0, 0,
1, 0, 0,
1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"c": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
1, 0, 0,
1, 1, 1,
0, 0, 0,
],
"d": [
0, 0, 1,
0, 0, 1,
1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"e": [
0, 0, 0,
0, 1, 1,
1, 0, 1,
1, 1, 0,
0, 1, 1,
0, 0, 0,
],
"f": [
0, 1, 1,
1, 0, 0,
1, 1, 0,
1, 0, 0,
1, 0, 0,
0, 0, 0,
],
"g": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
1, 0, 1,
0, 1, 1,
1, 1, 0,
],
"h": [
1, 0, 0,
1, 0, 0,
1, 1, 1,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"i": [
0, 1, 0,
0, 0, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 0,
],
"j": [
0, 1, 0,
0, 0, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
1, 0, 0,
],
"k": [
1, 0, 0,
1, 0, 0,
1, 0, 1,
1, 1, 0,
1, 0, 1,
0, 0, 0,
],
"l": [
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 1,
0, 0, 0,
],
"m": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
1, 1, 1,
1, 0, 1,
0, 0, 0,
],
"n": [
0, 0, 0,
0, 0, 0,
1, 1, 0,
1, 0, 1,
1, 0, 1,
0, 0, 0,
],
"o": [
0, 0, 0,
0, 0, 0,
0, 1, 1,
1, 0, 1,
1, 1, 0,
0, 0, 0,
],
"p": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
1, 0, 1,
1, 1, 1,
1, 0, 0,
],
"q": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 1,
],
"r": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
1, 0, 0,
1, 0, 0,
0, 0, 0,
],
"s": [
0, 0, 0,
0, 0, 0,
0, 1, 1,
0, 1, 0,
1, 1, 0,
0, 0, 0,
],
"t": [
0, 1, 0,
0, 1, 0,
1, 1, 1,
0, 1, 0,
0, 1, 0,
0, 0, 0,
],
"u": [
0, 0, 0,
0, 0, 0,
1, 0, 1,
1, 0, 1,
0, 1, 1,
0, 0, 0,
],
"v": [
0, 0, 0,
0, 0, 0,
1, 0, 1,
1, 0, 1,
0, 1, 0,
0, 0, 0,
],
"w": [
0, 0, 0,
0, 0, 0,
1, 0, 1,
1, 1, 1,
1, 1, 1,
0, 0, 0,
],
"x": [
0, 0, 0,
0, 0, 0,
1, 0, 1,
0, 1, 0,
1, 0, 1,
0, 0, 0,
],
"y": [
0, 0, 0,
0, 0, 0,
1, 0, 1,
1, 0, 1,
0, 1, 0,
1, 0, 0,
],
"z": [
0, 0, 0,
0, 0, 0,
1, 1, 0,
0, 1, 0,
0, 1, 1,
0, 0, 0,
],
",": [
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 1, 0,
1, 0, 0,
],
".": [
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 1, 0,
0, 0, 0,
],
" ": [
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
"\t": [
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,
],
"\n": [
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
"<": [
0, 0, 1,
0, 1, 0,
1, 0, 0,
0, 1, 0,
0, 0, 1,
0, 0, 0,
],
">": [
1, 0, 0,
0, 1, 0,
0, 0, 1,
0, 1, 0,
1, 0, 0,
0, 0, 0,
],
"=": [
0, 0, 0,
1, 1, 1,
0, 0, 0,
1, 1, 1,
0, 0, 0,
0, 0, 0,
],
"(": [
0, 0, 1,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 1,
0, 0, 0,
],
")": [
1, 0, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
1, 0, 0,
0, 0, 0,
],
"[": [
0, 1, 1,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 1,
0, 0, 0,
],
"]": [
1, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
1, 1, 0,
0, 0, 0,
],
"{": [
0, 1, 1,
0, 1, 0,
1, 1, 0,
0, 1, 0,
0, 1, 1,
0, 0, 0,
],
"}": [
1, 1, 0,
0, 1, 0,
0, 1, 1,
0, 1, 0,
1, 1, 0,
0, 0, 0,
],
":": [
0, 0, 0,
0, 1, 0,
0, 0, 0,
0, 0, 0,
0, 1, 0,
0, 0, 0,
],
";": [
0, 0, 0,
0, 1, 0,
0, 0, 0,
0, 0, 0,
0, 1, 0,
1, 0, 0,
],
"'": [
0, 1, 0,
0, 1, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
'"': [
1, 0, 1,
1, 0, 1,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
"1": [
0, 1, 0,
1, 1, 0,
0, 1, 0,
0, 1, 0,
1, 1, 1,
0, 0, 0,
],
"2": [
0, 1, 0,
1, 0, 1,
0, 0, 1,
0, 1, 0,
1, 1, 1,
0, 0, 0,
],
"3": [
1, 1, 1,
0, 0, 1,
0, 1, 0,
0, 0, 1,
1, 1, 0,
0, 0, 0,
],
"4": [
1, 0, 1,
1, 0, 1,
1, 1, 1,
0, 0, 1,
0, 0, 1,
0, 0, 0,
],
"5": [
1, 1, 1,
1, 0, 0,
1, 1, 0,
0, 0, 1,
1, 1, 0,
0, 0, 0,
],
"6": [
0, 0, 1,
0, 1, 0,
1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"7": [
1, 1, 1,
0, 0, 1,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 0,
],
"8": [
1, 1, 1,
1, 0, 1,
1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"9": [
1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 1, 0,
1, 0, 0,
0, 0, 0,
],
"0": [
1, 1, 1,
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"+": [
0, 0, 0,
0, 1, 0,
1, 1, 1,
0, 1, 0,
0, 0, 0,
0, 0, 0,
],
"-": [
0, 0, 0,
0, 0, 0,
1, 1, 1,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
"_": [
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
1, 1, 1,
0, 0, 0,
],
"`": [
1, 0, 0,
0, 1, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
"~": [
0, 0, 0,
0, 0, 1,
1, 1, 1,
1, 0, 0,
0, 0, 0,
0, 0, 0,
],
"/": [
0, 0, 1,
0, 0, 1,
0, 1, 0,
1, 0, 0,
1, 0, 0,
0, 0, 0,
],
"?": [
1, 1, 1,
0, 0, 1,
0, 1, 1,
0, 0, 0,
0, 1, 0,
0, 0, 0,
],
"\\": [
1, 0, 0,
1, 0, 0,
0, 1, 0,
0, 0, 1,
0, 0, 1,
0, 0, 0,
],
"|": [
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 0,
],
"!": [
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 0, 0,
0, 1, 0,
0, 0, 0,
],
"@": [
0, 1, 0,
1, 0, 1,
1, 0, 1,
1, 0, 0,
0, 1, 1,
0, 0, 0,
],
"#": [
1, 0, 1,
1, 1, 1,
1, 0, 1,
1, 1, 1,
1, 0, 1,
0, 0, 0,
],
"$": [
1, 1, 1,
1, 1, 0,
0, 1, 1,
1, 1, 1,
0, 1, 0,
0, 0, 0,
],
"%": [
1, 0, 1,
0, 0, 1,
0, 1, 0,
1, 0, 0,
1, 0, 1,
0, 0, 0,
],
"^": [
0, 1, 0,
1, 0, 1,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
"&": [
1, 1, 0,
1, 1, 0,
0, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 0,
],
"*": [
0, 1, 0,
1, 1, 1,
0, 1, 0,
1, 0, 1,
0, 0, 0,
0, 0, 0,
],
[CHAR.UP]: [
0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 0, 1, 1, 1,
1, 1, 0, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0,
],
[CHAR.LEFT]: [
0, 1, 1, 1, 1, 1, 0,
1, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 0, 0, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0,
],
[CHAR.DOWN]: [
0, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0,
],
[CHAR.RIGHT]: [
0, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 1, 1, 1,
1, 1, 0, 0, 0, 1, 1,
1, 1, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0,
],
[CHAR.PI]: [
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 1, 1,
0, 1, 0, 1, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
],
}
};

54
icons.ts Normal file
View File

@ -0,0 +1,54 @@
export const codeIcon = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 1, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0,
0, 1, 0, 0, 0, 0, 1, 0,
0, 1, 0, 0, 0, 0, 1, 0,
0, 1, 1, 0, 0, 1, 1, 0,
0, 0, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
];
export const spriteIcon = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0,
0, 1, 1, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
];
export const mapIcon = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 0, 1, 1, 1, 1, 0,
0, 1, 1, 1, 0, 0, 1, 0,
0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
];
export const sheetsIcon = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0,
0, 1, 1, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0,
0, 1, 1, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
];
export const trashIcon = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
];

64
index.ts Normal file
View File

@ -0,0 +1,64 @@
import {
mainloop,
frame,
clearScreen,
} from "./window.ts";
import { runCode } from "./runcode.ts";
import { getCodeSheet } from "./sheet.ts";
import { refreshKeyboard, keyPressed, K } from "./keyboard.ts";
import { repl, resetRepl } from "./repl.ts";
import { addToContext } from "./runcode.ts";
import { editmode } from "./editmode.ts";
import { refreshMouse } from "./mouse.ts";
import { camera } from "./builtins.ts";
// deno-lint-ignore no-explicit-any
let game: any = null;
let mode: "play" | "edit" | "repl" = "repl";
addToContext("play", () => {
mode = "play";
game = runCode(getCodeSheet(0));
game.init();
});
clearScreen();
await mainloop(async (_t) => {
// TODO: use t
if (keyPressed(K.ESCAPE)) {
const modeTo = ({
play: "repl",
edit: "repl",
repl: "edit",
} as const)[mode];
if (mode === "play") {
resetRepl();
}
if (mode === "edit") {
clearScreen();
}
mode = modeTo;
} else {
if (mode === "play") {
if (game) {
game.update();
game.draw();
}
frame();
} else if (mode === "repl") {
repl.update();
camera(0, 0);
repl.draw();
frame();
} else if (mode === "edit") {
await editmode.update();
camera(0, 0);
editmode.draw();
frame();
}
}
refreshKeyboard();
refreshMouse();
}, false);

1
initialCart.json Normal file
View File

@ -0,0 +1 @@
[{"sheet_type":"code","value":"// Sample\n\nreturn {\n\tinit() {},\n\tupdate() {},\n\tdraw() {\n\t\tcls();\n\t\ttxt(10,10,\"Hello, World!\");\n\t}\n}"},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null},{"sheet_type":"none","value":null}]

174
keyboard.ts Normal file
View File

@ -0,0 +1,174 @@
import { font, CHAR } from "./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;
}

215
maptab.ts Normal file
View File

@ -0,0 +1,215 @@
import { clearScreen, fillRect } from "./window.ts";
import { drawSprite, drawText } from "./builtins.ts";
import { COLOR } from "./colors.ts";
import { getMapSheet, getSheet, setSheet } from "./sheet.ts";
import { M, mouseClick, mouseDown, mouseHeld, mousePos } from "./mouse.ts";
import { drawTransparentRect, drawVoidRect, inRect, reGrid } from "./util.ts";
import { page } from "./viewsheets.ts";
import { useSpritesheet } from "./builtins.ts";
import { keyPressed } from "./keyboard.ts";
import { K } from "./keyboard.ts";
const state = {
selectedSpriteSheet: 0,
selectedSprite: 0,
viewX: 0,
viewY: 0,
dragging: false,
dragFromViewX: 0,
dragFromViewY: 0,
dragFromX: 0,
dragFromY: 0,
get spriteSheetPage() {
return Math.floor(this.selectedSprite/64);
},
set spriteSheetPage(val) {
this.selectedSprite = 64*val+this.spriteWithinPage;
},
get spriteWithinPage() {
return this.selectedSprite%64;
},
set spriteWithinPage(val) {
this.selectedSprite = 64*this.spriteSheetPage+val;
},
get map() {
return getMapSheet(page.activeSheet);
},
set map(val) {
setSheet(page.activeSheet, "map", val);
},
setInPatch(i: number, sprsheet: number, sprite: number) {
const cellVal = this.map.subgrid(this.viewX, this.viewY, patchW, patchH).values[i];
if (cellVal) {
cellVal[0] = sprsheet
cellVal[1] = sprite;
}
},
get patch() {
return this.map.subgrid(this.viewX, this.viewY, patchW, patchH);
}
}
const patchX = 0;
const patchY = 8;
const patchW = 16;
const patchH = 9;
const spriteW = 8;
const spriteH = 8;
const spriteSheetX = 0;
const spriteSheetY = 88;
const spriteSheetW = 16;
const spriteSheetH = 4;
const spriteSheetPickerX = 0;
const spriteSheetPickerY = 81;
const spriteSheetPickerW = 16;
const spriteSheetPickerH = 1;
const spriteSheetPickerTabW = 7;
const spriteSheetPickerTabH = 7;
const spriteSheetPageSwapX = 121;
const spriteSheetPageSwapY = 81;
const spriteSheetPageSwapW = 7;
const spriteSheetPageSwapH = 7;
const update = () => {
const {x: mouseX, y: mouseY} = mousePos();
const inPatch = inRect(mouseX, mouseY, patchX, patchY, patchW*spriteW, patchH*spriteH - 2);
const inSpriteSheetPicker = inRect(mouseX, mouseY, spriteSheetPickerX, spriteSheetPickerY, spriteSheetPickerW*spriteSheetPickerTabW, spriteSheetPickerH*spriteSheetPickerTabH);
const inSpriteSheet = inRect(mouseX, mouseY, spriteSheetX, spriteSheetY, spriteSheetW*spriteW, spriteSheetH*spriteH);
const inSpriteSheetPageSwap = inRect(mouseX, mouseY, spriteSheetPageSwapX, spriteSheetPageSwapY, spriteSheetPageSwapW, spriteSheetPageSwapH);
if (mouseHeld()) {
if (inPatch) {
const {x, y} = reGrid(mouseX, mouseY, patchX, patchY, spriteW, spriteH);
const cellNumber = patchW*y+x;
state.setInPatch(cellNumber, state.selectedSpriteSheet, state.selectedSprite);
}
if (inSpriteSheetPicker) {
const {x, y} = reGrid(mouseX, mouseY, spriteSheetPickerX, spriteSheetPickerY, spriteSheetPickerTabW, spriteSheetPickerTabH);
state.selectedSpriteSheet = spriteSheetPickerW*y+x;
}
if (inSpriteSheet) {
const {x, y} = reGrid(mouseX, mouseY, spriteSheetX, spriteSheetY, spriteW, spriteH);
state.spriteWithinPage = spriteSheetW*y+x;
}
} else if (mouseDown(M.MIDDLE)) {
if (inPatch) {
const {x, y} = reGrid(mouseX, mouseY, patchX, patchY, spriteW, spriteH);
state.dragging = true;
state.dragFromX = x;
state.dragFromY = y;
state.dragFromViewX = state.viewX;
state.dragFromViewY = state.viewY;
}
} else if (mouseHeld(M.MIDDLE)) {
if (state.dragging) {
const {x, y} = reGrid(mouseX, mouseY, patchX, patchY, spriteW, spriteH);
state.viewX = state.dragFromViewX - x + state.dragFromX;
state.viewY = state.dragFromViewY - y + state.dragFromY;
}
} else if (mouseClick(M.MIDDLE)) {
state.dragging = false;
} else if (mouseClick()) {
if (inSpriteSheetPageSwap) {
state.spriteSheetPage = (1+state.spriteSheetPage)%2;
}
}
if (keyPressed(K.ARROW_RIGHT)) {
state.viewX += 1;
}
if (keyPressed(K.ARROW_LEFT)) {
state.viewX -= 1;
}
if (keyPressed(K.ARROW_UP)) {
state.viewY -= 1;
}
if (keyPressed(K.ARROW_DOWN)) {
state.viewY += 1;
}
}
const outlineRect = (x: number, y: number, w: number, h: number, color: number) => {
fillRect(x, y, w, 1, color);
fillRect(x, y, 1, h, color);
fillRect(x+w-1, y, 1, h, color);
fillRect(x, y+h-1, w, 1, color);
}
const draw = () => {
const {
selectedSpriteSheet,
spriteWithinPage,
spriteSheetPage,
} = state;
clearScreen();
fillRect(0, 8, 128, 112, COLOR.DARKGRAY);
// Draw the current patch
drawVoidRect(patchX-1, patchY-1, (patchW*spriteW)+2, (patchH*spriteH)+2);
state.patch.values.forEach((val, i) => {
const spriteX = patchX+spriteW*(i%patchW);
const spriteY = patchY+spriteH*Math.floor(i/patchW);
if (!val) {
return;
}
const [sprsheet, sprite] = val;
drawTransparentRect(spriteX, spriteY, 8, 8);
if (getSheet(sprsheet).sheet_type === "spritesheet") {
useSpritesheet(sprsheet);
drawSprite(spriteX, spriteY, sprite);
}
});
// Draw the bar
fillRect(spriteSheetPickerX, spriteSheetPickerY-1, 128, 1, COLOR.BLACK);
fillRect(spriteSheetPickerX, spriteSheetPickerY, 128, 7, COLOR.DARKGRAY);
// Draw the spritesheet picker
fillRect(spriteSheetPickerX, spriteSheetPickerY, spriteSheetPickerTabW*spriteSheetPickerW, spriteSheetPickerTabW, COLOR.BLACK);
Array(spriteSheetPickerW*spriteSheetPickerH).fill(0).forEach((_, i) => {
const tabX = spriteSheetPickerX+spriteSheetPickerTabW*(i%spriteSheetPickerW);
const tabY = spriteSheetPickerY+spriteSheetPickerTabH*Math.floor(i/spriteSheetPickerW);
let color = COLOR.DARKGREEN;
if (getSheet(i).sheet_type !== "spritesheet") {
color = COLOR.BROWN;
}
if (i === page.activeSheet) {
color = COLOR.PURPLE;
}
if (i === selectedSpriteSheet) {
color = {
[COLOR.BROWN]: COLOR.TAN,
[COLOR.DARKGREEN]: COLOR.GREEN,
[COLOR.PURPLE]: COLOR.PINK,
}[color];
}
fillRect(tabX, tabY, spriteSheetPickerTabW, spriteSheetPickerTabH, color);
drawText(tabX+2, tabY+1, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"][i]);
});
// Draw the spritesheet page swap button
fillRect(spriteSheetPageSwapX, spriteSheetPageSwapY, spriteSheetPageSwapW, spriteSheetPageSwapH, COLOR.BLUE);
drawText(spriteSheetPageSwapX+2, spriteSheetPageSwapY+1, state.spriteSheetPage.toString());
// Draw the spritesheet
fillRect(spriteSheetX, spriteSheetY, (spriteSheetW*spriteW), (spriteSheetH*spriteH), COLOR.BLACK);
if (getSheet(selectedSpriteSheet).sheet_type === "spritesheet") {
useSpritesheet(selectedSpriteSheet);
Array(64).fill(0).forEach((_, i) => {
const sprI = i+64*spriteSheetPage;
const sprX = spriteSheetX+spriteW*(i%spriteSheetW);
const sprY = spriteSheetY+spriteH*Math.floor(i/spriteSheetW);
drawSprite(sprX, sprY, sprI);
if (i === spriteWithinPage) {
outlineRect(sprX, sprY, spriteW, spriteH, COLOR.WHITE);
}
});
}
}
export const maptab = {
update,
draw,
}

96
mouse.ts Normal file
View File

@ -0,0 +1,96 @@
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,
};
const mouseEvents: Array<{
type: "click" | "down" | "up" | "move" | "dblclick",
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 pixX = Math.floor(128*evt.clientX/width);
const pixY = Math.floor(128*evt.clientY/height);
return {
x: pixX,
y: pixY,
}
}
addEventListener("dblclick", (evt) => {
mouseEvents.push({type: "dblclick", button: evt.button, ...eventPixelCoords(evt)});
});
addEventListener("click", (evt) => {
mouseEvents.push({type: "click", button: evt.button, ...eventPixelCoords(evt)});
});
addEventListener("mousedown", (evt) => {
mouseButtonsDown[evt.button] = true;
mouseEvents.push({type: "down", button: evt.button, ...eventPixelCoords(evt)});
});
addEventListener("mouseup", (evt) => {
mouseButtonsDown[evt.button] = false;
mouseEvents.push({type: "up", button: evt.button, ...eventPixelCoords(evt)});
});
addEventListener("mousemove", (evt) => {
const coords = eventPixelCoords(evt);
mouseEvents.push({type: "move", button: evt.button, ...eventPixelCoords(evt), prevX: mouseX, prevY: mouseY});
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];
}

63
nonetab.ts Normal file
View File

@ -0,0 +1,63 @@
import { clearScreen, fillRect } from "./window.ts";
import { drawIcon, drawText } from "./builtins.ts";
import { COLOR } from "./colors.ts";
import { getSheet, setSheet } from "./sheet.ts";
import { mouseClick, mousePos } from "./mouse.ts";
import { reGridWithGap } from "./util.ts";
import { page } from "./viewsheets.ts";
import { useSpritesheet } from "./builtins.ts";
import { codeIcon, mapIcon, spriteIcon } from "./icons.ts";
const gridX = 8;
const gridY = 40;
const cellW = 8;
const cellH = 8;
const gapX = 8;
const gapY = 8;
const sheetTypes = ["code", "spritesheet", "map"] as const;
const defaultSheetVal = {
code: () => "",
spritesheet: () => Array(128).fill(0).map(() => Array(64).fill(0)),
map: () => Array(64*64).fill(0).map(() => [0, 0]),
none: () =>null,
}
const update = () => {
if (mouseClick()) {
const {x: mouseX, y: mouseY} = mousePos();
const g = reGridWithGap(mouseX, mouseY, gridX, gridY, cellW, cellH, gapX, gapY);
if (g) {
const {x, y: _y} = g;
const sheetType = sheetTypes[x];
setSheet(page.activeSheet, sheetType, defaultSheetVal[sheetType]());
page.tab = getSheet(page.activeSheet).sheet_type;
}
}
}
const draw = () => {
clearScreen();
useSpritesheet(page.activeSheet);
fillRect(0, 8, 128, 112, COLOR.BLACK);
drawText(4, 16, "Click an icon below to choose");
drawText(4, 16+7, "this sheet's type...");
// Draw the spritesheet
sheetTypes.forEach((sheetType, i) => {
const sx = gridX+(cellW+gapX)*(i%6);
const sy = gridY+(cellH+gapY)*Math.floor(i/6);
const icon = {
code: codeIcon,
spritesheet: spriteIcon,
map: mapIcon,
none: null,
}[sheetType];
drawIcon(sx, sy, icon, COLOR.BLUE);
});
}
export const nonetab = {
update,
draw,
}

130
pico8_builtins.txt Normal file
View File

@ -0,0 +1,130 @@
- [x] load
- [x] save
- [ ] folder
- [ ] ls
- [x] run
- [ ] stop
- [ ] resume
- [ ] assert
- [ ] reboot
- [ ] reset
- [ ] info
- [ ] flip
- [x] printh (as `log`)
- [ ] time/t
- [ ] stat
- [ ] extcmd
- [ ] clip
- [x] pset
- [ ] pget
- [ ] sget
- [ ] sset
- [ ] fget
- [ ] fset
- [?] print
- [ ] cursor
- [ ] color
- [x] cls
- [x] camera
- [x] circ
- [x] circfill
- [x] oval
- [x] ovalfill
- [ ] line
- [x] rect
- [x] rectfill
- [ ] pal
- [ ] palt
- [x] spr
- [ ] sspr
- [ ] fillp
- [x] btn
- [x] btnp
- [ ] sfx
- [ ] music
- [x] mget
- [x] mset
- [x] map
- [ ] tline
- [ ] peek
- [ ] poke
- [ ] peek2
- [ ] poke2
- [ ] peek4
- [ ] poke4
- [ ] memcpy
- [ ] reload
- [ ] cstore
- [ ] memset
- [ ] menuitem
- [ ] cartdata
- [ ] dget
- [ ] dset
- [ ] serial
- [ ] setmetatable
- [ ] getmetatable
- [ ] rawset
- [ ] rawget
- [ ] rawequal
- [ ] rawlen
== most things below here handled by js or easily included.
-- js Array has most things we want here
- [ ] add
- [ ] del
- [ ] deli
- [ ] count
- [ ] all
- [ ] foreach
- [ ] pairs
- [x] max
- [x] min
- [ ] mid
- [x] flr
- [x] ceil
- [x] cos
- [x] sin
- [x] atan2
- [x] sqrt
- [x] abs
- [x] rnd
- [ ] srand
-- skipping these in favor of bitwise operations if needed
- [ ] band
- [ ] bor
- [ ] bxor
- [ ] bnot
- [ ] shl
- [ ] shr
- [ ] lshr
- [ ] rotl
- [ ] rotr
-- js comes with stuff here (String, Number, typeof, etc.)
- [ ] tostr
- [ ] tonum
- [ ] chr
- [ ] ord
- [ ] sub
- [ ] split
- [ ] type
-- js comes with stuff here
- [ ] cocreate
- [ ] coresume
- [ ] assert
- [ ] costatus
- [ ] yield

130
repl.ts Normal file
View File

@ -0,0 +1,130 @@
import { drawText} from "./builtins.ts";
import { getKeysPressed, shiftKeyDown, shiftMap, K } from "./keyboard.ts";
import { font } from "./font.ts";
import { addToContext, evalCode } from "./runcode.ts";
import { clearScreen, fillRect } from "./window.ts";
import { COLOR } from "./colors.ts";
const lineHeight = 6;
const history: Array<string> = [];
let historyIndex = history.length;
let textLinesAbove: Array<string> = [];
let maxLineLen = 0;
let currentLine = "";
let index = 0;
export const resetRepl = () => {
historyIndex = history.length;
textLinesAbove.length = 0;
maxLineLen = 0;
currentLine = "";
index = 0;
}
const print = (arg: unknown) => {
textLinesAbove.push(...String(arg).split("\n").flatMap((line)=>{
const wrap = [];
while (line.length) {
wrap.push(line.slice(0,32));
line = line.slice(32);
}
return wrap;
}));
textLinesAbove = textLinesAbove.slice(-20);
}
const printVal = (arg: unknown) => {
print(JSON.stringify(arg));
}
addToContext("print", print);
addToContext("printVal", printVal);
const update = () => {
// TODO: model this after the newer editmode.ts version using getKeyboardString
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 (char in font.chars) {
currentLine = currentLine.slice(0, index)+char+currentLine.slice(index);
index += 1;
} else if (key === K.BACKSPACE) {
if (index > 0) {
currentLine = currentLine.slice(0, index-1)+currentLine.slice(index);
index -= 1;
}
} else if (key === K.ARROW_LEFT) {
index -= 1;
if (index < 0) {
index = 0;
}
} else if (key === K.ARROW_RIGHT) {
index += 1;
if (index > currentLine.length) {
index = currentLine.length;
}
} else if (key === K.ARROW_UP) {
historyIndex -= 1;
if (historyIndex < 0) {
historyIndex = -1;
}
const lineAtHistory = history[historyIndex] ?? "";
currentLine = lineAtHistory;
index = currentLine.length;
} else if (key === K.ARROW_DOWN) {
historyIndex += 1;
if (historyIndex > history.length) {
historyIndex = history.length;
}
const lineAtHistory = history[historyIndex] ?? "";
currentLine = lineAtHistory;
index = currentLine.length;
} else if (key === K.ENTER) {
if (currentLine) {
history.push(currentLine);
historyIndex = history.length;
}
print("> "+currentLine);
try {
const ran = evalCode(currentLine);
if (ran !== undefined) {
printVal(ran);
}
} catch (err) {
print(err.name+":\n"+err.message);
}
maxLineLen = 0;
currentLine = "";
index = 0;
}
}
maxLineLen = Math.max(maxLineLen, currentLine.length);
}
const drawTextAbove = () => {
textLinesAbove.forEach((line, i) => {
fillRect(0, 1+i*lineHeight, 4*(line.length+1)+1, lineHeight+1, COLOR.BLACK);
drawText(0, 1+i*lineHeight, line);
});
}
const draw = () => {
clearScreen();
drawTextAbove();
fillRect(0, 1+textLinesAbove.length*lineHeight, 4*(2+maxLineLen+1)+1, lineHeight+1, COLOR.BLACK);
fillRect((2+index)*4, textLinesAbove.length*lineHeight+1, 4, lineHeight-1, COLOR.RED);
drawText(0, 1+textLinesAbove.length*lineHeight, "> "+currentLine);
}
export const repl = {
update, draw
}

56
runcode.ts Normal file
View File

@ -0,0 +1,56 @@
// deno-lint-ignore no-explicit-any
const G: any = {
eval: eval,
};
// deno-lint-ignore no-explicit-any
const builtins: any = {};
const context = new Proxy(G, {
get: (target, prop) => {
if (prop in builtins) {
return builtins[prop as keyof typeof builtins];
}
return target[prop];
},
set: (target, prop, value) => {
target[prop] = value;
return true;
},
has: () => {
return true;
},
});
export const runCode = (code: string) => {
try {
new Function(code);
} catch (err) {
throw err;
}
const fn = new Function("context", `
with (context) {
${code}
}
`);
return fn(context);
}
export const evalCode = (code: string) => {
try {
return runCode(`return eval("(${code.replaceAll('"', '\\"')})");`);
} catch (err) {
if (err.name === "SyntaxError") {
return runCode(`return eval("${code.replaceAll('"', '\\"')}");`);
} else {
throw err;
}
}
}
// deno-lint-ignore no-explicit-any
export const addToContext = (name: string, value: any) => {
builtins[name] = value;
}
export const getBuiltins = () => {
return builtins;
}

55
sheet.ts Normal file
View File

@ -0,0 +1,55 @@
import { getCart } from "./cart.ts";
import { LinearGrid } from "./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) => {
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."
}
return LinearGrid(value, 64);
}

134
spritetab.ts Normal file
View File

@ -0,0 +1,134 @@
import { clearScreen, fillRect } from "./window.ts";
import { drawSprite, drawText } from "./builtins.ts";
import { COLOR } from "./colors.ts";
import { getSpriteSheet, setSheet } from "./sheet.ts";
import { mouseClick, mouseHeld, mousePos } from "./mouse.ts";
import { drawTransparentRect, inRect, outlineRect, reGrid } from "./util.ts";
import { page } from "./viewsheets.ts";
import { useSpritesheet } from "./builtins.ts";
const state = {
selectedSprite: 0,
selectedColor: 0,
get spriteSheetPage() {
return Math.floor(this.selectedSprite/64);
},
set spriteSheetPage(val) {
this.selectedSprite = 64*val+this.spriteWithinPage;
},
get spriteWithinPage() {
return this.selectedSprite%64;
},
set spriteWithinPage(val) {
this.selectedSprite = 64*this.spriteSheetPage+val;
},
get sprites() {
return getSpriteSheet(page.activeSheet);
},
set sprites(val) {
setSheet(page.activeSheet, "spritesheet", val);
}
}
const paletteX = 88;
const paletteY = 16;
const swatchW = 8;
const swatchH = 8;
const paletteW = 4;
const paletteH = 6;
const spriteX = 8;
const spriteY = 16;
const pixelW = 8;
const pixelH = 8;
const spriteW = 8;
const spriteH = 8;
const sheetX = 0;
const sheetY = 88;
const sheetW = 16;
const sheetH = 4;
const spriteSheetPageSwapX = 121;
const spriteSheetPageSwapY = 80;
const spriteSheetPageSwapW = 7;
const spriteSheetPageSwapH = 7;
const update = () => {
if (mouseHeld()) {
const {x: mouseX, y: mouseY} = mousePos();
const inPalette = inRect(mouseX, mouseY, paletteX, paletteY, paletteW*swatchW, paletteH*swatchH);
const inSprite = inRect(mouseX, mouseY, spriteX, spriteY, spriteW*pixelW, spriteH*pixelH);
const inSheet = inRect(mouseX, mouseY, sheetX, sheetY, sheetW*spriteW, sheetH*spriteH);
if (inPalette) {
const {x, y} = reGrid(mouseX, mouseY, paletteX, paletteY, swatchW, swatchH);
state.selectedColor = paletteW*y+x;
}
if (inSprite) {
const {x, y} = reGrid(mouseX, mouseY, spriteX, spriteY, pixelW, pixelH);
const pixelNumber = spriteW*y+x;
state.sprites[state.selectedSprite][pixelNumber] = state.selectedColor;
}
if (inSheet) {
const {x, y} = reGrid(mouseX, mouseY, sheetX, sheetY, spriteW, spriteH);
state.spriteWithinPage = sheetW*y+x;
}
} else if (mouseClick()) {
const {x: mouseX, y: mouseY} = mousePos();
const inSpriteSheetPageSwap = inRect(mouseX, mouseY, spriteSheetPageSwapX, spriteSheetPageSwapY, spriteSheetPageSwapW, spriteSheetPageSwapH);
if (inSpriteSheetPageSwap) {
state.spriteSheetPage = (1+state.spriteSheetPage)%2;
}
}
}
const draw = () => {
const {sprites, selectedSprite, selectedColor} = state;
clearScreen();
useSpritesheet(page.activeSheet);
fillRect(0, 8, 128, 112, COLOR.DARKGRAY);
// Draw the palette
fillRect(paletteX-1, paletteY-1, (paletteW*swatchW)+2, (paletteH*swatchH)+2, COLOR.BLACK);
Object.keys(COLOR).forEach((name, i) => {
const swatchX = paletteX+swatchW*(i%paletteW);
const swatchY = paletteY+swatchH*Math.floor(i/paletteW);
fillRect(swatchX, swatchY, swatchW, swatchH, COLOR[name as keyof typeof COLOR]);
if (name == "TRANSPARENT") {
// transparent
drawTransparentRect(swatchX, swatchY, swatchW, swatchH);
}
if (i === selectedColor) {
outlineRect(swatchX, swatchY, swatchW, swatchH, name === "WHITE" ? COLOR.BLACK : COLOR.WHITE);
}
});
// Draw the current sprite
fillRect(spriteX-1, spriteY-1, (spriteW*pixelW)+2, (spriteH*pixelH)+2, COLOR.BLACK);
drawTransparentRect(spriteX, spriteY, (spriteW*pixelW), (spriteH*pixelH));
sprites[selectedSprite].forEach((pix, i) => {
fillRect(spriteX+pixelW*(i%spriteW), spriteY+pixelH*Math.floor(i/spriteW), pixelW, pixelH, pix);
});
// Draw the spritesheet page swap button
fillRect(spriteSheetPageSwapX, spriteSheetPageSwapY, spriteSheetPageSwapW, spriteSheetPageSwapH, COLOR.BLUE);
drawText(spriteSheetPageSwapX+2, spriteSheetPageSwapY+1, state.spriteSheetPage.toString());
// Draw the spritesheet
fillRect(sheetX, sheetY-1, (sheetW*spriteW), (sheetH*spriteH)+1, COLOR.BLACK);
Array(64).fill(0).forEach((_, i) => {
const sprI = i+64*state.spriteSheetPage;
const sprX = sheetX+spriteW*(i%sheetW);
const sprY = sheetY+spriteH*Math.floor(i/sheetW);
drawSprite(sprX, sprY, sprI);
if (i === state.spriteWithinPage) {
outlineRect(sprX, sprY, spriteW, spriteH, COLOR.WHITE);
}
});
}
export const spritetab = {
update,
draw,
}

98
util.ts Normal file
View File

@ -0,0 +1,98 @@
import { COLOR } from "./colors.ts";
import { fillRect, setPixelColor } from "./window.ts";
export const inRect = (x: number, y: number, rectX: number, rectY: number, rectW: number, rectH: number) => {
return (
x >= rectX &&
x < rectX+rectW &&
y >= rectY &&
y < rectY+rectH
)
}
export function reGridWithGap (x: number, y: number, gridX: number, gridY: number, cellW: number, cellH: number, gapX: number, gapY: number): {x: number, y: number} | null {
const gx = Math.floor((x-gridX)/(cellW+gapX));
const gy = Math.floor((y-gridY)/(cellH+gapY));
if (x >= gridX+(cellW+gapX)*gx+cellW || y >= gridY+(cellH+gapY)*gy+cellH) {
return null;
}
return {
x: Math.floor((x-gridX)/(cellW+gapX)),
y: Math.floor((y-gridY)/(cellH+gapY)),
}
}
export function reGrid (x: number, y: number, gridX: number, gridY: number, cellW: number, cellH: number): {x: number, y: number} {
const gx = Math.floor((x-gridX)/(cellW));
const gy = Math.floor((y-gridY)/(cellH));
return {
x: gx,
y: gy,
}
}
export const outlineRect = (x: number, y: number, w: number, h: number, color: number) => {
fillRect(x, y, w, 1, color);
fillRect(x, y, 1, h, color);
fillRect(x+w-1, y, 1, h, color);
fillRect(x, y+h-1, w, 1, color);
}
export const drawTransparentRect = (x: number, y: number, w: number, h: number) => {
Array(w*h).fill(0).map((_z, j) => {
const jx = j%w;
const jy = Math.floor(j/w);
setPixelColor(x+jx, y+jy, (jx+jy)%2 ? COLOR.BLACK : COLOR.DARKGRAY);
})
}
export const drawVoidRect = (x: number, y: number, w: number, h: number) => {
Array(w*h).fill(0).map((_z, j) => {
const jx = j%w;
const jy = Math.floor(j/w);
setPixelColor(x+jx, y+jy, (jx+jy)%2 ? COLOR.BLACK : COLOR.DARKERBLUE);
})
}
export const subgrid = <T>(array: Array<T>, gridW: number, x: number, y: number, w: number, h: number): Array<T|undefined> => {
return Array(h).fill(0).flatMap((_, i) => {
if (y+i < 0 || y+i > array.length/gridW) {
return Array(w).fill(undefined);
}
const x0 = Math.max(0, x);
const x1 = Math.min(x+w, gridW);
const start = (y+i)*gridW+x0;
const end = (y+i)*gridW+x1;
const before = Array(x0 - x).fill(undefined);
const after = Array((x+w) - x1).fill(undefined);
const middle = array.slice(start, end);
return [...before, ...middle, ...after];
})
}
export const LinearGrid = <T>(array: Array<T>, gridW: number) => {
return {
get(x: number, y: number) {
return array[this.coordsToIndex(x, y)]
},
getIndex(i: number) {
return array[i];
},
set(x: number, y: number, value: T) {
array[this.coordsToIndex(x, y)] = value;
},
setIndex(i: number, value: T) {
array[i] = value;
},
values: array,
indexToCoords: (i: number) => {
return [i%gridW, Math.floor(i/gridW)];
},
coordsToIndex: (x: number, y: number) => {
return y*gridW+x;
},
// TODO: make alterations to subgrid affect parent grid
subgrid: (x: number, y: number, w: number, h: number) => LinearGrid(subgrid(array, gridW, x, y, w, h), w),
}
}

63
viewsheets.ts Normal file
View File

@ -0,0 +1,63 @@
import { clearScreen, fillRect } from "./window.ts";
import { drawIcon, drawText } from "./builtins.ts";
import { COLOR } from "./colors.ts";
import { getSheet } from "./sheet.ts";
import { mouseClick, mousePos } from "./mouse.ts";
import { getCart } from "./cart.ts";
import { font } from "./font.ts";
import { codeIcon, spriteIcon, mapIcon } from "./icons.ts";
import { reGridWithGap } from "./util.ts";
const fontHeight = font.height;
export const page = {activeSheet: 0, tab: "sheet"};
const gridX = 8;
const gridY = 20;
const cellW = 22;
const cellH = 16;
const gapX = 8;
const gapY = 8;
const update = () => {
if (mouseClick()) {
const {x: mouseX, y: mouseY} = mousePos();
const g = reGridWithGap(mouseX, mouseY, gridX, gridY, cellW, cellH, gapX, gapY);
if (g) {
const {x, y} = g;
page.activeSheet = 4*y+x;
const sheet = getSheet(page.activeSheet);
if (!sheet) {
console.log(x, y, g);
}
page.tab = getSheet(page.activeSheet).sheet_type;
}
}
}
const draw = () => {
clearScreen();
fillRect(0, 8, 128, 112, COLOR.DARKGRAY);
// Draw the sheet grid
getCart().forEach((sheet, i) => {
const x = gridX+(cellW+gapX)*(i%4);
const y = gridY+(cellH+gapY)*Math.floor(i/4);
fillRect(x, y, cellW, cellH, i===page.activeSheet ? COLOR.PURPLE : COLOR.BLACK);
drawText(x+(cellW)/2, y+(cellH-fontHeight)/2, i.toString().padStart(2,"0"));
const icon = {
code: codeIcon,
spritesheet: spriteIcon,
map: mapIcon,
none: null,
}[sheet.sheet_type];
if (icon) {
drawIcon(x+2, y+4, icon, COLOR.WHITE);
}
});
}
export const viewsheets = {
update,
draw,
}

253
window.ts Normal file
View File

@ -0,0 +1,253 @@
import {
createWindow,
getProcAddress,
gl,
} from "./deps.ts";
export {mainloop} from "./deps.ts";
import { COLOR, palette } from "./colors.ts";
export const gameWindow = createWindow({
title: "Faux",
width: 1024,
height: 1024,
resizable: false,
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);
addEventListener("resize", (event) => {
gl.Viewport(0, 0, event.width, event.height);
});
const pixelsPerRow = 128;
const top = 1;
const left = -1;
const cell = 2/pixelsPerRow;
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);
// allPixelColors.fill(0);
}
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();
}