andrejtrindade [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=86005 load x cartdata <p>I am currently developing my second PICO-8 game. I'm considering adding an option to load my first game directly from the new cart, if the player wants to give it a try.</p> <p>Both games are single carts, and there is no sort of communication between them. It's a simple <em>load</em> call with a <em>breadcrumb</em> parameter.</p> <p>I was concerned about <em>cartdata</em> though. Both games use <em>cartdata</em> to store progress, each using its own ID. And <a href="https://www.lexaloffle.com/dl/docs/pico-8_manual.html">the PICO-8 User Manual</a> states, in section 6.11, that <em>&quot;CARTDATA can be called once per cartridge execution&quot;</em>.</p> <p>The wording on the manual indicates that my approach was OK, but I decided to test it anyway, just to make sure.</p> <h1>Only once</h1> <p>First, I simply called <em>cartdata</em> twice to see how PICO-8 would enforce the single call.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>cartdata(&quot;test_load_1&quot;) cartdata(&quot;test_load_2&quot;)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>This is the error message:</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/cartdata.png" alt="" /> <h1>Simulating carts</h1> <p>I created two simple test carts. Below is the code for test_load_1.p8, which simulates <em>cartdata</em> and <em>load</em> behaviour in my new game.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _init() cartdata(&quot;test_load_1&quot;) n = peek(0x5e00) end function _update() if btnp(4) then n += 1 if (n &gt; 9) n = 1 poke(0x5e00,n) end if btnp(5) then load(&quot;test_load_2&quot;,&quot;load test_load_1&quot;) end end function _draw() cls() color(7) print(&quot;test_load_1 - n = &quot;..n) print(&quot;&quot;) print(&quot;press 🅾️ to add 1 to n (max 9)&quot;) print(&quot;&quot;) print(&quot;press ❎ to load test_load_2&quot;) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>There is a vaiable, <em>n</em>, that starts at 0 if there is no save data. You press 🅾️ to increment it by 1 (up to 9, then it loops back to 1). Whenever you do that, the new value is persistent in <em>cartdata</em> (ID = <em>&quot;test_load_1&quot;</em>).</p> <p>You press ❎ to load test_load_2.p8, which simulates <em>cartdata</em> behaviour in my old game. The code for that follows below.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _init() cartdata(&quot;test_load_2&quot;) n = peek(0x5e00) end function _update() if btnp(4) then n += 10 if (n &gt; 90) n = 10 poke(0x5e00,n) end end function _draw() cls() color(7) print(&quot;test_load_2 - n = &quot;..n) print(&quot;&quot;) print(&quot;press 🅾️ to add 10 to n (max 90)&quot;) print(&quot;&quot;) print(&quot;use pause menu to go back&quot;) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here you press 🅾️ to increment n by 10 (up to 90, then it loops back to 10). Whenever you do that, the new value is persistent in <em>cartdata</em> (ID = <em>&quot;test_load_2&quot;</em>).</p> <p>To go back to test_load_1, you must use the last option in the pause menu to follow the <em>breadcrumb</em>.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/53_pause.png" alt="" /> <h1>Results</h1> <p>There were no surprises in my tests. Both carts were able to update their save data independently, no matter how many times I switched back and forth between them. I even checked the .txt files in pico-8\cdata and everything was properly persisted.</p> <p>This may seem obvious and unnecessary, but I just wanted to make sure before implementing the feature in my new game. I searched the forums and couldn't find a definitive answer, so I decided to test for myself.</p> <p>I made this post hoping this might be useful for someone. Happy coding!</p> https://www.lexaloffle.com/bbs/?tid=149969 https://www.lexaloffle.com/bbs/?tid=149969 Wed, 02 Jul 2025 12:29:15 UTC test_102 <p> <table><tr><td> <a href="/bbs/?pid=169494#p"> <img src="/bbs/thumbs/pico8_test_102-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=169494#p"> test_102</a><br><br> by <a href="/bbs/?uid=86005"> andrejtrindade</a> <br><br><br> <a href="/bbs/?pid=169494#p"> [Click to Play]</a> </td></tr></table> </p> <p>A very simple cart to test:</p> <ul> <li><code>stat(102)</code></li> <li><code>load(&quot;#bbs_id&quot;)</code></li> <li><code>load(&quot;localfile_or_multicartexport&quot;)</code></li> </ul> <p><em>Note: I used <a href="https://www.lexaloffle.com/bbs/?tid=55172">tiny tactics</a>, my first PICO-8 game, as an example.</em></p> <h1>stat(102)</h1> <p>Value returned by <code>stat(102)</code> depending on how the cart is run:</p> <ul> <li>number 0 when running .p8 file in PICO-8.</li> <li>number 0 when running cart from SPLORE.</li> <li>number 0 when running Windows .exe standalone.</li> <li>empty string when running .html standalone export directly from filesystem.</li> <li><em>&quot;www.lexaloffle.com&quot;</em> string when running from BBS forum post.</li> </ul> <p>I suppose running the .html standalone from somewhere will return the domain name as a string.</p> <h1>BBS ID</h1> <p>When <code>load(&quot;#bbs_id&quot;)</code> works:</p> <ul> <li>when ruuning .p8 file in PICO-8.</li> <li>when running from BBS forum post.</li> <li>when running from SPLORE.</li> </ul> <p><code>load(&quot;#bbs_id&quot;)</code> doesn't work when running .html and binary exports, as stated in the <a href="https://www.lexaloffle.com/dl/docs/pico-8_manual.html">PICO-8 User Manual</a> (section 6.1).</p> <h1>Local .p8 file or multcart export</h1> <p>I exported test_102 as .html and .bin <em>including tiny_tactics.p8</em> to test multicart behaviour.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>export test_102.html tiny_tactics.p8 export test_102.bin tiny_tactics.p8</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/failed.png" alt="" /> <ul> <li>The .html standalone shows a <strong>DOWNLOAD FAILED</strong> notification on the bottom of the screen when trying to <code>load(&quot;#bbs_id&quot;)</code>.</li> <li>The standalone Windows executable does not do that.</li> </ul> <p><em>Note: I only tested the Windows binary.</em></p> <h1>Conclusion</h1> <p>Calling <code>load(&quot;localfile_or_multicartexport&quot;)</code> first, then <code>load(&quot;#bbs_id&quot;)</code>, will always work without showing the <strong>DOWNLOAD FAILED</strong> notification.</p> <p>You can test this pressing ⬇️ in this test cart.</p> <h1>Code</h1> <p>Code is so simple I decided to include it here as a snippet.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>-- test_102 -- by andrejtrindade function _init() s102 = stat(102) end function _update() if btnp(2) then load(&quot;#tiny_tactics&quot;, &quot;load test_102&quot;) load(&quot;tiny_tactics&quot;, &quot;load test_102&quot;) end if btnp(3) then load(&quot;tiny_tactics&quot;, &quot;load test_102&quot;) load(&quot;#tiny_tactics&quot;, &quot;load test_102&quot;) end if (btnp(4)) load(&quot;tiny_tactics&quot;, &quot;load test_102&quot;) if (btnp(5)) load(&quot;#tiny_tactics&quot;, &quot;load test_102&quot;) end function _draw() cls() color(7) print(&quot;stat(102):&quot;, 0, 18) print(&quot;[&quot;..s102..&quot;]&quot;) print(&quot;&quot;) print(&quot;type:&quot;..type(s102)) print(&quot;&quot;) print(&quot;press ❎ = load bbs id&quot;) print(&quot;press 🅾️ = load local/exported&quot;) print(&quot;press ⬆️ = bbs &gt; local/exported&quot;) print(&quot;press ⬇️ = local/exported &gt; bbs&quot;) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>I hope the above results are useful to someone.</p> https://www.lexaloffle.com/bbs/?tid=149956 https://www.lexaloffle.com/bbs/?tid=149956 Tue, 01 Jul 2025 18:19:31 UTC stat(102) <p>I am currently developing my second PICO-8 game. I'm considering adding an option to load my first game directly from the new cart, if the player wants to give it a try.</p> <p>I know, from <a href="https://www.lexaloffle.com/dl/docs/pico-8_manual.html">the PICO-8 User Manual</a> (section 6.1), that <em>&quot;BBS carts can be loaded from other BBS carts or local carts, but not from exported carts.&quot;</em> But how can I detect from which type of cart the code is running?</p> <h1>stat(102)</h1> <p>I know, from <a href="http://pico8wiki.com/index.php?title=Stat">the PICO-8 Wiki</a>, that I can use <code>stat(102)</code> (not officially documented) to gather URL information. For BBS carts, it returns <em>&quot;www.lexaloffle.com&quot;</em>. For local carts, it returns <em>0</em>.</p> <p>I wrote a simple test cart to investigate a little further.</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function _init() s102 = stat(102) end function _update() end function _draw() cls() color(7) print(&quot;stat(102):&quot;,0,0) print(&quot;[&quot;..s102..&quot;]&quot;,0,6) print(&quot;type:&quot;..type(s102),0,18) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Here is what I found...</p> <h2>.html standalone, BBS and splore</h2> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/Standalone HTML.png" alt="" /> <p>The .html standalone returns an empty string when running directly from the filesystem. I suppose that, if I upload this somewhere, it will return the domain, like <em>&quot;itch.io&quot;</em>.</p> <p>I don't want to upload this test cart to the BBS, but I suppose it would return <em>&quot;www.lexaloffle.com&quot;</em> if I did, both running from a post in the forum and running from <strong>splore</strong>.</p> <p>Can someone confirm this?</p> <h2>.bin standalones and .p8 carts</h2> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/Standalone BIN Windows.png" alt="" /> <p>Unfortunately, both .bin standalones (which can <strong>NOT</strong> load BBS carts) and .p8 carts running inside PICO-8 (which <strong>CAN</strong> load BBS carts) returns <em>0</em>, so I can't use <code>stat(102)</code> to differentiate.</p> <p><em>Note: I only tested the Windows executable.</em></p> <p>Any suggestions on another way to detect if the code is running from an exported binary or from a .p8 cart inside PICO-8?</p> https://www.lexaloffle.com/bbs/?tid=149952 https://www.lexaloffle.com/bbs/?tid=149952 Tue, 01 Jul 2025 15:15:58 UTC Tiny Tactics Postmortem <h1>Tiny Tactics Postmortem</h1> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/24_1-title.png" alt="" /> <p>A few months ago I discovered PICO-8 and decided to make a game for it. It was a nice experience, and I'm proud of this little turn based, tactics / puzzle game. I'll try to summarize below how I made <strong>Tiny Tactics</strong>.</p> <p>I didn't have much spare time to devote to this project (maybe 5 to 10 hours a week), but I think it went pretty smooth. I spent exactly 3 months making the game (from purchasing a license for PICO-8 to exporting the final version of the cart).</p> <p>You can play the game and download the cart <a href="https://www.lexaloffle.com/bbs/?tid=55172">here</a>.</p> <p>I have also created a <a href="https://github.com/andrejtrindade/tiny_tactics/tree/main">project</a> on GitHub. It contains:</p> <ul> <li>source code before flattening / compression;</li> <li>spritesheet and map exports;</li> <li>a standalone HTML version; </li> <li>binaries for Windows, Mac, Linux and Raspberry Pi; </li> <li>a <strong>walkthrough</strong>: GIFs I recorded using PICO-8, showing my solutions for each level; </li> <li><strong>leaderboards</strong>: more GIFs, showing how to get <em>gold trophies</em> I'm aware are possible (first person to claim the record gets the credit).</li> </ul> <p>Note: <strong>Tiny Tactics</strong> was created using PICO-8 version 0.2.5g</p> <h2>1. What I set out to do</h2> <p>One of my main goals when making the game was learning PICO-8, so I decided to only use documented features while reading the PICO-8 Manual. This means no alternate palette, for example. I only used graphics functions in the most straightforward way possible (no 3D, lighting or other complicated techniques not directly supported by the PICO-8 graphics callbacks).</p> <p>I also decided right away to fit everything into a single cart: I liked the limitations imposed by PICO-8, and I feared going multicart would allow the scope to creeply get out of hand (as it tends to do), especially on my first project.</p> <p>On the other hand, I didn't want the game to feel like a vertical slice demo. I wanted it to feel somewhat complete, so I decided to cram as much levels into the cart as possible (no decompression or other advance techniques though, see the previous paragraphs). One of the earliest design decisions I made in <strong>Tiny Tactics</strong> was to devote all shared memory to the map.</p> <p>You can see below that i used 99.87% of the available tiles in the map (including shared memory).</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/tiny_tactics.map.png" alt="" /> <p>This left me with only two tabs for sprites. I also decided very early on that all enemies would use the player sprites, palette swapped. I experimented with different sprites for enemies, but the levels became more difficult to read so I abandoned the idea.</p> <p>Of course there are also sprites for drawing the map (including enemies starting postion), story elements etc.<br /> In the end I had just enough sprites to make a simple (yet pleasant) title screen and add a few &quot;easter eggs&quot; to the levels (dining rooms and offices, just to make the levels feel more lived-in).</p> <p>You can see below that I used all available sprites (first two tabs only).</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/tiny_tactics_sprites.png" alt="" /> <p>The UI was also very important from the start, as I feel this is part of what makes the game feel &quot;somewhat complete.&quot; I opted not to include transitions or other fancy UI animations, though. Instead, I focused on the quality of life settings the player can configure in the game's menus (customizable animations, controls, tips and so on).</p> <p>Also, I tried to &quot;do it only once&quot;. I didn't want to spend lots of time redesigning levels or reimplementing mechanincs. If it wasn't fun, I scrapped it ASAP. If it was fun, I finished it and moved on. This is my first project, it was not supposed to have a long development cycle.</p> <p>Finally, I quickly decided to use an external editor (Notepad++) to edit the code. The code section in tiny_tactics.p8 only contains 20 <code>#include</code> lines and a few comments, like the cart label ones.</p> <h2>2. What changed along the way</h2> <h3>2.1. Feel</h3> <p>The game started as a minimalist tactics game, but it ended up also feeling like a puzzle game. Which is fine by me, these are my two favorite game genres. What was set on stone from the very beginning was that I wanted the game to feel &quot;fair&quot;. The player and the enemies should be able to perform the same actions. Everyone should die in one hit. I wanted the player to feel he is overcoming all those enemies by being smarter than their simple AI, not because of some super powered armor or weapon.</p> <h3>2.2. Colors</h3> <p>The colors I used to palette swap the enemies changed a lot during development. I needed to have good contrast with the floor. The checkered white / light gray floor tile was set on stone very early on, but enemies' colors changed a lot along the way. When designing the last enemy types, the idea of using both hair and jacket colors to independently indicate different characteristics of their AI behaviour finally came to me.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/enemies.png" alt="" /> <p>Also, the idea of palette swapping wall colors to make each secret base feel different from the last one only came when designing the final bases. For most of the development process, the map was all in shades of gray. In the end of the development process, I realized this made progress very monotonous. I tried to choose wall colors that don't sacrifice level readability and also don't distract you from the enemies you should be focusing on.</p> <h3>2.3. Levels</h3> <p>When I was designing the first levels, the plan was to make all levels 15x13 tiles (which is the maximum level size, as some screen space is reserved for status bars on top and bottom and for the EXIT text when the door is unlocked).</p> <p>Each level would consist of multiple rooms and, when a player moved from one room to another, enemies would enter the room from color coded doors. I quickly realized this made the code a lot more complex than it should... And also severely limited level design, which was the worst drawback.</p> <p>I changed the approach, making each level be a single room with x, y, width and height coordinates stored in a table. Player and enemies are positioned when the level starts. This is done through color coded floor sprites (invisible by palette swapping these colors to white when drawing the map).</p> <p>This brought much more freedom for the level design - and was much simpler to code.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/map2.png" alt="" /> <h3>2.4. Menu</h3> <p>When I designed the first few levels I quickly realized the game felt a lot like a puzzle game. I remembered how much fun I had optimizing my solutions in <em>Sokoban</em> and <em>Hitman Go</em>, so I decided to add a turn counter and records very early on. Player records are persisted on cart data.</p> <p>On the other hand, I didn't want players to feel stuck so I included an option to unlock all levels (I also persisted that option on cart data). This helped a lot with development and testing.</p> <p>For a long part of development, the menu only had three options. </p> <ul> <li><strong>play</strong> - I wanted level names front and center, as I quickly found out they were important to make levels feel unique and keep them engaging. When you press <strong>play</strong> you only see level numbers, names and wether you've cleared them or not.</li> <li><strong>options</strong> - The only option at the time was to unlock all levels.</li> <li><strong>records</strong> - Where the player can compare their best score for each level with the <em>dev's record</em>. As the record chase is meant to be the endgame, I decided this would be the last option on the menu.</li> </ul> <h3>2.5. Controls</h3> <p>Using arrows to move and a button to shoot was obvious from the start, but what about the other button? Should it also be used? For what? In the beginning I thought about using it to let the player spin in place (without moving), but I had the intuition this would lead to less interesting, more bureaucratic puzzles - and more complex AI. </p> <p>I decided the second button would simply skip the turn for the player. This made the levels more claustrophobic - in a good way! Now the player has to consider if he has enough space to position himself so he can aim at enemies. This made for much more interesting puzzles.</p> <p>I know the community knows keyboard input mappings like the back of their hands, but I wanted to show the game to friends who never played a PICO-8 game, so I decided to ask the player if he is using a keyboard and change button / key labels accordingly. The game does this when it can't load cart data (first time the game is started) or at any time the player selects <strong>controls</strong> on the title screen.</p> <p>Based on the feedback I received, this was the right choice.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/29_1-controls.png" alt="" /> <h3>2.6. Tips</h3> <p>Tips started as random hints that appeared when levels were small enough, so i had some screen space to show them. In the end, though, it was best to leave them in the main menu, so they are easier to access. </p> <p>I like that you have the basic info you need to play available in the game's menus, even if you don't read anything in a blog post. I wrote a lot more tips, but had to trim them down to the absolutely necessary so I could fit everything in a single cart.</p> <p><img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/30_1-tips1.png" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/31_2-tips2.png" alt="" /></p> <h3>2.7. Customizable animations</h3> <p>When I added animations to the game and tested levels with more enemies (like level 1-9), the testing process quickly became tedious. I decided to do what a lot of RPGs do: give players an option to skip animations. Then I decided to break that into more detailed options, like letting players set the <em>animation speed</em> and skip <em>move</em> and <em>idle</em> animations.</p> <p>I decided to let animations in mid speed by default and trust the player to find these options by himself. Many didn't. I noticed many people didn't read the instructions or even checked to see what was available in the <strong>options</strong>. I suspected this could happen, but didn't want to interrupt the player with yet another text screen. I'm not sure it was the best decision though.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/32_1-options.png" alt="" /> <p>While testing with <em>move</em> / <em>idle</em> animations turned off, I noticed the game was letting only one enemy act in each frame. This made the game feel too slow when there were many enemies on a level. I changed the code to make sure the game lets more than one enemy act in a single frame when their animations are skipped.</p> <p>The game still isn't as responsive as I would like when <em>idle</em> and specially <em>move</em> animations are turned off. But I don't have the tokens to completely overhaul the animations, turns and controls. Besides, as I said before, this is my first project. &quot;Do it once and move on&quot; was my motto! In any case, the game still is pretty playable and enjoyable as it is, IMHO.</p> <h3>2.8. Pause menu</h3> <p>Restarting or quitting the level is possible through additional commands in the pause menu.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/33_2-pause.gif" alt="" /> <p>I didn't want to assume player would be familiar with the pause menu, though. That's why the button / key used to access the pause menu are displayed in the <strong>controls</strong> screen.</p> <p>That's also why I added the ability to turn off sound from the title screen, even though you can do the same in the pause menu. Redundant? Yes. But if I didn't know I could do that for the first few weeks of development, why assume players will know about it? Many players are friends of mine who never played a PICO-8 game before...</p> <h3>2.9. Story</h3> <p>While designing levels and testing them, I felt the game was feeling like a simple pack of levels. Level names did make each level more unique, but I still missed some kind of overarching narrative. I believed the game needed a story, even if it was as simple as the story in the first <em>Super Mario Bros.</em> game. So I made a simple story referencing some sentences from that game, but using a Mission: Impossible / 007 setting - which better fitted the gameplay.</p> <p>Did you catch those references while playing the game? I even hightlighted the word substitutions in different colors to make them as identifiable as possible.</p> <p><img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/base_cleared.png" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/mario.png" alt="" /></p> <p>I liked the end result, even if it actually consists of just a few screens with text and very simple graphics with a different perspective (side view instead of top-down). It's simple, but I find it charming.</p> <h3>2.10. Trophies and alternative skin</h3> <p>I wanted to give players an additional challenge, and I wanted to reward them for it. So I added my personal records to the level coordinates table as the <em>dev's record</em>. I also added a screen to track records... And trophies! If the player matches my record for a level, he gets a silver trophy. If he breaks my record, he gets a gold one.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/records-gold.png" alt="" /> <p><strong>Note</strong>: I won't update the <em>dev's records</em>, even if I break my own records after the game's release (it has already happened, in fact). That's what the <a href="https://github.com/andrejtrindade/tiny_tactics/tree/main/leaderboards">leaderboards</a> are for.</p> <p>I also added an alternative skin in the <strong>options</strong> if the player manages to get trophies for all levels (doesn't matter if they are silver or gold). It's been a week since I published the game on the BBS. As far as I know, nobody has unlocked the alternative skin yet... If you manage to do so, post a screenshot on the game's thread!</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/37_3-.png" alt="" /> <h3>2.11. Sound</h3> <p>I'll admit sound was an afterthough in this project. The game didn't have any sound for most of the development process. Adding sound effects was pretty straightforward, but composing music takes time. I decided to do a very simple theme for the menus, use looping sound effects for story screens and not have any music during levels (just sound effects). I used less than 25% of the available sound effects and music patterns, but that's OK.</p> <p>Maybe my next game will have more sophisticated sound, but the sound in <strong>Tiny Tactics</strong>, though simple, is serviceable and pleasent - at least to my ears. Also, it doesn't distract you from solving the puzzles, which is something I consider essential.</p> <h3>2.12. Tokens</h3> <p>I have used 99.86% of the available tokens and 99.55% of the compressed bytes limit.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/39_tokens.png" alt="" /> <p>I broke the token limit a few times during the last few weeks. I had to use some tricks to optimize my token count. Here are the two main ones...</p> <ul> <li>The coordinates for each stage were combined into a single integer. As there are 81 levels, this saved me more than 1,000 tokens. For example, for stage 5-8 we have <code>0x1216.892e</code>. Let me break it down (all numbers are in hex): <ul> <li>12 and 16 are the x and y map coordinates of the topmost, leftmost tile of the level. </li> <li>8 is the level width in tiles. </li> <li>9 is the level height. </li> <li>Finally, 2e is the <em>dev's record</em> for the level.</li> </ul></li> <li>I used split() to create most of my tables, as each string only consume a single token. For example, the main menu is created with <code>split(&quot;play,controls,options,tips,records&quot;)</code>. You can do the same with numbers, split() will convert them for you.</li> </ul> <p>Something I wish I could do is to define constants without wasting tokens. Constants in lua are just variables, and you spent tokens to define them. I still created a lot of them to make the code more readable, but in the final weeks I just didn't have the tokens to spare.</p> <p>Sound effects constants, for example, are commented, and sfx numbers are hardcoded in the lua files. Side view character sprites for the story are also hardcoded for the same reason. I'm glad I managed to avoid this in most other situations, though.</p> <p>I also simplified or removed a few redundant, verbose comments from the code to keep everything under the compressed bytes limit. The code is still very readable though, I believe.</p> <h3>2.13. Release</h3> <p>Before posting the cart on the Lexaloffle BBS, I uploaded the whole project to GitHub. I wanted anyone to have easy access to the bynary apps (Windows / Mac / Linux / Raspberry Pi), the standalone HTML version and the uncompressed source code, even if the person doesn't have PICO-8 to export everything.</p> <p>I also created a walkthrough by recording GIFs, showing my solution for every level. After some time, it may be difficult to recall how exactly you finished a level in X turns. I know this from experience...</p> <h3>2.14. Updates</h3> <p>I updated the game a couple of days after releasing it. This first change was very minor: I addeed the level number to the clear screen, because my friends were sending me screenshots of broken records - and I had to ask which level they were playing.</p> <p>Then, a couple of days later, I found a bug (suprised no one complained!) where you could walk through the locked exit door before you eliminated all the enemies... So I updated the game again to fix this.</p> <p>I also added a leaderboards folder to the GitHub project. It contains GIFs showing how to get the gold trophies I'm aware are achieavable. First person to claim the record gets the credit (once I verify the new record is actually possible, of course).</p> <p>This game has 9 enemy types and 9 secret bases with 9 levels each. Right now there are 9 gold trophies on the leaderboards. Funny coincidence, huh?</p> <h2>3. How did it go?</h2> <p>It's been a week since I published the game on the BBS.</p> <p>I'm very happy to see my friends and family playing the game, finishing bases and even breaking my records! Two of them have already finished the whole game, all 81 levels! </p> <p>It's also very nice that several forum members tried the game and liked it enough to give it a star or leave a comment.</p> <p>I remember when I was a kid, drawing super heroes on my notebook. I didn't want to make money out of it, put my drawings in an art gallery or anything. I just liked drawing... Whenever I was proud of a drawing and my friends thought it was cool, that was my definition of success. </p> <p>That's how I feel now, decades later, about <strong>Tiny Tactics</strong>. I had fun making it! I'm thrilled people are also having fun playing it! It's not perfect, but I'm proud of it.</p> <p>Anyway, I hope you liked this writeup! And, if you still haven't played <strong>Tiny Tactics</strong>... <a href="https://www.lexaloffle.com/bbs/?tid=55172">Go try it!</a></p> https://www.lexaloffle.com/bbs/?tid=55244 https://www.lexaloffle.com/bbs/?tid=55244 Fri, 08 Dec 2023 15:54:35 UTC tiny_tactics <p> <table><tr><td> <a href="/bbs/?pid=138037#p"> <img src="/bbs/thumbs/pico8_tiny_tactics-5.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=138037#p"> tiny_tactics</a><br><br> by <a href="/bbs/?uid=86005"> andrejtrindade</a> <br><br><br> <a href="/bbs/?pid=138037#p"> [Click to Play]</a> </td></tr></table> </p> <h1>Tiny Tactics</h1> <p>A turn based, tactics / puzzle game for PICO-8</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-playing.gif" alt="" /> <h2>Controls</h2> <ul> <li>When you start the game for the first time it will show you the controls and ask if you are using a keyboard. Your answer will be saved, and appropriate button / key labels will be used on the menus. </li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/1-controls.png" alt="" /> <ul> <li>Shorter levels (like the first few ones) will also show the controls at the bottom so you get used to them.</li> <li>If you want to change labels from keys to buttons or vice-versa later on, just select <strong>controls</strong> on the title screen.</li> </ul> <h2>Features</h2> <ul> <li>81 levels</li> <li>9 enemy types</li> <li>2 hours of gameplay (first playthrough estimate)</li> <li>Story with an actual ending</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-briefing.png" alt="" /> <ul> <li>Customizable animations</li> <li>Ability to unlock all levels if you get stuck</li> <li>Appropriate button / key labels wether you are using a keyboard or not</li> </ul> <p><img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-buttons.png" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/3-keys.png" alt="" /></p> <ul> <li>In-game tips detailing enemies AI behaviour</li> <li>Your settings and records are saved</li> <li>Get trophies if you match the <em>dev's records</em></li> <li>Unlockable skin if you get trophies for all levels</li> </ul> <h2>Playing Levels</h2> <ul> <li>Select <strong>play</strong> on the title screen.</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/1-bases.gif" alt="" /> <ul> <li>Clear a level to unlock the next one.</li> <li>You can quit or restart the current level from the <em>pause menu</em>.</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-pause.gif" alt="" /> <ul> <li>Notice the level name at the top, it may be a hint on how to solve it.</li> <li>Notice the turn counter and the turn indicator (<strong>player turn</strong> / <strong>enemies turn</strong>) at the bottom.</li> </ul> <h2>Options</h2> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/1-options.png" alt="" /> <ul> <li>You can adjust the <em>animation speed</em>.</li> <li>You can speed up the gameplay further by disabling <em>idle</em> and/or <em>move</em> animations.</li> <li>If you get stuck, you can <em>unlock all levels</em>.</li> <li>You can also change your <em>active skin</em> (once you unlock the <em>alternative skin</em>).</li> <li>Your settings are saved when you go back to the title screen.</li> <li>You can also <em>show FPS</em> (frames per second), which boringly stay at 30 at all times in my experience. This setting is intentionally not saved.</li> </ul> <h2>Tips</h2> <ul> <li>Select <strong>tips</strong> from the title screen for some in-game hints.</li> </ul> <p><img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/1-tips1.png" alt="" /> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-tips2.png" alt="" /></p> <ul> <li>Take your time to understand enemies AI behaviour, which is indicated by the color of their hair and jacket.</li> <li>Also note the order of activation during enemies turn.</li> </ul> <h2>Records</h2> <ul> <li>Your record is saved when you set a new personal record for a level.</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/1-record_saved.png" alt="" /> <ul> <li>Select <strong>records</strong> from the title screen to see the <em>dev's records</em> and compare them to yours.</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-records.png" alt="" /> <ul> <li>You earn a <em>silver trophy</em> for each level in which you tied the <em>dev's record</em>.</li> <li>It's possible that you are able to break the <em>dev's record</em> in some levels. In such case, you will earn a <em>gold trophy</em>.</li> <li>If you earn trophies for all levels you will unlock an <em>alternative skin</em>. It doesn't matter if your trophies are silver or gold.</li> <li>Once the <em>alternative skin</em> is unlocked, you can change your <em>active skin</em> in the <strong>options</strong>.</li> </ul> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/3-.png" alt="" /> <h2>Project</h2> <p>You can download <a href="https://github.com/andrejtrindade/tiny_tactics">the complete project</a> from GitHub, including:</p> <ul> <li>the separate .lua files before flattening;</li> <li>a <a href="https://github.com/andrejtrindade/tiny_tactics/tree/main/exports/html">standalone HTML version</a>;</li> <li><a href="https://github.com/andrejtrindade/tiny_tactics/tree/main/exports/binary%20apps">binary versions</a> for Windows / Mac / Linux / Raspberry Pi.</li> </ul> <h2>Walkthrough</h2> <p>I strongly suggest you try to tackle the levels in the game by yourself. If you get stuck, you can unlock all levels in the <strong>options</strong> and skip the level that's giving you trouble.</p> <p>The same can be said about records. You don't need to tie the <em>dev's record</em> for every level, unless you want to unlock the alternative skin. Even then, you don't need to do it straight away, you can always keep playing the next levels and come back for records later.</p> <p>But if you really want to see how the <em>dev's record</em> can be obtained for a specific level, I have recorded GIFs showing my solution for all levels in the game. The GIFs are on the GitHub repository, in the <a href="https://github.com/andrejtrindade/tiny_tactics/tree/main/walkthrough">walkthrough folder</a>.</p> <h2>Gold Trophies</h2> <p>On many levels, especially the simpler ones, the <em>dev's record</em> already seem to be the optimal solution, so don't expect to be able to get gold trophies on all levels.</p> <p>If you manage to get a gold trophy... Take a screenshot of the <em>level clear</em> screen! This screenshot will contain your entire solution so you (or anyone else) can replay it.</p> <img loading="lazy" style="margin-bottom:16px" border=0 src="/media/86005/2-7 solution.png" alt="" /> <p>Please post your screenshot on this forum. I'll appreciate it, and I'll add your record to the leaderboards (see below), giving you the credit for being the first to discover said solution (to the best of my knowledge, naturally).</p> <h2>Leaderboards</h2> <p>On the GitHub repository there is a <a href="https://github.com/andrejtrindade/tiny_tactics/tree/main/leaderboards">leaderboards folder</a> with GIFs showing how to get the gold trophies that I'm aware are possible. First person to claim the record gets the credit (once I can verify the record is really achievable, of course).</p> <h2>Development</h2> <p>I wrote an extensive <a href="https://www.lexaloffle.com/bbs/?tid=55244">postmortem</a>. Go read it!</p> <h2>Feedback</h2> <p>This is my first PICO-8 game. It took exactly 3 months of my spare time (5 to 10 hours / week) to make, from purchasing a license to exporting the final version of the cart. Any feedback is appreciated.</p> <p>I hope you have fun playing <strong>Tiny Tactics</strong>!</p> https://www.lexaloffle.com/bbs/?tid=55172 https://www.lexaloffle.com/bbs/?tid=55172 Thu, 30 Nov 2023 12:46:00 UTC