Log In  

Add an instant replay to your game!

(Shown here: OMEGA ZONE by @kometbomb)

New from the mind that brought you Instant 3D! and still hasn't apologized, Instant Replays!

Paste this snippet into the end of your game and be amazed at your new replay capabilities!!! Or it could fail completely! That's the mystery of it!!!

 local prev={

 local bstate,pstate,addr,isplay={},{},0x8000,false

 local function updatebtns()
  for i=0,5 do
  if isplay then
   local mask=peek(addr) addr+=1
   for i=0,5 do
   local mask=0
   for i=0,5 do
    if(bstate[i]) mask|=1<<i
   if addr<0x8000+0x42ff then
       poke(addr,mask) addr+=1

 local function doreplay()

 cartdata=function() end
 if not isplay then
  local seed=rnd(0xffff.ffff)
  local seed=peek4(addr)addr+=4
  menuitem(5,"end replay",function()dset(63,0)run()end)

 if _update then

 if _update60 then

  return bstate[i]

  return bstate[i] and not pstate[i]

  if isplay then 

Okay, I've exceeded by exclamation point quota.

This is a little experiment in capturing the player's input and replaying it exactly as before in order to make the game play out exactly the same way - assuming everything goes to plan.

To use, paste the snippet into the end of you cart, after the existing code. Then play the game for a minute and select "Replay" from the pause menu. The replay will continue looping until you select "End replay" from the pause menu.

Note - There are a bunch of requirements/caveats for this to actually work:

  • Your game must be controlled by the player 1 gamepad only. Player 2 controls, mouse, keyboard etc is not captured.
  • Pico-8 must be able to read/write to an external cart (named "mot_replay.p8"). I don't think this will work in binary/web exports.
  • Your game must not use:
    • Pico-8 memory above 0x8000
    • Menu item slot 5
    • Cart data slot 63
  • The snippet will hijack your cart data and use cartdata("replay") instead
  • Replays cut off after 10 minutes or so due to limited storage space.

And there's probably other ways it can fail that I haven't thought of :-). Try it and see..

A few games it seems to work with:

Harold's Bad Day by @biovoid

Phoenix by @pahammond
(Had to remove the cartdata() call from the snippet)

Ninja Cat by @cubee
(Had to remove the cartdata() call from the snippet)

Super Disc Box by @Farbs

P#106415 2022-02-06 08:55 ( Edited 2022-02-07 07:38)

Cart #mot_grandprix_editor-2 | 2021-10-09 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This is the track editor for Mot's Grand Prix

I don't recommend running this in browser as you cannot save/load tracks - anything you build will be lost.
Rather download the file and run it in Pico-8 itself.

You will also need to download the main game and run it in Pico-8 in order to race on any tracks you create.

This editor requires a keyboard and mouse.

Existing tracks

You can download the tracks from the existing game from this Google Drive folder. Place them in your carts folder (next to the editor) then select "Load" from the editor hamburger menu (top right) to load them.

Basic usage

IMPORTANT: There are known bugs that will crash the editor, so save often!

  • Use the arrow keys to scroll along the track.
  • Use the hamburger menu (top right) to load save and export tracks.
  • The "New" menu creates new stages, curves and items (described in detail further down).
  • Click "Drive" to drive on the track (select "Editor" from the pause menu to return to the editor).
  • Select things by clicking on them. You can then edit their properties, or delete them by clicking "-".
  • After making a change you usually have to click "Update" (bottom right) to update the visual model (I didn't have enough tokens to make this automatic).

Track format

The basic components of a race track are:

  • Stages
  • Curves
  • Items


Are used to partition the race track. Every track must have at least one stage.
The advantage of having multiple stages is that their curves and items are stored independently of the other stages, so if you modify the curves in a stage you only need to fixup the positions of the background items within that stage. Also they can help you keep track of what part of the track you are working on.

Click "New->Stage" to add a new stage. The new stage will be inserted immediately after the current stage.

The current stage is displayed top-left underneath the menu bar.

You can click on the name to edit it. Or click "-" to delete it.
Deleting a stage deletes all its curves and background items, so be careful.


Curves define the shape of the race track, i.e. the corners, straights, hills, valleys etc.
Note: Despite the name a "curve" doesn't have to actually curve. It can be completely straight.

Click on the race track itself to select a curve.

Click "New->Curve" to add a new curve.
If a curve is already selected, the new curve will be inserted immediately before it in the current stage.
If no curve is selected, the new curve will be added to the end of the current stage.
To delete the curve, select it and click the "-" button next to the curve (NOT the one next to the stage!)

Edit the curves properties to modify it.
Most properties are edited using sliders. Drag with the left mouse for small increments and right mouse for large.
Remember to click "Update" to see the result of a change.

  • Length = Length of the curve in units (units are about 4-10 meters long - scale isn't very accurate in this game)
  • Turn = How sharply the road turns. 0 is straight, negative values turn left, positive turn right.
    For reference the hairpin turn in "Suzuka" is -0.32, which is close to the limit of how sharply cars can corner in this game.
  • Pitch = Vertical gradient. 0 is flat, -1 is up 45 degrees, 1 is down 45 degrees.
    Pitch is an optional property. Tick the tick-box to enable it. If omitted the pitch from the previous curve will be used.
    Pitch is linearly interpolated. The value you set is actually the pitch at the end of the current curve. It will smoothly transition from the previous pitch to that value across the curve.
  • Left/Right = Define the horizontal range, i.e. how far left/right cars can go without crashing into a barrier.
    The left and right barriers are visually represented by the "L Barrier" and "R Barrier" settings below. It's a good idea to set these - otherwise the player will simply crash into an invisible barrier.
    The road is 8 units wide, so the "Left" usually is -4 or less, and right 4 or greater, although they can be set to -3/3 to clip into the gravelly region of the road. The AI cars expect the full width of the road to be available though, so be careful when doing this.
    Left/right are also optional, linearly interpolated properties.
  • L Barrier/R Barrier = Define the visual left and right barriers. Each one is a list of item types that will be repeated. Tick the tick box to specify the barrier type, then click "[New]" to add an item type.
    Select the item type from the list (use the mouse wheel to scroll). Make sure to only select item types starting with "Barrier_" (I didn't have enough tokens to automatically filter the list).
    Generally it makes sense to use just one barrier types, or combine "Barrier_Fence" with one of the other types.
    L Barrier/R Barrier are optional.
  • Trees = Generate random trees on the left/right side of the map.
    This is a list of horizontal positions to place the trees at trees' horizontal positions vary randomly by a few units as well.
    Trees are optional.


Items are background items, like stadiums, buildings, signs. They don't affect gameplay, but make the track look more alive.
Click on a background item to select it.
You can also use the "<" and ">" buttons to cycle through the items in a stage (useful if you accidentally place one in a position where it is never visible and can't be selected normally).
Click "New->Item" to create a new item, then select the item type from the list (mouse wheel to scroll).
The item will be created in the current stage, a few units in front of the camera.
To delete the item, click the "-" button next to it.

Background items have 3 properties representing their position relative to the race track.

  • X = Horizontal position relative to the race track (negative = left, positive = right)
  • Y = Vertical position relative to the ground (negative = up, positive = down)
  • Z = Distance along track from start of stage.

Other properties

You can also edit the sky and ground colours by selecting "Properties" in the hamburger menu.

Generated content

The following items are generated automatically and cannot be edited:

  • Race track shoulder things (I tried to Google for the proper name, but only found things that stop horses from biting your shoulders...)
  • Distance to turn signs
  • Racing line markers


To race on your track in the actual game, you need to export it:

  1. Click "Export" in the hamburger menu. This will copy a string to the clipboard.
  2. Open up "Mot's Grand Prix" in Pico-8.
  3. Open tab 8.
  4. Choose a track to replace (there isn't enough compressed space left to add any more).
  5. Replace the string for one of the existing tracks. Be sure to switch to "Puny font" mode (press Ctrl+P) before pasting from the clipboard.
  6. Optionally edit the "finline" variable to set the finish line distance.
  7. If you want to change the track name on the start screen, edit the "tracknames" variable in tab 1.

Your track should now be in the game.

P#97963 2021-09-30 23:37 ( Edited 2021-10-09 11:58)

Cart #mot_grandprix2-0 | 2022-04-22 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Title screen artwork by Marco Vale

Update: The track editor is now available here.

Update 2: Added Autodromo Nazionale di Monza circuit

Update 3: Added last two tracks (Hockenheim and Spa Francorchamps) plus championship mode. The BBS version now plays identically to the itch.io multi-cart version.

Step back in time to an era when folk were real folk and racecar steering wheels were real steering wheels.
Mot's Grand Prix is a pseudo 3D Formula 1 racer inspired by Grand Prix Circuit, Continental Circus and a bunch of other 80s/90s racers I've forgotten :).

Strap into your single seater and blast around 6 classic race courses:

  • Brands Hatch
  • Hockenheim (old layout)
  • Autodromo Nazionale Monza
  • Silverstone
  • Spa Francorchamps
  • Suzuka

Or enter the championship and try to be top of the points table at the end of the season.

The game has 4 difficulty levels plus a practice mode to learn the courses without the pressure of racing.


  • Left/Right = Steer
  • Z/Up = Accelerate
  • X/Down = Brake

Mouse control

You can also enable mouse control in the pause menu. It has 3 settings:

  • Off = No mouse control. Keys/gamepad only.
  • Steering = Mouse controls steering (left/right axis). Keys/gamepad controls acceleration/braking.
  • Full = Mouse controls steering (left/right axis) and acceleration/braking (up/down axis).

"Steering" mode can be quite playable if you use a large window size or full-screen mode. Use the keyboard keys to accelerate/brake as usual.
"Full" mode is not really playable unless you're some kind of robot, but can be useful for other input devices if they can be configured to output mouse movements (e.g. a Steam controller).


This is a game where you have to slow down for the corners!
Use the colour of the racing line to judge your speed.

  • Green -> You are fine
  • Yellow -> You are close to the limit, but should be able to make the turn
  • Red -> You are likely going too fast for the corner

If you're really having trouble, you can switch on "Auto Brake" in the pause menu. Auto-brake automatically slows you down for corners, but not to avoid crashing into other cars.

Don't be timid about muscling through traffic
There is no damage model. Your car is indestructible. Be fearless! :-)

Hope you enjoy this game.

P#97606 2021-09-21 12:36 ( Edited 2022-04-22 03:16)

Just a suggestion, but for modern controllers it would be really nice if the extra controls could be mapped like:

  • Right trigger = X
  • Left trigger = O
  • Right thumbstick = Mouse

That would allow FPS games like Poom, Sorcerer to be played like a twin stick shooter on capable input devices, without actually changing Pico-8's input model.

P#96136 2021-08-17 12:48

This is a cut down version of the variable inspector I wrote a while ago. It's a bit more limited, but a lot more light weight. And there's a way to use it even if you have no tokens to spare (described below).

You use it to view Lua table variables:

  1. Run your game
  2. Press Esc to break into it
  3. Type: dinsp(variable)

Then use the mouse to expand values and scroll wheel to scroll up/down.

Code snippet follows (for some reason I can't use the code formatting tags):

poke(0x5f2d,3)cursor()pal()clip()function dadd(n,v,i,p)add(lines,{n=n,v=v,x=type(v)=="table"and"+"or" ",i=i},p) end function dexp(l,p)if l.x=="+" then for n,v in pairs(l.v) do p+=1 dadd(n,v,l.i.." ",p)end l.x="-"end end

function dinp()local x,y,b,w=stat(32),stat(33),stat(34),stat(36)line(x,y,x,y+2,10)if btnp(❎) then local i=(y-10)\6+1 local p=s+i local l=lines[p]if l and (x-10)\4==#l.i then dexp(l,p)end end s=max(min(s-stat(36),#lines-18),0)end

function dui()while true do rectfill(10,10,118,118,1)for i=1,18 do local l=lines[i+s]if l then print(l.i..l.x..l.n..":"..tostr(l.v),10,(i-1)*6+10,7)end end dinp()flip() end end function dinsp(v)lines,s={},0 dadd("value",v,"",#lines+1)dui()end

The snippet is 246 tokens.
If you don't have 246 spare tokens, you can still use it as follows:

  1. Run your game
  2. Press Esc to break into it
  3. Paste in each of the 3 snippet lines one at a time, pressing enter after each.
  4. Type: dinsp(variable)

The snippet must be entered as 3 separate lines because Pico-8 has a 255 character limit for lines in immediate mode.

P#95305 2021-07-26 07:12 ( Edited 2021-07-26 07:13)

This is a suggestion, not a bug obviously.

The ability to toggle on a metronome tick/beep every 4 beats in the sfx editor would be quite helpful for composing tunes.

P#95021 2021-07-18 08:41

Cart #mot_sorcerer-11 | 2022-04-10 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

UPDATE: The dungeon generation logic has been revamped. Rooms are not always rectangular, and can often have a bit more cover (or hiding places for monsters). Also added a couple of new enemy types. Gold has been removed (I needed the extra tokens), but it didn't really do anything anyway.

Grab your magic staff and venture deep into the demon realm to defeat Bahmott and his evil servants!

Trial of the Sorcerer is a procedurally generated 3D first person shooter inspired by Wolfenstein 3D and Catacomb Abyss.

Shoot monsters, collect loot, find keys to unlock doors, and try to find the exit to the next level.
And don't forget to pickup the power crystals to level-up your magic staff along the way.


Arrows = Move
X = Shoot
Z = Hold to strafe

Or use player 2 controls (E,S,D,F) to move/strafe and player 1 controls to turn.

Mouse input can be enabled via the pause menu (once in game), but only when playing via the Pico-8 application as mouse locking doesn't currently work in the browser version.


Doors with a small black keyhole require keys.
Each door requires a specific key, so if one won't open it means you don't have the right key yet.

P#94900 2021-07-16 05:45 ( Edited 2022-04-10 02:42)

Cart #mot_crypt-1 | 2021-07-02 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Still needs quite a bit of finishing off.
I'm posting it now mainly so I can play it on my phone :-)

Levels are randomly generated. Shoot monsters, collect loot, find keys to unlock doors and reach the exit.

P#94197 2021-06-29 09:35 ( Edited 2021-07-02 08:23)

Cart #mot_formula1-4 | 2021-02-19 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Strap into one of the world's fastest race cars and blast around the legendary Bronzerock circuit like it's 1988!
...or something - there's not a lot of physical or historical accuracy in this game :)


Driving off road slows you down. Driving too fast around corners causes you to slide.

You can use the pause menu to view your last/best lap times (my best so far is 1:43.23)

This is work-in-progress, so some caveats:

  • There are no other cars yet.
  • Lots of things need tweaking/adjusting.
P#87611 2021-02-14 03:01 ( Edited 2021-02-19 23:18)

Cart #mot_f1-0 | 2021-02-05 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This is very unfinished and I wasn't planning to release this just yet, but somebody asked to play it so here it is :-)

Z/X accel/brake
Left/right to steer

Eventually there will be consequences for leaving the road and crashing into things.

P#87276 2021-02-05 21:12

Cart #mot_wolf3d-3 | 2021-01-29 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This isn't really a game - unless you consider it a short "Walking simulator" - it's more of a tech demo.

The engine is a basic Wolfenstein-3D like 3D engine. It has floor and ceiling textures and render reasonably sized and complex rooms at 60 frames-per-second, in a 128x96 viewport.

  • Arrow keys = move
  • X = toggle map mode

If anyone feels like something out of it, it's fairly easy to get started with (details below).

Levels are built using the map editor, using the sprites on page 1.

The bottom left sprites are walls, except the left-most one which positions a door.
The next row up is for placing objects.
The numbered circles are for placing triggers that trigger code when the player reaches them.
The gray arrows at the top are for setting the player start position and direction.

You can use the top left 124x32 tiles of the map area.

Wall textures (and door texture) are sprite tabs 2 and 3.

You can define up to 8 (including the door).

Objects are sprite tab 4.

They are always 16x16 pixels. You can define up to 16.

Objects must be defined in the "otyp" array (code tab 1):

-- y    h   w  solid flat
 { .33,.4, .5, true},
 {   0, 1, .3, true},
 { .5,.45, .7, false,true},
 {.375,.5, .7, true},
 { -.3,.4, .3, false},
 { .3,.35, .4, true},
 { .5,.45, .8, false,true},
 { .1, .8, .4, true},
 { .2, .6, .6, true}    
  • y = y position (-0.5 = ceiling, 0.5 = floor)
  • h = height
  • w = width
  • solid = true if object will obstruct player's movement
  • flat = true to flatten object to floor/ceiling

Floor and ceiling textures are defined at the very right of the map.

Floor and ceiling "plane types" must also be defined in code tab 1:

-- plane types
--       tex  scale height xvel yvel
pl_tile ={ 0, 0.5   }
pl_panel={ 1, 0.5   }
pl_dirt ={ 2, 0.125 }
pl_stone={ 3, 0.25  }
pl_sky  ={ 4, 7,    10,    .007,.003}
  • tex = Which "texture" to use. 0 = topmost.
  • scale = Texture scale factor.
  • height = Optional. Set the plane height, e.g. for sky textures. Otherwise defaults to floor/ceiling height.
  • xvel,yvel = Optional. Creates moving planes.

You then select the floor and ceiling planes by setting the "floor" and "roof" variables.

floor={ typ=pl_dirt,  x=0,y=0 }
roof ={ typ=pl_sky,   x=0,y=0 }

P#86903 2021-01-29 07:19 ( Edited 2021-01-29 23:26)

Cart #mot_flight-3 | 2020-12-14 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

A little arcady "flight sim" I started writing.
Buzz past randomly generated islands in a wrap-around ocean.

I will probably add stuff to blow up eventually :)

There is no throttle! Just steer with arrows.

P#84733 2020-11-27 07:47 ( Edited 2020-12-14 10:05)

by Mot
Cart #mot_pool-23 | 2020-10-08 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This is Mot's 8-Ball Pool, a little pool simulation inspired by 3D pool on the C64 and Amiga.
Shoot a round of pool against a friend, or one of the 7 different AI characters.


The UI at the bottom of the screen shows how each player is progressing.
The player's name flashes when it is their turn.
The color they must sink is displayed next to their name, and the balls already sunk are displayed above.
A white ball indicates they have a free ball.

When player fouls, the reason for the foul is displayed in a scrolling message along the bottom of the screen.


I'll assume you know the basic rules of 8-ball pool, so I'll just describe how the rules have been implemented.
The game uses a simplified set of rules:

  • The game ends when the black ball is sunk.
  • If you sink it (legally) after sinking all your colored balls, you win. Otherwise you forfeit and your opponent wins.
  • The first ball sunk becomes that player's color.
  • A legal shot involves hitting a ball of your color first, and not sinking the wrong color, white ball or black ball (except when you're supposed to).
  • A free ball is awarded to the other player after a "foul". A free ball means you get a free second shot even if you don't sink a ball - as long as your shot was legal.

Hope you enjoy it.
If you're curious, you can see the development progress in this Twitter thread.

P#82696 2020-10-08 08:05


I felt like transcribing a little tune I made in Caustic to Pico8.
Could use a bit more work (instruments, balancing, is pattern 14 off key?) but I'm happy enough with it.

P#82002 2020-09-20 04:27 ( Edited 2020-09-20 04:27)

by Mot
Cart #mot_pool-22 | 2020-10-06 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

A work-in-progress 3D pool table simulation.

P#81393 2020-08-31 11:55 ( Edited 2020-10-06 08:57)

Not sure if anyone has posted a smap() yet, but it's reasonably straightforward to implement with tline, so here's mine.

Parameters are:
cx,cy,cw,ch Specify a region in the tile map. Measured in tiles.
sx,sy,sw,sh Region on the screen to map to.
flipx,flipy Whether to flip horizontally and/or vertically.
layers Layer flags. Same as for map().

You need to supply at least the c and s parameters.

function smap(cx,cy,cw,ch,sx,sy,sw,sh,flipx,flipy,layers)

 -- negative screen sizes
 if(sw<0)flipx=not flipx sx+=sw
 if(sh<0)flipy=not flipy sy+=sh

 -- delta
 local dx,dy=cw/sw,ch/sh

 -- apply flip
 if flipx then
 if flipy then

 -- clip
 if(sx<0)cx-=sx*dx sx=0
 if(sy<0)cy-=sy*dy sy=0

 -- render with tlines
 -- pick direction that results
 -- in fewest tline calls
 if sh<sw then
  -- horizontal lines
  for y=sy,sy+sh-1 do
  -- vertical lines
  for x=sx,sx+sw-1 do

Cart #mot_smap-0 | 2020-07-22 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#79752 2020-07-22 05:44

Mot's Animation System

Cart #mot_animsys-2 | 2020-07-19 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Mot's Animation System is a visual editor for creating 2D animations.

You start by sequencing sprites and/or tilemaps together to create the basic components. Then combine them using timelines and key-frames to make animations. Or combine them again to make bigger, more complex animations.

Animations can trigger sounds and music, and call back into to your main program, e.g. to trigger game play events.

You could use it for:

  • Character animations, like run cycles
  • Larger multi-sprite animated characters
  • Cutscenes
  • Animated birthday cards

Animations can be loaded and played in your own carts (the loading and playback code is 1836 tokens). They can either be stored in Lua strings or loaded from a separate cart.


This is all very much in Beta at the moment!. And some corners had to be cut to fit everything into the token limit. In particular the editor doesn't prevent you from shooting yourself in the foot by:

  • Creating self referencing animations (infinite recursion)
  • Creating animation files larger than 0x4300 bytes (about 17K) that won't save properly.

It lacks any sort of undo/redo logic, so if you use this, please take frequent backups of your saved animations.

And also, because the animation code is 1836 tokens and the animation data can be several kilobytes, it's not the best fit for every program.

Feel free to @ me if you need any help.

Overview and basic navigation

WARNING: While you can edit animations in the nested browser version you can't save them! So please don't get too invested if you play with it in-browser. Run it from Pico-8 itself in order to save your changes.

Selecting and playing animations

Move the mouse over the window to display the UI.

You can pan the animation with the middle mouse button, and zoom in/out with the scroll wheel.
At the bottom right there are 3 controls:

  • A slider to zoom in/out (behaves exactly the same as the mouse wheel)
  • A play/pause button that does what you'd expect
  • A home (house) button recenters the animation and sets the scale back to 1

The name of the current animation is displayed at the top. Click on it to select another animation.

Use the mouse wheel to scroll through the animations. Animations with a person icon are sprite/tilemap animations. Animations with a dotted line icon are key frame based animations.


At the top right are the menu icons. Clicking on them opens a drop down menu.

The hamburger menu is for creating/deleting animations.

You can create new animations of different types, or delete the current animation.

The hourglass menu is for saving and loading animation sets.

You can save/load your animations to from external carts or import sprites and tilemaps from another cart.

This is covered in more detail later.

Animation properties

Click "Properties" at the bottom of the screen to expand the properties panel.
This is where you edit the current animation. The UI changes based on the animation type.

When expanded, the "Properties" bar also displays two icons. These toggle between different tabs.
The I icon displays information and settings for the animation. Once again they differ based on the animation type. The other icon will be the person or dotted line depending on the animation type, and switches back to the main editor.

Creating animations

Importing graphics

(...and music and sound effects)

In order to create animations for your own carts, you'll want to import the sprites and tilemaps that your cart uses.
Click the hourglass icon at the top right, then select "IMPORT GFX". This will list the carts in your Pico-8 carts folder.

Once again you can scroll with the mouse wheel.
Click on the cart to import its sprites, tile maps, sound effects and music into the editor.

Clearing animations

Select "Clear" from the hourglass menu to clear out the demo animations.

Saving and loading

The hourglass menu allows you to save and load your animations. Animations are saved to Pico-8 cartridges ("carts").
They overwrite the sprites in the cart, and possibly the tile maps, sfx and music, depending on how many animations you have defined. Therefore you should not save your animations into your existing game carts!

To avoid catastrophe the editor will only save/load animation data into cart files that end with .anim.p8

"Save", "Save as" and "Load" work as you would expect.

The first time you save (or if you click "Save as") you will be prompted with a list of animation carts to save into.

Or you can click "New Cart" and key in the name of a new cart to create.

Enter the name without the .p8 extension, as the editor will add .anim.p8 automatically.

WARNING: The editor can only save 0x4300 bytes of animation data to a cart (about 17K). If your animations grow beyond this limit, they will not save correctly and may not reload either.
You can keep tabs on this by running Pico-8 in console mode. The number of bytes written is reported in the console when you save, so you can see when it gets near the 17K limit (for reference, the demo animation is about 3.8K).
Taking frequent backups of your animation file is strongly advised.

Sprite animations

Sprite (and tile map) animations are the building blocks of all animated sequences.
Create a sprite animation by clicking the hamburger icon and selecting "NEW SPRITE ANIM".
To edit the animation, make sure the "Properties" section is expanded.

This UI allows you to define a sequence of frames, each of which displays a sprite.
The frames of the animation are displayed horizontally.

Click on a frame to select it.
If the animation has more than 5 frames, click the rightmost frame to scroll right and the leftmost to scroll left.

The "+" and "-" buttons add and delete frames respectively. Adding always creates a new frame at the end of the animation. Alternatively you can insert a new frame immediately after the selected frame by clicking "Ins".

The bottom of the editor shows the available sprites.

As in Pico-8 itself, sprites are grouped into 4 pages, with page number "tabs" at the top right.
To set the sprite for a particular frame, first click on the frame, and then click on the sprite in the sprites area.
The sprite for the current frame is displayed with a white rectangle.

You can also flip a frame horizontally or vertically by ticking "Flip X" and "Flip Y" respectively while the frame is selected.

Information and settings

Click the I icon on the "Properties" bar to display the sprite animation information and settings.

The Name allows you to give your animation a name, so you can find it more easily in the animation chooser.
You should try to choose short names in order to save space and so that they display properly in the animation list. (Ideally the editor would enforce this but it doesn't have enough spare tokens.)
The name is also used to fetch animations when rendering them in your own carts, so it is a good idea to give each animation a unique one.

The Type allows you to choose between a sprite or tile map based animation. (Tile map animations are described in the next section.)

FPS is the frames-per-second speed of the animation.
The value is set using a "number editor" box. Click and drag left/right to decrease/increase the value. Click and drag with the right mouse button to decrease/increase 10x faster.

Size is the horizontal (left box) and vertical (right box) size of each frame in sprites (or tile map tiles, if using a tile map animation).

The Origin defines the origin point of the animation. If the animation is rendered at coordinates X,Y then the origin is the part of the animation that will align with that position on screen. It is also the part of the animation that stays in the same place if you scale it larger or smaller. It's stored as two values ranging from 0 to 1, representing the fraction of the animation's width and height. So (0,0) sets the top left corner as the origin. (1,1) sets the bottom right corner. (0.5,0.5) sets the center.

Hover the mouse over the animation preview area to see the origin point (displayed as a gray cross)

Tile map animations

Tile map animations are special sub class of sprite animations, where the animation frames are defined as sections of the tile map. They are useful for creating larger "sprite" animations, or to define backgrounds for animated scenes.

To create a tile map animation, first create a sprite animation (as described above), click the I to bring up the information/settings and click "Tiles".
Typically you will want to set "Size" here as well (to the width and height of the animation in tiles).

Click the person icon to edit the tile map frames.

The UI is similar to defining sprite frames UI. The main difference is that the tile map is displayed instead of the sprite pages. Use the middle mouse button to drag the tile map around, so that you can select the region that corresponds to your animation frame.

(The UI is unfortunately a bit cumbersome and would have benefited from a mouse-wheel zoom. But it's usable.)

Timeline animations

This is the main feature of the animation system: the ability to create animations defined by key frames on a timeline.
Timeline animations are built from other animations, which are often sprite animations but can be other timeline animations as well. In this section we will refer to the other animations as "child animations".
The key frames specify which animations to play at which positions, plus other attributes like scale and flip flags. The animation system interpolates between these values based to create the animation.

Create a timeline animation by clicking the hamburger icon and selecting "NEW TIMELINE ANIM".

The timeline animation UI has two main areas.

Timelines and key frames

The left area contains the timelines and key frames.

The blue top line is for triggering events, like sound effects. This is covered later on.

The gray lines are timelines. Each one displays a single "child" animation. You can include more child animations by creating more timelines, using the buttons at the bottom.

The + and - buttons add and delete timelines respectively.
Use the up and down arrows to re-order the timelines. This controls the order that the child animations are drawn on screen. Timelines are drawn from top to bottom, so the topmost timeline's animation will appear behind the others, and the bottom timeline's animation will appear in front.

The purple dots on the timeline are key frames.
These can be added and deleted with the key frame buttons:

To insert a key frame, first click on the timeline at the position corresponding to the desired time. Then click +.
Note: If key frames are close together it may be difficult or impossible to click on the timeline between them without selecting one of the existing ones. The work around is to click + to create the new key frame anyway, then move it to the desired time.

To delete a key frame, click on it to select it, then click -.

Key frame properties

The right hand side displays the properties of the selected key frame.

Use the number box at the top to set the key frame time on the timeline (drag left/right with the mouse).
(I would have preferred to implement this as dragging the key frame along the timeline, but you know... tokens.)

The key frame properties are grouped into two tabs, which are accesed by clicking the icons to the right of the number box.

The first tab () has the following properties:

Anm selects which child animation to play. Click on the animation name to select it from the list. The child animation will play from this key frame onward. If "None" is selected, the key frame will not change the child animation.

Loop indicates whether the child animation will loop. If not it will stop once it has played to the end (or start if playing backwards).

T and Spd set the time and speed of the child animation. You can use these to start the child animation half way through, or play it in reverse, or double speed etc.
T and Spd are optional properties. This means you can choose not to specify them on a particular key frame, which is useful if you want to add a key frame to change a different property (say the on-screen position). Optional properties are controlled by a tick box on the right hand side which must be ticked in order to enter a value.
For T and Spd there is only one tick box, as you must either set them both, or none at all.

Visible indicates whether the child animation is visible from this key frame onward.

Click the icon to display the second tab.

It has the following properties:

Pos sets the position of the child animation as a X and Y coordinates, relative to the center of the current time line animation. If you render the animation in the center of the screen (like the editor does by default), then 0,0 will position the child animation there.
Pos is an optional property.
It is also an interpolated property. As the animation plays the position will be linearly interpolated between the values supplied on the key frames, so that the child animation moves smoothly from one position to the next.

Sca sets the horizontal and vertical scale factor of the child animation. A value of 1 means no scaling.
Sca is an optional interpolated property.

Flp flips the child animation horizontally and/or vertically from the current key frame onward.

Note: The Visible property is also available on this tab page. It is the same property as on the other page.

Information and settings

As with sprite animations you can click the I icon to display the information and settings.

Refer to the sprite animation section regarding the Name property.

The Duration allows you to specify the animation duration in seconds.

Storyboard animations

A storyboard animation allows you to play a set of animations one after the other. It is useful for stitching together smaller scenes, like the scenes that make up the demo animation. The editor ensures that each animation starts immediately after the previous one finishes, and adjusts it appropriately in response to duration changes.

To create a storyboard animation, click the hamburger icon and select "NEW STORYBOARD ICON".

The storyboard animation UI is essentially a cut down version of the timeline animation UI.There is no events timeline and only one animation timeline, and key frames are restricted to a subset of properties.

Create the storyboard by adding key frames to the single timeline and setting the "Anm" property to the animation that should play.

Unlike timeline animations, you cannot set the key frame time directly. Instead there are two new key frame buttons that allow you to control their order.

Click the left button to move the key frame to towards the start of the sequence. The right button moves it towards the end.

Animation events

Timeline animations can also trigger events. These can be used to play sound effects and music, or display text on the screen. You can also create your own event types and handle them in your own carts.

Events are created by inserting key frames into the blue top-most timeline.

The Typ property specifies the event type.
The remaining properties are data to supply to the event handler. If an event handler doesn't use a particular property, you can choose to omit it by leaving its right-hand tick-box unticked (which saves a byte or two in the animation data).

Click on the value to select the event type.

The editor displays the built-in event types that it understands. Or you can click "Custom" to create your own event type. Custom event types are ignored by the editor, but you can implement event handlers in your own carts (this is described later on).
You should keep your custom event names short, as the name string is stored in each event.

The built-in events are as follows:

SFX plays a sound effect.
n=The sound effect number.

MUS starts/stops music.
n=The starting pattern. Omitting it (unticking the right tick-box) indicates that the music should stop.

TXT displays text on the screen.
txt=The text to display
pos=The screen coordinates
n=The number of frames the text will remain on screen

RELTXT displays text on screen relative to the current animation.
This is exactly the same as TXT except that "pos" is relative to the center of the current animation.

CLR clears all text from the screen.

Playing animations in your own carts

Loading and playback code

In order to load and playback animations from your own carts, you will need the loading and playback code.
All this code is in tab 1 of the "Mot's Animation System" cart, and needs to be copied into your cart. Be aware that it will cost you 1836 tokens.

Loading from an external cart

Loading animations from an external cart means that your cart will not be self-contained, and may prevent it from working when uploaded onto the Lexaloffle BBS. However it's relatively easy to setup, and makes for a streamlined workflow, where you save your changes in the editor and simply rerun your cart to load them.

You might want to use this method while developing your cart, and then switch to the embedded Lua string method (described later) once you're ready to release it.

To load from an external cart:

    -- load into gfx ram, then decode
    local stream=readmemstream(0x0)

    -- restore gfx ram

    -- extract animation to play    

This copies the animation data into memory, and deserializes it into a Lua table.
The process overwrites the sprites and possible tile maps, sfx and music, so the "reload" is necessary to copy them back in from the cartridge ROM.
Individual animations can then be fetched by name.

Rendering an animation

To render an animation, call the "draw" method:


Where t is the animation time in seconds.

By default the animation will not loop, but you can use the "duration" method to achieve this if necessary.


The full list of parameters for "draw" is:
t=The animation time
x,y=The position on screen (defaults to 64,64)
xscale,yscale=The horizontal and vertical scale factors (defaults to 1,1)
flipx,flipy=Whether to flip horizontally or vertically (defaults to false,false)
dt=The delta time from the last frame (defaults to 1/30 or 1/60 depending on whether _update or _update60 is defined)

The delta time is used to trigger events, and tells the animation system how far the animation has advanced since the previous frame. The animation system will trigger all events on the timeline between then and the specified time.

Drawing text

If an animation contains "txt" or "reltxt" events, this text is not rendered immediately by the "draw" method, but queued up to be rendered later. This this allows you to render one or more animations then draw the text last so that it appears in front.

After drawing your animations, render the text by calling the "framedone" method of the "anim_events" global variable:


Handling animation events

When an animation event triggers, the animation system looks for a function on the "anim_events" global variable with the same name as the event to call.
The system sets up a default "anim_events" table with handlers for the built-in events ("sfx","mus","txt","reltxt","clr"), so these events will work automatically.

You can handle your own events by adding functions to the anim_events table.

The parameters are:

ev=The event key frame.
This is a table variable with properties:

  • t
  • x,y
  • n
  • txt

Which correspond to the values configured in the animation editor. Property values (except t) can be nil if no value was entered.

x,y=The screen position of the animation that triggered the event.

Note: To save tokens the event is called as a function, not a method. I.e. there is no "self" parameter.

After frame rendering

You can also create event types that render content after the animations have been rendered (like the "txt" and "reltxt" events), by calling anim_events.doafterframe and passing it a function to run.
The function will be converted into a coroutine, so you can use yield() for multi-frame drawing.

For example, here's an event that causes the screen to flash 3 times. The screen will flash white by default, or you can specify a colour by setting "n":


   -- three flashes
   for i=1,3 do
    -- one frame of white
    cls(ev.n or 7)

    -- two normal frames

Embedding animations as Lua strings

Instead of loading animation data from an external cart, you can embed it directly into your Lua source as hexidecimal strings, to create a single self-contained cart.

For example, here's the demo animation embedded into a cart as a Lua string:

Cart #mot_animintro-1 | 2020-07-19 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

You will need enough free space to fit the strings in without exceeding the compressed size limit however.

To generate the Lua strings you must run Pico-8 in console mode.
Load your animation in the editor, then click the hamburger icon and select "EXPORT ANIMS".

The editor will write a table of hexidecimal strings to the console window. This looks like (on Windows anyway):

INFO:  "6d617301390108622d636173746c65022810103232019007000107622d68696c6c7302281008320001a00b000107622d7261696c73022810013232012008000105622d736561022810103232018007000107622d7472656573022810103232012000000107632d64616e6365013b010232640a4000004100004000004100014000004100004200004200004200004200000107702d737061726b012802023232010100000106742d616e696d022810043232010003000107742d616e696d73022812043232010003000108742d656469746f72012804013232013000000106742d6d6f7473022810043232010000000108742d73797374656d022810043232018005000108742d76697375616c01280401323201200000010674682d61726d01280102321401460000010774682d626f647901280202416401430000010774682d636c756201280201503201740000010674682d6c656701280102326401450000010774682d6c65676201280102326401470000010774722d626f647902280b05323201940200010774722d7073746e02280201323201980600010774722d726f643202280501323201180700010874722d736b69727402280b01323201140500010874722d736d6f6b6501130101323203600000610000620000010974722d776865656c7302280b023232019405000308632d64616e636532880001020000",
INFO:  "0b06000000000000640004440009060000640005000305696e74726f963201090000091b000064000492090b1c000000000000640004860b091d0000640004de0d091e00006400042a12091f00006400046a1809200000640004521c092100006400043a2009220000640004d2280923000064000400020731312d74686f679209010400000f3169001400c800c800000064000ce8030a19001400820000000c6c0702190014000c9e07026900140008034c0403747874070c0008005a1374686f67206265207468696e6b696e672e2e2eaa05037478740702000e00781f77686174206973206d6f74277320616e696d6174696f6e2073797374656d3fd007037478740710003c00781869276d20736f20676c616420796f752061736b65642e2e2e020831322d7469746c65f401040400000b2c00000000000064000c960004640064000caa0004640064000ca00004960096000c0200000b29000000000000640000b9000a0100cdff0000b400040200000b29000000000000640000d2000ad0ff23000000aa00040200000b29000000000000640000f0000a31000e000000dc000403c800037366780202e100037366780203fa00037366780204020731332d646573635802030600000b0df6ffd6ff0000640008410004320032000c550004780078000c690004640064000cc20102f6ffd6ff0cef0102a6ffd6ff0c0600",
INFO:  "000b0a1800d6ff0000640008730004320032000c870004780078000c9b0004640064000cc201021800d6ff0cef01025200d6ff0c07000009090000640008eb000600000d00280028000cff0004640064000c130104550064000cc20104550064000cdb0104550005000cef01040a00050008040000037478740714001400ff0469732061af00037478740768001400ff03666f72c0000672656c74787407f4ffe8ffff067069636f2d38c20103636c7200020632312d7365714c04030600000f06d6ff2200c800c8000000000000fa000004770102d6ff220004f4010b1900001e00000096000c52030200001e000c010402000060000c0400000f0611000200c800c8000a00000000130100047701021100020004f4010200001e00000400000f0627002a00c800c80046000000002c01000477010227002a0004f4010200001e00000611000672656c74787407d3ffd8ffff1673657175656e636520737072697465206672616d6573170203636c720058020672656c74787407d8ffd8ffff136c6f6f6b696e6720676f6f6420627269616e21de0303636c7200f401036d757302054704036d757300020732322d636f6d62400601042c010d30c800c800000064000500000000f4010b310a002300000064000d78050250002300080400000672656c74787407c0ffd8ffff20636f6d62696e65207370726974657320666f",
INFO:  "72206d6f726520636f6d706c657864000672656c74787407ecffe2ffff0a616e696d6174696f6e73260203636c7200e8030672656c74787407f2ffe8ff3c1274686f67206665656c2077656972642e2e2e020532332d6267e803020500000b24800000000000640008af0002800000000ce10002000000000c840302000000000cb6030280ff00000804000009310000640009e9000ae6ff0800000064000d30020af5ff08008c0000000d840300090300000672656c74787407c4ffceff5a1d7573652074696c65206d61707320666f72206261636b67726f756e6473c2010672656c74787407dcffe7ff3c0d6b6e6f636b206b6e6f636b2e2e9e020672656c74787407f6ffceff46127765277265206e6f7420617420686f6d652e020832342d636f6d6232e8030105c800093500006400047c0108b400000004580208b40064000494020b3914000000000064000ce8030292ff00000c0200000672656c74787407ceffc4ff5a196f7220746f206d616b65206c61726765722073707269746573f4010672656c74787407ceffc4ff641a77686963682063616e20616c736f20626520636f6d62696e6564020833312d7363656e659808010200000b2d000000000000000008c80008000064000c0400000672656c74787407c4ffc4ff781c7468656e20636f6d62696e6520697420616c6c20746f67657468657264000672",
INFO:  "656c74787407d8ffceff7813746f20637265617465206375747363656e65732c01036d757302003308036d757300020634312d6f7574c409010300000b2b00000000000064000c080704640064000c6c07046400000008000209622d636173746c6532e803030100000b0400000000000064000c0100000b020000000000006400080100000b0100000000000064000c000208622d747265657332e803020100000b0500000000000064000c0100000b0300001500000064000c000208622d747265657366c800030100000b0400000000000064000c0100000b020000e1ff000064000c0100000b2700000000000064000c000208622d74726565736cc800020200000b2500000000000064000cc80002800000000c0200000b2580ff0000000064000cc80002000000000c000206632d74686f67c800010100000a00000000000064000c000208702d737061726b32c800010400000f07000000000a000a00000064000c1e0004640064000cc800040a000a00086e0004640064000c000208732d636173746c65e803020100000b2400000000000064000c0200000b31e4ff0800320064000d2c010af0ff0800960000000d0177010672656c74787407d8ffe4ff3c0d74686f6720636f6d6520696e3f0205732d6f7574d007040200000b2c00000000000064000c2c01082c0100000c02b4000b3992ff1e00000064000d13",
INFO:  "0102b0ff1e000d03e1000b3157001400000096000c8a020a390014003c0000000c00000008028c000b19ecffe4ff000064000c00000008000207732d7469746c65f401030414000f0b0000dcff64006400000064000c000006000070ff900190010c5e01020000dcff0c7c01028000dcff080532000f080000000090019001000064000c00000008460004640064000c720102000000000c90010280ff0000080564000f0c0000900090019001000064000c78000600002400640064000c00000008860102000024000ca401028000240008031900037366780201460003736678020178000373667802010207732d747261696ed007020100000b2600000000000064000c0400000b346e00f6ff000064000c5802020a00f6ff0cb004020a00f6ff0c6c07022efff6ff0c01dd020672656c74787407d8ffd8ff3c1a74686973207363656e65206d616b65206e6f2073656e73652e2e020a74682d61726d636c75626400020100000f10feff0b0087006400000064000c0100000b0e00000000000064000c00020774682d626974739001040700000b1201000000000064000c320002030000000c6400020100ffff0c960002ffff00000cc80002010000000c0e010210000b000c5e0100080700000b0f0000f8ff000064000c3200020000faff0c6400020000f8ff0c9600020000faff0cc800020000f8ff0c0e01020900f4",
INFO:  "ff0c540100080700000b11ffffffff000064000c320002fdff00000c670002ffff00000c960002010000000cc80002ffffffff0c0e0102fcff0d000c720100080700000b2e0000efff000064000c3f00020100f1ff0c7100020000efff0ca00002fffff1ff0cc800020000efff0c0e0102edffe7ff0c7c01000c0232000373667802009600037366780200020774682d6d616b65c800010100000b2f0000000090019cff0400020774682d77616c6bc800010100000b2f00000000000064000c00020774722d626974735e01060200000b38e0ffe6ff000064000c640000080400000b330000fbff000064000c6400020000fbff0ca0000aefffeaff000000000c2c0100080400000b1600001300000064000c640002000013000ca000021e0009000cfa0000080400000b1800001500000064000c640002000015000ca0000209001c000c130100080700000f151500130056006400000064000c320002150019000c190002110016000c4b0002180016000c640002150013000ca000022b002d000ce10000080700000b15f0ff1900000064000c320002f0ff13000c190002f3ff16000c4b0002edff16000c640002f0ff19000ca00002ddff28000cc800000800020874722d626f6479321400010200000b1300000000000064000c0a0002000001000c00020774722d6c6f6e67e803060100000b3124000a000000000004",
INFO:  "0100000b3900000000000064000c0100000b365b001500000064000c0100000b1945000a00000064000c0100000b1954000a00140064000c0100000b1965000a000a0064000c00020774722d6d616b65f000010100000b320000000054019cff0400020974722d706c7466726de803020100000b160000f9ff000064000c0100000b1800000000000064000c00020974722d736d6f6b65326400010200000b170000000000006400046400020000ebff0400020974722d736d6f6b65336400040100000b3700000000000064000c0100000b37000000001e0064000c0100000b37000000003c0064000c0100000b3700000000500064000c00020874722d747261696e6400010100000b3200000000000064000c00",
INFO:  "",
INFO:  "",
INFO:  "",
INFO:  "",
INFO:  "",
INFO:  "",
INFO:  "",
INFO:  "",
INFO:  ""}

You will need to copy it from the console window and remove the "INFO: " line prefixes. You can optionally remove any blank strings at the end of the table.

Then add it to your code, assigning it to a variable. For example:


You can now use the following code to load the animations:

    -- load animation from animdata string
    local stream=readstrstream(animdata)

    -- extract animation to play    

P#79085 2020-07-12 00:44 ( Edited 2020-07-19 06:57)

OK, this may be a bit bonkers, but I figured out how to play Ramps with a racing wheel, with proper analogue steering and acceleration/braking.

It's a little involved and only works on Windows.

Mouse input

First, you need a version of Ramps (or whatever game you plan to play) edited to accept mouse input. Like this version:

Cart #mot_ramps_mouse-0 | 2020-06-06 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

WARNING: Attempting to drive with an actual mouse may cause high blood pressure and throwing things.
Also I've only tried this in PICO-8 itself - not sure if it works when running in a browser.

Joystick -> mouse

Next, you need a utility to convert game controller input to mouse movement.

I couldn't find one that did exactly what I needed, so I ended up making my own here.
Tip: If you try it out remember F12 toggles it on/off - as you can't use the mouse when it's enabled.


So basically run the utility, run the mouse-input version of Ramps in PICO-8, switch to full-screen, and go :-)

P#77721 2020-06-06 01:58

I'm a bit new here, so I don't know if adding network capability has previously been discussed (?)

But it feels to me some simple UDP send/receive instructions would add some cool functionality, without adding too much complexity.

Perhaps something like:

[write message to RAM]

Where [ip-locn],[port-locn] would be some special reserved locations for IP address and port.
UDP packets are usually small. I've seen <=512 bytes recommended in places, which easily fits into the user RAM address range.
And an IPV4 address would fit in a number variable, and would be adequate for LAN play.

Receive could be:

[read message from RAM]

Returning 0 if nothing is waiting.
It could also populate [ip-locn] with the return IP address so that replying would be straightforward.

Perhaps [port-locn] could be pre-populated with a default port number (0x1C08 ?).

Any thoughts?
Am I trying to make it simpler than is actually possible? (I haven't done socket programming in a while, so I forget what's necessary to get UDP/IP working).

P#77688 2020-06-05 09:36


by Mot
Cart #mot_ramps-21 | 2020-06-22 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This is Ramps! A 3D racer inspired by Powerdrift, Stunt Car Racer, Hard Drivin'.

Choose from 8 varied race tracks, and overtake the other drivers in 5 laps to win.
As well as racing you'll need to navigate jumps, roller-coaster like ramps and occasionally even drive upside down.

Includes 3 difficulty levels, plus a practice mode for getting the feel of the tracks.

This all started as an experiment to see how well Pico-8 could render a Powerdrift-like scaled sprite racetrack (pretty well, as it turns out). Then it was about trying to implement a driving model that could handle the ramps, jumps and loops. And finally wrapping it up into a somewhat finished game.

Like with Loose Gravel, I would have liked to add a championship mode, but there just aren't enough tokens :-)

Big thanks to everybody who gave advice and feedback. Particularly @freds72 who even went as far as digging into the code and making some performance optimisations (~20% CPU improvement!).


Z = Accelerate
X = Brake
Left/Right to steer

You don't have to wait to be automatically respawned after you fall of the track, select "Respawn" in the pause menu to get back into the race faster.

The green and red lines on the speedometer are recommended minimum and maximum speeds.
The green line (minimum speed) is particularly important for clearing jumps (and occasionally getting up steep hills), so make sure to keep your speed above it.
The red line (maximum speed) is to prevent you from sliding out on sharp corners or overshooting the jumps. Typically this is just a suggested speed. Depending on your racing line, driving style etc you may be able to go faster.


Update: Other cars' speeds now vary randomly throughout the race
Update 2: Can knock other cars sideways a bit & they don't knock you sideways as much. Round road sprite corners a bit.
Update 3: Fix game crashes in AI code when road is blocked.

P#77443 2020-05-31 05:23 ( Edited 2020-06-22 08:03)

View Older Posts
Follow Lexaloffle:        
Generated 2022-07-02 18:09:20 | 0.115s | Q:78