Create PICO-8 games in TypeScript

Overview

TS-PICO-8 - TypeScript for PICO-8

Create PICO-8 games in TypeScript!

TS-PICO-8 contains all the function declarations (.d.ts) for the PICO-8 API and will compile your TypeScript code to Lua and inject it into your PICO-8 cart.

Compression and mangling options are configurable to optimize for token usage, and an external spritesheet allows you to easily manipulate sprites using the image editor of your choice (i.e., aseprite).

PICO-8 is restarted automatically whenever new changes are detected to provide you with a seamless workflow.

I believe this is the first project to provide an entry point into using a typed language with PICO-8.

Installation and usage

Prerequisites

  1. Install a recent version of PICO-8
  2. Install a recent version of NodeJS
  3. Clone this repository to your local machine
  4. Install dependencies npm install
  5. Make sure "$PWD/node_modules/.bin/" is in your $PATH.

Create a PICO-8 project

  1. Run bin/tspico8.js init -d mycart to generate the default workspace.
  2. Optionally configure p8workspace/tspico8.json to specify compression, mangling, and pico-8 location. Note: tspico8 will attempt to detect the pico-8 location automatically.
  3. Run bin/tspico8.js run -d mycart to watch for changes inside of mycart and recompile/relaunch when detected.

Configuration (tspico8.json)

{
  // Set this if tspico8 can't find your pico-8 install automatically
  "pico8": {
    "executable": "path/to/pico8/executable/file"
  },
  "compression": { 
    "compressedFile": "build/compressed.js",
    "indentLevel": 1,
    "compress": false, // enable compression
    "mangle": false // enable mangling (shortens variable names)
  },
  // Configure mangling
  // See: https://github.com/mishoo/UglifyJS#mangle-options
  "mangleOptions": {
    "toplevel": true,
    "reserved": ["_init", "_update", "_draw"]
  },
  // Configure compression
  // See: https://github.com/mishoo/UglifyJS#compress-options
  "compressOptions": {
    "dead_code": true,
    "conditionals": false,
    "comparisons": true,
    "evaluate": true,
    "booleans": true,
    "loops": true,
    "unused": true,
    "join_vars": true
  }
}

Notes on compilation, compression and mangling

This project is basically a Rube Goldberg machine, and the build process is very brittle. Getting from TypeScript to Lua is accomplished as follows.

  1. A file watcher (chokidar) watches mycart for any changes to *.ts or spritesheet.png.
  2. When a change is detected, tsc is invoked and the resulting JavaScript dumps to mycart/build/compiled.js.
  3. The compiled.js is fed into uglify with the params provided in tspico8.json to produce a compressed JavaScript file mycart/build/compressed.js.
  4. The compressed JavaScript file is then fed to a PICO-8 specific JavaScript to Lua compiler (jspicl-cli) that does something very close to a one-to-one translation and assembles the final cart.

The PICO-8 Lua interpreter is somewhat limited compared to modern Lua, so there are a lot of scenarios where unexpected output from any stage of this process can break your cart.

You will achieve the most stable results by leaving compress and mangle set to false in the tspico8.json file, which is the default. That said, experimenting with compress and mangle can allow you to achieve a substantial savings in your cart size.

USE THEM AT YOUR OWN RISK :-)

Workflow (after workspace init)

  1. Run bin/tspico8.js run -d mycart (or whatever dir you prefer).
  2. Modify mycart/main.ts and PICO-8 will restart upon every file save.
  3. Modify mycart/spritesheet.png and PICO-8 will restart upon every file save.
  4. Spritesheet colors should be limited to the PICO-8 color palette.
  5. If you want to use the PICO-8 sprite editor, you'll need to dump its sprite sheet after making changes (export spritesheet.png from inside of PICO-8).
  6. Any other changes made inside of PICO-8 should be saved back to the cart game.p8 before recompiling, or they will be overwritten after each build.
  7. If you want to use multiple TypeScript files, read this first.

Commands

# builds bin/tspico8.js from typescript
$ npm run build-bin
# creates project skeleton inside of p8build
$ bin/tspico8.js init
# watches for changes and reloads PICO-8 on recompile
$ bin/tspico8.js run
# formats TypeScript code (run before submitting PR)
$ npm run prettier-format

Spritesheet hack (requires seperate Imagemagick install)

# Use imagemagick to remap a 128x128 spritesheet to the PICO-8 color map (included in repo)
$ convert newsheet.png -dither none -remap pico-8-8x.png spritesheet.png

Credits

  1. Most of the code is adapted/borrowed from tic80-typescript.
  2. The jspicl JavaScript to PICO-8 Lua compiler does a lot of the heavy lifting behind the scenes.

Thanks to these projects!

You might also like...

A solution for highlights extraction for sports games by The Unknowns.

A solution for highlights extraction for sports games by The Unknowns.

Sportlight by The Unknowns Next.js, Express.js, NLTK, symbl.ai Inspiration - Problem Statement #3 by Experion Technologies Publishing highlights after

Apr 19, 2022

A fast & reliable transaction API for web3 Games, Bridges and other projects

Gelato Relay SDK SDK to integrate into Gelato Multichain Relay Table of Contents Installation Introduction Quick Start Payment Types Request Types Sen

Dec 31, 2022

Train and test your brain with these memory games

Train and test your brain with these memory games

Want to help grow your Hippocampus? Engage your Cerebrum? Try these games! Train and test your brain with these memory games. Each game is designed to help you increase your memory. How much can you memorize with each game? Test yourself.

Jun 16, 2022

This code can be used to log activity on yours Polytoria Games.

This code can be used to log activity on yours Polytoria Games.

polyGameLogger This code can be used to log activity on yours Polytoria Games. current version: 0.1.1 Basic Setup First of all, you need to create a .

Jun 13, 2022

Add multiple languages support for RPG Maker MV games.

============================================================================ __ __ _ _ _ _ |

Dec 19, 2022

Play logic games and claim exclusive NFTs!

Play logic games and claim exclusive NFTs!

Bit Gaming Samruk Hackathon Winner 🏆 Play-to-earn DAO with exclusive NFT collection Idea We are bringing together curious minds and reward them with

Jun 21, 2022

🚀 A script to boost hours in Steam games.

🚀 A script to boost hours in Steam games.

Steam-HourBoost Where can I find the file to add my games? Follow the path below: /steam-hourboost/steam-boost/steam-games.json Where can I find app-i

Jul 14, 2022

A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.

Obsidian Media DB Plugin A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your va

Dec 21, 2022

Phaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.

Phaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.

Phaser - HTML5 Game Framework Phaser is a fast, free, and fun open source HTML5 game framework that offers WebGL and Canvas rendering across desktop a

Jan 7, 2023
Comments
  • Edit jspicl-cli to allow the usage of classes

    Edit jspicl-cli to allow the usage of classes

    If you create a basic class with a method in Typescript like this :

    class MyClass {
      public myMethod() {
        return "Hello World";
      }
    }
    
    let instance = new MyClass()
    
    function _init() {
      instance.myMethod();
    }
    

    and try to run pico-8-ts, you'll get a nasty error telling you :

    attempt to index local 'myclass' (a function value)
    

    If we look at compiled.js we can see what causes this :

    var MyClass = /** @class */ (function () {
        function MyClass() {
        }
        MyClass.prototype.myMethod = function () {
            return "Hello World";
        };
        return MyClass;
    }());
    var instance = new MyClass();
    function _init() {
        instance.myMethod();
    }
    

    The javascript transpiled by tsc targets ES5, so it doesn't use class and Pico 8 Lua doesn't support prototypes.

    Except that if you change the tsconfig.json to target ES6, even if you get this correct javascript, the problem persists.

    class MyClass {
        myMethod() {
            return "Hello World";
        }
    }
    let instance = new MyClass();
    function _init() {
        instance.myMethod();
    }
    

    Pico 8 still cries about protoypes except we don't have any prototypes in our generated output anymore.

    It's because of jspicl-cli.
    In /node_modules/jspicl-cli/bin/cli at line 100 we can see that it uses Bublé to compile js into < ES2015.
    By default, Bublé strips away classes and use protoypes instead, and that's what transforms our class.

    I removed Bublé and got an error, so instead I've just added the option to skip over classes and leave them alone :

    function getInputOptions({ input, output, ...jspiclOptions }) {
      return {
        input,
        plugins: [
          includePaths({
            paths: [path.resolve(input)]
          }),
    +      buble({
    +        transforms: {
    +          classes: false,
    +        }
    +      }),
          {
            renderChunk: source => source.replace(/\/\/ <!-- DEBUG[^//]*\/\/\s-->/g, "")
          },
          jspiclPlugin(jspiclOptions)
        ]
      };
    }
    

    Now it works fine. Should we create another patch over jspicl-cli ? Or do we make a fork so we can change #1 , this and maybe even replace the jspicl dependency to @jspicl/jspicl so we can migrate to version 3.0.0 and enable ESM ?

    opened by QuentinWidlocher 7
  • "Unknow file extension" error when running an empty project with Node 18.1.0

    I use node 18.1.0 and I tried this tool by creating a sample project by running these commands :

    ./bin/tspico8.js init -d test-project
    ./bin/tspico8.js run -d ./test-project/
    

    After Typescript is done compiling and uglify is done compressing, when ./node_modules/.bin/jspicl-cli is ran, it just hangs there so I ran the jspicl-cli command manually and this is what I got :

    node:internal/errors:466
        ErrorCaptureStackTrace(err);
        ^
    
    TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /Users/Quentin/Documents/Projects/Native/Pico8/pico-8-typescript/node_modules/.pnpm/[email protected]/node_modules/jspicl-cli/bin/cli. Loading extensionless files is not supported inside of "type":"module" package.json contexts. The package.json file /Users/Quentin/Documents/Projects/Native/Pico8/pico-8-typescript/node_modules/.pnpm/[email protected]/node_modules/jspicl-cli/package.json caused this "type":"module" context. Try changing /Users/Quentin/Documents/Projects/Native/Pico8/pico-8-typescript/node_modules/.pnpm/[email protected]/node_modules/jspicl-cli/bin/cli to have a file extension. Note the "bin" field of package.json can point to a file with an extension, for example {"type":"module","bin":{"cli":"./bin/cli.js"}}
        at new NodeError (node:internal/errors:377:5)
        at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:80:11)
        at defaultGetFormat (node:internal/modules/esm/get_format:122:38)
        at defaultLoad (node:internal/modules/esm/load:21:20)
        at ESMLoader.load (node:internal/modules/esm/loader:431:26)
        at ESMLoader.moduleProvider (node:internal/modules/esm/loader:350:22)
        at new ModuleJob (node:internal/modules/esm/module_job:66:26)
        at #createModuleJob (node:internal/modules/esm/loader:369:17)
        at ESMLoader.getModuleJob (node:internal/modules/esm/loader:328:34)
        at async Promise.all (index 0) {
      code: 'ERR_UNKNOWN_FILE_EXTENSION'
    }
    
    Node.js v18.1.0
    

    I managed to get the tool working by renaming ./node_modules/jspicl-cli/bin/cli to cli.cjs and ./node_modules/jspicl-cli/bin/plugin to plugin.cjs.

    I also needed to change the paths in cli.cjs and ./node_modules/.bin/jspicl-cli of course.

    opened by QuentinWidlocher 7
  • Migrate to @quentin_widlocher/jspicl-cli

    Migrate to @quentin_widlocher/jspicl-cli

    Resolve #1 and #2

    This PR :

    • Use @quentin_widlocher/jspicl-cli to :
      • Allow ESM usage with recent versions of NodeJS.
      • Fix the usage of prototypes for classes.
    • Remove now-useless patch-package
    • Target ES6 to fix classes in Javascript
    opened by QuentinWidlocher 1
Owner
Travis Whitton
Travis Whitton
This extensions adds blocks to help you create your own carnival games in MakeCode Arcade using throwable balls, extra timer functions, and extra game-over options.

Usage This extensions adds blocks to help you create your own carnival games in MakeCode Arcade using throwable balls, extra timer functions, and extr

Microsoft 6 Nov 16, 2022
Learn design patterns through games with TypeScript and Phaser 🕹️

Welcome to Design patterns gamified! I created this repo to teach design patterns through games. Each folder contains a tiny game that demonstrates ho

Paula Santamaría 41 Nov 10, 2022
Offline modification of Doodle Champion Island Games by Google

Doodle Champion Island Games This is an offline backup copy of the Doodle Champion Island Games by Google and Studio 4°C. The game has been modified t

null 67 Dec 24, 2022
Javascript engine to make fast games.

G3 Javascript Basic javascript engine to make fast games and write clean code. ??️ Example code: const g3 = new G3() const Window = g3.createWindow("

Starship Code 2 Feb 13, 2022
La extensión web que muestra el precio de los juegos de la web de Xbox, PlayStation, Nintendo y Epic Games Store con los impuestos de Argentina incluidos. Conocé cuanto vas a pagar por tus juegos 💚💙❤️

Conocido anteriormente como Xboxito Impuestito - Conocé el precio real de los juegos Impuestito calcula y muestra el precio de los juegos de la web de

Luke ✨ 23 Dec 4, 2022
Add all games from purchased itch.io bundles to your library.

itch-io-bundle-claimer When you purchase some game bundles on itch.io, you are required to manually claim every game that you want to add to your libr

Anthony Brown 7 Oct 3, 2022
Record games and emulate a League of Legends spectator server

Neeko-Server Neeko-Server is an application that record games emulates a League of Legends spectator server to serve them to the League of Legends cli

Vivi 15 Dec 1, 2022
See a banned user's profile, their friends, their favorite games, their followers etc.

Roblox-Banned-User-Viewer AKA BanView See a banned user's profile, their friends, their favorite games, their followers etc. Ever wondered how to view

SCR1PP3D 4 Nov 18, 2022
Mirror from https://github.com/BochilGaming/games-wabot/tree/multi-device

Games-Wabot Join Group Diskusi NO BOT Deploy to Heroku Heroku Buildpack BuildPack LINK FFMPEG here IMAGEMAGICK here FOR TERMUX USER Type mentioned bel

null 48 Dec 20, 2022
A 👩‍💻 developer-friendly entity management system for 🕹 games and similarly demanding applications, based on 🛠 ECS architecture.

Miniplex Ecosystem miniplex miniplex-react Introduction Miniplex is an entity management system for games and similarly demanding applications. Instea

Hendrik Mans 253 Dec 31, 2022