Log In  


Cart #fake_menu_test-3 | 2025-10-19 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
9

A fake pause menu for meta-shenanigans in a pico8 game! Inspired by Voices of the Void, I wanted to recreate a framework to fake pico8's built in pause menu. It can't actually favorite or adjust settings (I don't think). Now includes a little ghost pal!

Explanation of methods:

The basic functionality comes from the capability to detect the pause menu button with btnp(6). This alone doesn't stop the real pause menu from appearing. With a poke(0x5f30,1) we can temporarily suppress (not prevent) the pause menu from being activated for this frame.

Now, it's just a matter of recreating the pause menu as close as possible. The pause menu changes based on if it was launched from splore, run from the prompt, or in the bbs. We can deduce a few things from different stat calls.

Firstly, if it is in the bbs, we won't have a favourite option. stat(102) can tell us if we're on a website. Next up is the "Exit to Splore" entry, which only shows up if the cart was launched from splore. We can check by referencing the upper y position of the real pause menu given by stat(13) If it is exactly 39, then the menu has a 5th entry and we show "Exit to Splore".

With both of those sussed out we can show a menu that at first glance looks like the built in menu, and behaves like it.

Changelog:

Now shows "Exit to Splore" option as well as "Add to Favourites" when appropriate.

9


2

Very Funny. I'd love to play a 4th wall breaking game where a background character blinks from time to time during pause...
Wonder if there's a way to know if you're playing from splore with the FAVORITE line or from the web without the line, so the pause menu looks perfect from the web too. Probably some way to kow it with some obscure stat() or peek() call.
Rater than burning the CPU by setting pixels one by one with PSET, you could temorarily set the screen as the sprite source poke(0x5f54,0x60), set the draw palette appropriately, and draw the pause menu area over itself with a single sspr call.

You can see an example of the technique
https://www.lexaloffle.com/bbs/?tid=148256
in the screen zoom out fade.


@RealShadowCaster I never knew you could draw from other parts of memory! That works perfectly.

And I completely forgot the favourite option isn't there on the bbs! After a bit of reading it seems you can tell if a cart is on the bbs by stat(102) since it indicates the website the player is embedded in, or zero if it isn't.


The troll potential of this is out of this world.


1

@Doriencey , I agree :)

@hwd2002 ,
I wouldn't be surprised that zep has hidden the info that the current game has been favorited somewhere in memory or in stat().
Regarding the impossibility of programmatically accessing options, we could gaslight the player into thinking there's something wrong with the controller by quickly flashing a fake options menu as if the cancel button had also been pressed, and then calling the real pause menu with extcmd("pause") to cover our tracks and let the player access the setting for real.
For the reset option, we can also play nice with extcmd("reset") (I don't have anything against your mom, but sneakiness has its own charm)


1

@RealShadowCaster I personally prefer it when spooky games are subtle with their tricks. I was just coding this to be a feasibility test, hence the rather random silliness.

I could see something like this being used in a puzzle game (I think FUZ did it already with menuitem()), or as a way of making the fake pause menu part of the playable map. And since the pause button is detectable before the menu is drawn, you could do the invisible ghost trick with the real menu as well (without animation of course).

It's a gimmick for sure, but one that has a lot of avenues for creativity outside of spookiness.


@hwd2002
Tried to compare your pause menu withe the system one, and got strange differences with colors and cursor in the pause area when I changed the grass tiles outside the pause area (!!!).

After a lot of simplification to try and figure why, here's the minimal demo code that shows the behavior and my partial conclusions:

function screenshot()
 extcmd("screen")
end

function _init()
 menuitem(1,"screen",screenshot)
end

function _draw()
 cls()
 rectfill(0,0,128,128,flr(rnd()*16))
 rectfill(64,0,128,128,flr(rnd()*16))
 for c=0,15 do
  x=85+4*(c%4)
  y=18+4*(c\4)
  rectfill(x,y,x+3,y+3,c)
  y+=30
  rectfill(x,y,x+3,y+3,c)
 end 
 extcmd("pause")
end

The menu cursor has a black outline that is usually not visible because it's the same black as the dark pixels of the pause menu.
The screenshot below has the black outline fully visible because pixels below around the cursor are light.

The difference in menu colors is due to the fact that the light threshold between light and dark pixels is not fixed. Black 0 is always dark, white 7 is always light, but every other color can be either treated as dark or light depending on the average luminosity of the screen picture.
In the example screenshot that is pretty light, only white 7, beige 15 and yellow 10 are considered light.
(the 3 light square in the palette displayed in the pause area)


@RealShadowCaster Very interesting! It seems the light threshold is assuming foreground objects will be brighter than the average brightness for most games, and the menu uses that to try and make foreground stuff be visible over the background. I'll bet Splore uses the same system for its menus.

Of course one must ask themselves how many tokens should be committed to a single gimmick.



[Please log in to post a comment]