233 lines
4.7 KiB
Markdown
Raw Permalink Normal View History

2023-04-28 17:57:35 -07:00
A game is comprised of up to 16 "bags", each bag being exactly 8196 characters (8 kibibytes)
The first bag is the "core" of the game, and is what executes on startup.
It may include code from other bags.
bags can contain arbitrary data, but the built-in system allows convenient usage of data in specific forms.
The main uses of data are:
1. code
2. planar grid (sprites, maps, etc.)
3. sounds
(5.5 colors * 1.5 saturations + 1 gray) * 3.5 brightnesses
red 2 3
yellow/orange 2 4
green 2 3
blue 2 3
purple 1 3
gray 1 4
6 + 8 + 6 + 6 + 3 + 4 = 33
"data" can refer to a sprite or a sound
## In PICO
A sprite is:
- an 8x8 grid of values each of which is: 4 bits,
- and 8 1-bit flags
that is: 64x4 = 32x8 = 32 bytes
A sound is:
- an array of 32 notes each of which is: pitch(0-63), volume(0-7), instrument(0-15?), effect(0-7),
- 5 flags: 0-1, 0-1, 0-2, 0-2, 0-2
that is: 32x(6+3+4+3) = 32x16 = 64x8 = 64 bytes
## In FEMTO
A sprite is:
- an 8x8 grid of values each of which is: color(0-31), 3 unused bits
that is: 64x8 = 64 bytes
A sound is:
- an array of 16 notes each of which is: pitch(0-63), volume(0-7), instrument(0-15?), effect(0-7),
that is: 16(6+3+4+3) = 16x16 = 32x8 = 32 bytes
A "block" is 64 bytes = 0x40
A "sheet" is 64 blocks = 4096 bytes = 4KiB = 0x1000
A sound-sheet, a sprite-sheet, a code-sheet, a map-sheet
A full map is:
- 128x128 with values from 0-255
A quarter-map is:
- 64x64 grid of values each of which is 8 bits
This is equivalent to a "sheet": the "blocks" are 8x8 regions
What if we allow a "meta-sheet" which contains metadata for other blocks.
Each block in a different sheet is associated with a single byte here.
That means this has 64 sheets' worth of metadata.
A meta-sheet is:
- each block is 64 bytes: one byte for each block of a different sheet. -- the hooking up happens separately
Your package can be comprised of up to 64 sheets (= 256KiB = 0.25MiB)
Each sheet can be accessed by an id 0-63. Each block in a sheet can be accessed by an id 0-63.
Sheet 0 is the entrypoint and is the code that starts running.
draw_sprite(sheet_id, block_id)
play_sound(sheet_id, block_id)
map_get(sheet_id, block_id, x, y)
## The Language
Considering a functional language idea.
We have an IO "monad"?
type systems seem complicated... so we'll skip that for now.
functions are variables.
```
// JSON5 + arrow functions
set {succ: (n) => {
for (key: value in obj) {
}
if (condition) {
// do stuff
}
elseif (cond2) {
// different stuff
}
else {
// more stuff
}
return +(n, 1)
}}
```
If a function is called with the
```
funcname (arg1, arg2) {
// block here
}
```
syntax, then contents of the block can be called by calling `thunk()`.
Each function can also prepare for a chain, by calling `passalong(value)`, and the very next function call can read it with `passalong()`.
```
:x 40
:x 50
obj = {
abc: "def",
ghi: "jkl"
}
arr = [
"foo",
"bar"
]
succ = (n) => +(n, 1)
succ(n)
myif = (cond) => if(cond, thunk)
map(obj, (key, value) =>
if(eq(key, "abc"),
"overridden",
value
)
)
```
Maybe just use lua, js, or lisp after all.
Main downsides of lua: I don't love the function syntax and lack of curly braces.
Downsides of lisp: syntax is hard to get used to.
Maybe: js-like.
So:
- arrow functions
- JSON5 object syntax
- if/else with js syntax
- for with `for (key: value in obj)` OR `for (value in obj)` OR `for (key: in obj)`
- break and continue
- while with js syntax
No `this`.
```
x = 5;
while(condition, () => {
// do stuff
});
if(equal(x, 5), () => {
x = +(x, 1);
});
foreach(obj, (value, key) => {
// do stuff
});
```
```
if cond {
}
elseif cond2 {
}
else {
}
```
Eval strategy: eval current thing. If current thing is function, then eval next thing, and apply the func to that next thing. If current thing is function, eval next thing, and apply the func to that next thing. repeat. Do that if `return-and-call` was used, otherwise, if just `return` was used, move on to evaluate the next thing.
You can pass along certain things, e.g. "if" can say "i ran" or "i didn't run", and the "else" can pick that up and run with it.
You can also set a flag that says "I'm the end of this statement"
The pass-along can also be used to implement setting a variable, like `x = 5`, since the `x` can pass along its name, then the `=` can set it, and end after the `5`.
In general, anything infix can work that way, but precedence won't be right. E.g. `x = x + 5` will set `x = x`, and then add 5 to the result of that assignment, and return, but without updating `x`.
```
for val in obj {
}
for key : val in obj {
}
for key : in obj {
}
```
Functions are created with `fn var thunk`, or `var => thunk`
Properties can be accessed and set with `obj . prop`
Command is:
```
set x = 5
if condition {
// ...
}
```