Based on excellent work by @HomineLudens and @Vampirics, Pine-2K is a Fantasy Console for the Pokitto meant for quick games.
If you only want to play Pine-2K games, then here’s the download for you:
pine-2k.zip (185.6 KB)
Extract the zip into your SD Card (you should have the pine-2k folder in the root of the card) and you’re set.
Changelog
- v1.4.0 Fix crashes, new code viewer.
- v1.3.0 Lots of bug fixes, compiler generates smaller code, new built-in functions, new example
- v1.1.0 Updated with a request from Marketting, minor fixes, new banners
- v1.0.0 Initial release
If you want to make games, read on!
This is what the full source code to a Pine-2K game looks like.
const minX = 6, maxX = 22;
const minY = 0, maxY = 20;
const boardWidth = maxX - minX;
const boardHeight = maxY - minY;
const heart = builtin("sHeart");
const lvl = builtin("sLvl");
const pts = builtin("sPts");
const ledOFF = builtin("shape2");
const ledON = builtin("sBtn");
const snakeColor = 112;
const bgColor = 113;
const appleColor = 87;
const maxLen = 100;
var snake = new Array(maxLen);
var begin;
var end;
var currentLen;
var len;
var x, y, vx, vy, ax, ay;
var dead, won = 0;
var stepTime;
var delay = 0;
var level = 0;
var inputEnabled = true;
var score = 0;
var prevScore = 0;
var lives = 3;
window(44, 0, 176, 176);
tileshift(2, 0);
io("DURATION", 50);
reset();
function reset(){
if(lives < 0){
highscore(score);
exit();
return;
}
color(bgColor);
for(y = 0; y < boardHeight; ++y){
for(x = 0; x < boardWidth; ++x){
tile(x + minX, y + minY, ledOFF);
}
}
t = 0;
delay = 100 - (level * 5);
x = boardWidth / 2;
y = boardHeight / 2;
currentLen = 1;
len = 5;
vx = 0;
vy = 0;
dead = false;
end = 0;
begin = end;
snake[begin] = (x << 16) + y;
ax = random(0, boardWidth);
ay = random(0, boardHeight);
inputEnabled = true;
}
function pattern(x, y, imgName){
var img = builtin(imgName);
var w = peek(img++);
var h = peek(img++);
x += minX;
y += minY;
for(var ty = 0; ty < h; ++ty){
for(var tx = 0; tx < w; ++tx){
var c = peek(img, tx);
if(c){
color(c - 7);
tile(x + tx, y + ty, ledOFF);
}
}
img += w;
}
}
function plot(i, img){
var c = snake[i];
var ty = (c << 16) >> 16;
tile((c >> 16) + minX, ty + minY, img);
}
function hud(){
color(47);
sprite(50, 165, lvl);
cursor(60, 165);
printNumber(level + 1);
sprite(110, 165, pts);
cursor(120, 165);
printNumber(score);
color(0);
for(var i=0; i<lives; ++i)
sprite(80 + (i * 8), 165, heart);
}
function update(){
if(pressed("C"))
exit();
hud();
if(inputEnabled){
if(pressed("LEFT") && (vx != 1)){
vx = -1;
vy = 0;
inputEnabled = false;
}
if(pressed("RIGHT") && (vx != -1)){
vx = 1;
vy = 0;
inputEnabled = false;
}
if(pressed("UP") && (vy != 1)){
vx = 0;
vy = -1;
inputEnabled = false;
}
if(pressed("DOWN") && (vy != -1)){
vx = 0;
vy = 1;
inputEnabled = false;
}
}
if((time() - stepTime) < delay)
return;
stepTime = time();
inputEnabled = true;
if(won){
vx = 0;
vy = 0;
--won;
if(!won){
++level;
reset();
return;
}
} else {
x += vx;
y += vy;
if(x < 0) x = boardWidth - 1;
else if(x >= boardWidth) x = 0;
if(y < 0) y = boardHeight - 1;
else if(y >= boardHeight) y = 0;
if((x == ax) && (y == ay)){
++score;
len += 3;
if(len >= ((5 + level) * 5)){
highscore(score);
prevScore = score;
delay = 20;
won = 10;
pattern(0, 2, "happyEmote");
return;
}
ax = random(0, boardWidth);
ay = random(0, boardHeight);
}
}
color(bgColor);
plot(begin, ledOFF);
if(dead){
vx = 0;
vy = 0;
if(score > prevScore) --score;
--len;
if(!len){
score = prevScore;
--lives;
reset();
return;
}
currentLen = len;
if(++begin == maxLen) begin = 0;
}
var growing = !dead;
if(vx || vy){
if(++end == maxLen) end = 0;
snake[end] = (x << 16) + y;
if(++currentLen > len){
growing = false;
if(++begin == maxLen) begin = 0;
currentLen--;
}else{
sound(40, 0);
}
}
color(snakeColor);
for(var i = begin; i != end;){
if(growing)
color(random(0, 255));
if(snake[i] == snake[end]){
delay = 20;
if(!dead){
dead = true;
sound(60, 0);
pattern(4, 7, "sSkull");
}
}
plot(i, ledON);
if(++i == maxLen) i = 0;
}
color(snakeColor-1);
plot(end, ledON);
color(appleColor);
tile(ax + minX, ay + minY, ledON);
}
“Hey, is that jav-”
“No.”
To make PINE games you don’t need an IDE or even a compiler. There’s a compiler built-in already! If you wanted to, you could even use the Pokipad text editor to code directly on the device, getting the full retro computer experience.
However, the process is so much easier using FemtoIDE.
FemtoIDE project for writing Pine-2K games: PINE-SDK.zip (859.9 KB)
Inside the pine
folder you will find each project. To make your own, simply create a new folder (with up to 15 characters in the name) and put your code in src.js
. Press Ctrl+G to run the emulator (not Ctrl+R) and see your game.
So what is this, exactly?
With Pokitto projects getting bigger and more complex, I decided to experiment with the opposite by imposing an interesting set of constraints. Some of these constraints can be lifted if we decide they’re too harsh. It’s easier to lift constraints later than to do the opposite.
Here are the current constraints:
All logic must fit in 2KB once compiled.
PINE games don’t get “flashed”. When you select a game in the menu it gets compiled directly into RAM1, which is 2KB in size. Since we’re talking about compiled code, it doesn’t really correspond to sourcecode size. Having shorter variable names or putting all the code in one line isn’t going to help.
Just like old consoles, the palette is fixed.
It’s the 256-color Miloslav palette, so it shouldn’t be too harsh of a constraint and it allows you to do lighting effects with recoloring.
Built-in Graphics
PINE comes with a varied set of 16x16 and 8x8 graphics, such as a tileset by Eiyeron & Jerom, from OpenGameArt. Both sizes can be used as tiles or sprites at the same time, together with recoloring. Coupled with the fact that sprites can be mirrored and/or flipped, there is a good amount of freedom. If you need more, you can load graphics from the SD into RAM. The FemtoIDE project comes with a script that can bundle various graphics together into Resource Packs (see Hiragana Flash for an example of it in use). Loading images from a resource pack is fast enough for animation. Finally, you can load 16-bit graphics directly to the LCD (see the sidescreen art in the example games).
Black Box
In the SDK version you’ll find a readme.md
and a language.md
file that documents much of the API and the language. It doesn’t document every aspect, however. The list of built-ins, for example, hasn’t been published on purpose: discovering and sharing builtins has been left as a bit of a meta minigame: The point is to encourage people to hunt, share and trade easter-eggs.
Why?
Because it’s fun. I had fun making Pine-2K, then I had fun writing (and actually finishing!) each of the included games.
I hope you have fun with it too.