eeooty [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=73872 WYSIWYG control code editor <h1>WYSIWYG control code editor</h1> <p> <table><tr><td> <a href="/bbs/?pid=128103#p"> <img src="/bbs/thumbs/pico8_wysiwyg-1.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=128103#p"> wysiwyg</a><br><br> by <a href="/bbs/?uid=73872"> eeooty</a> <br><br><br> <a href="/bbs/?pid=128103#p"> [Click to Play]</a> </td></tr></table> </p> <p>Hey there!</p> <p>Tired of manually writing that long, one-line print statement, filled with control codes in order to layout static text like in a title screen, painstakingly modifying <code>\^j</code> and <code>\+</code> values to position the text just right, adjusting <code>\^x</code> and <code>\^y</code> values of solid-filled text to draw background elements instead of using <code>rectfill</code> to save precious tokens, and then deciphering all of that when you need to go back and edit something? ...no? just me?</p> <p>Did you know that with <a href="https://www.lexaloffle.com/dl/docs/pico-8_manual.html#Appendix_A">P8SCII control codes</a>, you can change things like text color, size, screen position and more? Meaning, for <strong>static</strong> (and relatively positioned &ndash; <code>camera</code> offsets will apply) text, you never really need more than 1 argument to <code>print</code>, and no more than one <code>print</code> per layout!</p> <p>For example, <code>print(&quot;hello&quot;,8,8,5); print(&quot;world&quot;,16,16)</code> can be written as <code>?&quot;\^j22\f5hello\^j44world&quot;</code>, saving tokens and characters.</p> <p>However, there's a lot to remember with P8SCII, several potential snags, and it can get pretty unwieldy pretty quickly. That's why I made this <a href="https://en.wikipedia.org/wiki/WYSIWYG">WYSIWYG</a> tool!</p> <h2>tool usage</h2> <p>To load in immediate mode, run <code>load #wysiwyg</code>, or run the cart above.</p> <p>Hopefully the UI is fairly intuitive. See a demo below:</p> <img style="margin-bottom:16px" border=0 src="/media/73872/21_new_2.gif" alt="" /> <p><strong>Basic usage:</strong></p> <ul> <li>Click the plus in the layers tab to add a new text box</li> <li>With a layer selected (by default new layers are selected, but you can also select by clicking on it in the preview or in the layers tab): <ul> <li>click the edit icon (pencil icon) to edit it</li> <li>use arrow keys (when not in the editing tab) or drag it with your mouse to move it</li> </ul></li> <li>Press the tab key to show/hide the sidebar</li> <li>Scroll through the layers list</li> <li>Click on a selected background color again to set it to none</li> <li>Before clicking &quot;load from clipboard&quot;, paste (ctrl-v)</li> </ul> <h3>Note that there is no undo, so be careful and save (copy, then paste somewhere) often!</h3> <h2>features</h2> <p>Everything on the right half of the screens below was done using this editor, meaning that printing each layout is just 2 tokens (although quite a few characters)! The demo print commands are given below, which you can load into the editor to inspect the different layers and see how some of the effects were done.</p> <img style="margin-bottom:16px" border=0 src="/media/73872/25_out2.png" alt="" /> <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>--effects ?&quot;⁶jf4⁵ih⁶hᶜ2&sup2;2⁶xo⁶ye \n ⁶jg4⁴j⁶h&sup2;1⁶xm⁶yc \n ⁶jf4⁵ji⁶h⁶xn⁶yd \n ⁶jg5&sup3;h⁶h&sup2;2⁶xl⁶yb \n ⁶jg8⁵ijᶜa⁶x4⁶y6 2 tokens ⁶jl7&sup3;i only⁶jg7&sup3;iᶜ7⁶-#text:⁶jg5⁵ihany static⁶jff⁵iiᶜ0heavy outline⁶jff⁵ihheavy outline⁶jff&sup3;iheavy outline⁶jff&sup3;hheavy outline⁶jffheavy outline⁶jff⁴hheavy outline⁶jff⁴iheavy outline⁶jff⁵hiheavy outline⁶jff⁵hhᶜ7heavy outline⁶jfh⁵hiᶜ0light outline⁶jfh⁵ijlight outline⁶jfh⁴jlight outline⁶jfi&sup3;hlight outline⁶jfh⁵hjᶜ7light outline⁶jfk⁵jiᶜ0basic shadow⁶jfk⁵jhᶜ7basic shadow⁶jem⁵ijᶜ9multi-color (y)⁶jem⁵ijᶜa⁶y2multi-color (y)⁶jep⁵ihᶜ1&sup2;d⁶y6multi-color (x)⁶jep⁵ihᶜ2⁶-#⁶x2m u l t i - c o l o r ( x )⁶jgsᶜ7&sup2;9⁶y8 ⁶jfs⁵jh⁶x4⁶y6 containers \0&quot; --wysiwyg logo ?&quot;⁶jif⁵ijᶜ1editor⁶jif⁵iiᶜdeditor⁶jhd⁵ijᶜ1ctrlcode⁶jhd⁵iiᶜdctrlcode⁶jia⁶tᶜ0wysiwyg\n⁶jia⁶=ᶜ8wysiwyg\n⁶jiaᶜ9⁶y4wysiwyg\n⁶jiaᶜa⁶y3wysiwyg\n⁶jiaᶜb⁶y2wysiwyg\n⁶jiaᶜc⁶y1wysiwyg\n\0&quot; --age of ants title screen ?&quot;⁶jd7⁴i⁶w⁶tᶜ0age ants\n⁶jf6⁴j⁶-w⁶-t⁶y7. ⁶x3 .⁶x2 .⁶jt8⁵ji⁶x4⁶y6.⁶jc7⁵jh⁶w⁶tᶜ7age ants\n⁶jt8⁵ih⁶-w⁶-t.⁶je6⁵ji⁶y7. ⁶x3 .⁶x2 .⁶jj8&sup3;jᶜ0⁶x4⁶y6⁶:0060123515120800⁶jj7⁵jjᶜ7⁶:0060123515120800⁶jm6⁵ihᶜ5⁶x2.⁶x3 ⁶x2.⁶jc6⁵ih.⁶x3 ⁶x2.⁶jgc⁵jjᶜ0⁶x4difficulty:⁶jgc⁵jiᶜcdifficulty:⁶jdj⁵hjᶜ0press ❎ to start⁶jdj⁵hiᶜ9press ❎ to start⁶jqt⁵ihᶜ0v1.7⁶jqt&sup3;iᶜ6v1.7⁶jct&sup3;iᶜ0eeooty⁶jcs⁵ijᶜ6eeooty⁶jdm⁵hjᶜ0pause for options⁶jdm⁵hiᶜapause for options⁶jkf⁴hᶜ6\0&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><strong>Support for:</strong></p> <ul> <li>Various control code effects from the UI (tall, wide, stripey, inverted, border, x-width, y-height, fg color, optional bg color)</li> <li>Text printed near the bottom of the screen (adds <code>\0</code> to the end of the string so the console doesn't scroll)</li> <li>Multi-line textboxes <ul> <li>Maintains the first line's x-position for subsequent new lines using the <a href="https://www.lexaloffle.com/dl/docs/pico-8_manual.html#Special_Commands">cursor home control code</a></li> <li>Accounts for <a href="https://www.lexaloffle.com/bbs/?pid=128117#p">this tall mode quirk</a></li> <li>Newlines show up as ■ in the editor input</li> </ul></li> <li>As much character efficiency as sanely possible (will carry over as many effects as possible from textbox to textbox instead of resetting them each time)</li> <li>Easy 𝘱𝘶𝘯𝘺𝘧𝘰𝘯𝘵 insertion <ul> <li>Note: pasting saved output containing punyfont into a pico-8 code editor won't retain punyfont, you will have to paste it into the p8 file from an external editor</li> </ul></li> <li>Saving (to a 2-token print command) and loading via clipboard</li> <li>Inline custom control codes via an &quot;insert&quot; menu <ul> <li>Useful for one-off effects on characters within a textbox, e.g. the gif above shows using wide mode for just one character, but can also be useful to make a space a littler bigger or smaller using x-width</li> <li><strong>Warning:</strong> This is an advanced feature! Subsequent text boxes may break if you don't &quot;undo&quot; the one-off effect (or not, test it yourself!)</li> <li><strong>Warning:</strong> Constructing invalid control code sequences (even as you edit) can have unexpected results - save before trying stuff! </li> </ul></li> </ul> https://www.lexaloffle.com/bbs/?tid=52315 https://www.lexaloffle.com/bbs/?tid=52315 Tue, 04 Apr 2023 23:26:08 UTC Tall control code permanently changes line height <p>Hi! Think I ran into a bug when using the tall mode control code as part of a longer string (that then undoes it).</p> <p>Seems like tall mode retains the doubled line height even after it's turned off in the same string. The following code produces this output:</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>cls() ?&quot;\n ⁶t hi \n\n ⁶-t l1 \n l2 \n&quot;</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <img style="margin-bottom:16px" border=0 src="/media/73872/new_3.png" alt="" /> <p>...when I'd expect it to produce this (<code>&quot;l1 \n l2&quot;</code> is no longer in tall mode, so the height of the line break shouldn't be doubled):</p> <img style="margin-bottom:16px" border=0 src="/media/73872/new_2.png" alt="" /> <p>It seems like the only way to revert to normal line height is to start a new print command, but that's another 2 tokens! This is affecting a WYSIWYG-style static text editor I'm planning on releasing.</p> https://www.lexaloffle.com/bbs/?tid=52314 https://www.lexaloffle.com/bbs/?tid=52314 Tue, 04 Apr 2023 02:33:42 UTC Age of Ants <h1>Age of Ants</h1> <img style="margin-bottom:16px" border=0 src="/media/73872/rts_21.gif" alt="" /> <p><em>send your worker ants to gather resources&hellip;<br /> amass an army to defend your queen&hellip;<br /> build your ant empire and conquer the lawn!</em></p> <p><strong>Age of Ants</strong> is a demake of <strong>Age of Empires II</strong> (with an ant/bug retheme, why not), featuring:</p> <ul> <li>1 map with 4 possible starting locations</li> <li>Up to 2 allied AI opponents with 3 difficulty modes</li> <li>9 units, 8 buildings, 12 tech upgrades (most are repeatable)</li> <li>Up to 99 active units per player</li> <li>AoE2 controls &amp; behaviors (let me know if something seems off, it's been a while)</li> <li>Savefiles&sup1; (save in the pause menu to generate a screenshot, drag and drop to load)</li> <li>Original soundtrack</li> <li>wololoooo</li> </ul> <p>Defeat the enemy queen to win!</p> <p>It's playable on:</p> <ul> <li><strong>desktop with mouse</strong> (best experience, download <a href="https://eeooty.itch.io/age-of-ants#download">here</a>)</li> <li><strong>mobile/tablet</strong> (on bbs the page might scroll around - for a better experience try playing on <a href="https://eeooty.itch.io/age-of-ants">itch</a>)</li> <li><strong>handheld console</strong></li> </ul> <p>When the game first launches, open the pause menu and select the appropriate controls mode.</p> <p>&sup1;there are some caveats with savefiles, see the note on <a href="https://eeooty.itch.io/age-of-ants">itch</a></p> <h2>controls</h2> <p>I tried to stay as faithful to AoE2 controls as possible, but if you've never played or you need a refresher, check out the gifs below. If you prefer, there are also video versions embedded on <a href="https://eeooty.itch.io/age-of-ants">itch</a> (that way you can pause etc).</p> <h3>desktop tutorial (gif)</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <img style="margin-bottom:16px" border=0 src="/media/73872/desktop.gif" alt="" /> <p></div></div></div></p> <h3>mobile tutorial (gif)</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"><br /> <strong>Note:</strong> Playing on mobile from BBS is a little broken, the page might scroll around as you drag onscreen. For a better experience try playing on <a href="https://eeooty.itch.io/age-of-ants">itch</a>.</p> <img style="margin-bottom:16px" border=0 src="/media/73872/9_mobile.gif" alt="" /> <p></div></div></div></p> <h3>handheld tutorial (gif)</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <img style="margin-bottom:16px" border=0 src="/media/73872/console.gif" alt="" /> <p></div></div></div></p> <h3>advanced controls</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"><br /> The following controls try to copy some of AOE's quality-of-life shortcuts:</p> <ul> <li>Double-click on a unit to select all visible units of that type</li> <li>Left-click (or x) on a unit's portrait to deselect it</li> <li>Right-click (or o) on a unit's portrait to select <strong>just</strong> it</li> <li>Right-click when placing a building to build another one of the same type (replaces shift)</li> <li>Workers automatically know how to allocate themselves to farms (e.g. with 5 workers selected, you can build 5 farms and they will all farm a different one)<br /> </div></div></div></li> </ul> <h2>tech tree</h2> <img style="margin-bottom:16px" border=0 src="/media/73872/tech.png" alt="" /> <h2>credits</h2> <p>thank you to: </p> <ul> <li><a href="https://www.lexaloffle.com/bbs/?uid=22734"> @morgan3d</a>'s <a href="https://github.com/morgan3d/misc/tree/master/p8pathfinder">p8pathfinder</a> (modified for caching and approximating unreachable paths)</li> <li><a href="https://www.lexaloffle.com/bbs/?uid=12772"> @musurca</a>'s <a href="https://www.lexaloffle.com/bbs/?pid=90968#p">fast dist()</a></li> <li><a href="https://www.lexaloffle.com/bbs/?uid=11292"> @Gruber</a>'s <a href="https://www.lexaloffle.com/bbs/?pid=64837">&quot;explosion 18&quot;</a> sound effect</li> <li><a href="https://www.lexaloffle.com/bbs/?uid=23375"> @carlc27843</a>'s <a href="https://carlc27843.itch.io/pico-8-source-compression-visualizer">pxaviz</a> for compression hints</li> <li><a href="https://www.lexaloffle.com/bbs/?uid=29645"> @thisismypassword</a>'s <a href="https://github.com/thisismypassport/shrinko8">shrinko8</a> for code compression (mostly whitespace removal)</li> <li><a href="https://www.lexaloffle.com/bbs/?uid=49909"> @Wolfe3D</a> for the idea of a screenshot-based savefile</li> <li>siege engineers' <a href="https://aoe2techtree.net/">aoe2techtree.net</a> for some baseline unit stats</li> <li>the p8 community!</li> </ul> <p><a href="https://www.lexaloffle.com/bbs/files/73872/rts.p8">here</a>'s the uncompressed and highly commented sourcecode if anyone is curious. lots of token hacks and pretty tersely named variables, so hopefully the comments help demystify what's happening (i was originally attempting trying not to use shrinko but i caved once i added in helper text)</p> <p>itch link for downloads and stuff: <a href="https://eeooty.itch.io/age-of-ants"><a href="https://eeooty.itch.io/age-of-ants">https://eeooty.itch.io/age-of-ants</a></a></p> <p> <table><tr><td> <a href="/bbs/?pid=125247#p"> <img src="/bbs/thumbs/pico8_age_of_ants-7.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=125247#p"> Age of Ants</a><br><br> by <a href="/bbs/?uid=73872"> eeooty</a> <br><br><br> <a href="/bbs/?pid=125247#p"> [Click to Play]</a> </td></tr></table> </p> <p>have fun! if you'd like, share savefiles of your games (they will still work even on the win/lose screen)</p> <h3>changelog</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"><br /> <strong>v1.7 (2023-03-24)</strong></p> <ul> <li>fix bug where units will stop short or not attack buildings behind fog of war</li> <li>fix bug where game would crash shortly after loading game</li> <li>fix bug where an enemy castle or tower could get undiscovered if it attacks a ladybug</li> <li>fix fog borders appearing on far right edge of map even when those tiles are explored</li> <li>enemy units attacking you from fog of war now get &quot;discovered&quot; (same behavior that buildings already had)</li> <li>if you lose visibility of an enemy while chasing them, attacker will now continue to its last known location</li> </ul> <p><strong>v1.6 (2023-03-14)</strong></p> <ul> <li>fix bug where AI attackers could stall in the middle of the map</li> <li>fix bug where AI sometimes didn't repair buildings</li> <li>fix bug where a worker ant could drop resources off at an opponent mound (now it goes back to queen)</li> <li>fix possible out of memory crash when there are too many active units</li> <li>fix bug where mounds could take an extra long time to register as drop-offs</li> <li>added sfx to difficulty selection in title screen</li> </ul> <p><strong>v1.5 (2023-03-11)</strong></p> <ul> <li>fix bug where HP would be very messed up (much lower) when loading a saved game</li> <li>fix bug where unit spreading out wouldn't work on the right half of the map</li> <li>fix bug where you could cancel an enemy building construction</li> <li>fix bug where ladybug dying on an already-exhausted resource tile would produce a tile with infinite food</li> <li>better army composition in endgame for hard ai</li> <li>adjust damage multiplier table slightly</li> <li>allow loading games on mobile via file upload button (<a href="https://eeooty.itch.io/age-of-ants">itch.io</a> only)</li> <li>improve title screen font slightly</li> </ul> <p><strong>v1.4 (2023-02-28)</strong></p> <ul> <li>fix possible infinite loop when a large amount of units were near a building</li> <li>fix bug where archers could walk through buildings when attacking</li> <li>fix bug where if the game starts with the mouse was over where the minimap will be, the camera starts there</li> <li>update &quot;leaf&quot; sprite (green resource) to have more contrast and more closely match the color of green resource value</li> </ul> <p><strong>v1.3 (2023-02-24)</strong></p> <ul> <li>new unit: <strong>spider web</strong>. a light wall built by spiders that can only be crossed by your own spiders</li> <li>new: right-click when placing a building to build another one</li> <li>new: small grass tiles now contain a small amount of plant (green) resource (should reduce confusion)</li> <li>fixed: groups of overlapping units could go through buildings when spreading out (hopefully new algorithm for this is not too buggy/slow!)</li> <li>fixed: when ladybug died, it could drop its food tile not at the closest available spot to it</li> <li>fixed: tile directly above queen's right half was inaccessible</li> <li>fixed: when easy AI started in the bottom-left corner it never built anything </li> </ul> <p><strong>v1.2 (2023-02-07)</strong></p> <ul> <li><em>regeneration</em> tech upgrade now in mantis nest instead of castle</li> <li><em>farm upgrade</em> is now slightly cheaper</li> <li>show victory if enemy queen is converted</li> <li>make &quot;move&quot; (not &quot;attack-move&quot;) the default action on mobile, remove support for attack-move on mobile</li> <li>fixed bug where workers in a clump could move far from a resource and continue gathering</li> <li>fixed bug where population count wouldn't transfer properly after a unit is converted</li> </ul> <p><strong>v1.1 (2023-02-05)</strong></p> <ul> <li>fixed bug where gathering workers would drop resources off all the way back to the queen unnecessarily</li> <li>fixed bug where the bottom-right idle worker button would sometimes not detect idle workers (specifically if they depleted all nearby resources of a certain type)</li> <li>fixed bug where double-clicking on a unit that overlapped with a building (e.g. farm) would not select all visible units of that type</li> <li>fixed bug where worker ants would overlap when gathering resources</li> <li>improved late-game AI resource allocation, plus normal and hard AIs now research fireball</li> <li>updated map a little bit with a new mini pond tile<br /> </div></div></div></li> </ul> https://www.lexaloffle.com/bbs/?tid=51464 https://www.lexaloffle.com/bbs/?tid=51464 Fri, 03 Feb 2023 20:58:08 UTC saving tokens <p>hello!</p> <p>sorry if this is &quot;yet another&quot; boring token-saving post (haven't been around long enough to know how common they are), but here's a list of (fairly) generalizable token saving tricks i've compiled while working on my upcoming game, in case it might be helpful to anyone. i <em>think</em> there are some on here that i haven't seen elsewhere on bbs, but also some of these may be pretty basic :)</p> <p>this list does not include <code>unpack(split())</code> tricks, or <code>_ENV</code> tricks, which can both have enormous token windfalls.</p> <p>also, by far the best way to save tokens is not using these tricks, but to stare at your code hard... really hard... until something clicks and you realize you can convey 3 variables' worth of information with one, that you can reuse functions, that you're doing the same computation throughout your code many times and can use a variable, etc. or you know, to delete stuff.</p> <p>these are kind of last-resort, please god just 3 more tokens sorts of things (although they do add up). hope they are of some use. with that aside, let's begin!</p> <h3>2 tokens: flr to \</h3> <p>this one is basic but it took me a while to learn about <code>\</code>. <code>flr(a/b)</code> can be written as <code>a\b</code>, saving 2 tokens. if flooring the result of a multiplication, assuming that one side is a constant, you can just invert: <code>flr(a*2.5)</code> to <code>a\0.4</code></p> <h3>2 tokens: ceil to \ !?</h3> <p>this one is situational, but interestingly floor &quot;decreases&quot; a number even if it's negative, making it kind of function a bit like <code>ceil</code> in the opposite direction. the token save here only works if you can already negate the expression for free:</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>a+=ceil(p*2) --to: (note += changes to -=) a-=p\-.5</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>of course you can use order of ops to fix any precedence issues this might cause (this time moving the negation to a constant in case there's no <code>+=</code>/<code>-=</code>):</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>a=2*ceil(p*2) --to: a=p\-.5*-2</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: calling apis with string params</h3> <p>since lua lets you call functions without parens if the only argument is a string/table literal, you can save a token on many standard api calls, e.g. <code>sfx(5)</code> to <code>sfx&quot;5&quot;</code>, <code>stat&quot;&quot;</code>, <code>rnd&quot;&quot;</code>, <code>btnp&quot;&quot;</code>, <code>btn&quot;&quot;</code>, <code>dget&quot;&quot;</code>, <code>fillp&quot;&quot;</code> (for this one, see the decimal value for your pattern in the console e.g. <code>fillp&quot;23130.5&quot;</code> for <code>fillp(▒)</code>), or even <code>poke&quot;&quot;</code> if you want to set that value to 0.</p> <p>this will also work on your own functions too! if you call functions with a single truthy value, you can do <code>fn&quot;1&quot;</code> instead of <code>fn(true)</code>, you can even do this if you call a function with a single numeric argument, but keep in mind this won't work if you do any comparisons with that argument. <code>&quot;3&quot;+3</code> is allowed in lua, but not <code>&quot;3&quot;&gt;3</code>!</p> <h3>1 token: foreach</h3> <p><code>foreach(a, function(x) end)</code> saves a token over <code>for x in all(a) do</code>. note that this means you can't <code>break</code> or <code>return</code> early, and it does come at the cost of performance (since you are introducing the overhead of function calls). it's not worth the token for nested loops / huge arrays!</p> <h3>1 token: next, inext</h3> <p>easy one:</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>for k,v in pairs(tbl) do end --to: --unordered for k,v in next,tbl do end --ordered but only monotonically increasing past 1 are guaranteed: for k,v in inext,tbl do end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: use return value of add/del/deli</h3> <p>if you can guarantee that the item being added/removed is present, you can replace a reference with the function:</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>add(a,b) b.x=y --to: add(a,b).x=y</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: hacking 'if' - add/del/deli</h3> <p>if you call <code>add</code>, <code>del</code>, or <code>deli</code>, the call will only &quot;go through&quot; if the first argument (the list) is a list. if it's falsey, it's a no-op. so you can save a token with:</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>if (x) add(arr, i) --to: add(x and arr,i)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>note also that these functions are <code>nil</code>-forgiving for the second argument, too - <code>add(arr,nil)</code> does nothing, so you don't need <code>if(x) add(arr,x)</code></p> <h3>1 token: hacking 'if' - camera/pal</h3> <p>if you want to call <code>camera</code> with <code>x</code> under condition <code>c</code>, save a token with:</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>if (c) camera(x) --to: camera(c and x)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>note that this resets the camera if not <code>c</code> (which is actually sometimes what you want!)</p> <p>the same applies to pal:</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>pal(c and unspl&quot;5,6,7&quot;)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: hacking 'if' - function call next to assignment</h3> <p>if you want to call any function under a condition, if there is assignment (or a function call that can take an extra argument) nearby, you can remove the if and save a token:</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>if (x) f() k=v --to: k=v,x and f()</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>note that in this case the order matters! if it was the other way around:</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>k=v if (x) f()</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>you can't make the same conversion if <code>f()</code> relies on the new value of <code>k</code>.</p> <p><strong>function call next to fully-loaded function call</strong></p> <p>note that this same trick works if you are calling another function nearby with all of its parameters passed:</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>if (x) f() pset(50,50,9) --to: --pset doesn't take a 4th argument, but who cares! pset(50,50,9,x and f())</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>same caveat applies here with ordering - <code>f()</code> will be called before <code>pset()</code></p> <p>essentially we are forcing lua to have a floating expression without an assignment.</p> <p><strong><code>not</code> as <code>or</code></strong></p> <p>in all of the 'hacking if' cases above, if your condition <code>x</code> is of the form <code>not y</code>, you can usually do <code>or y</code> instead of <code>and not y</code>!</p> <h3>1-3 tokens: hacking 'if' - function calls in ternary</h3> <p>finally, this is kind of specific, but let's say you have an expression that should go one way under one condition, and another way otherwise. but let's also say that in one of those cases, you also need to make a function call.</p> <p>if the call returns truthy, it can be prefixed with <code>and</code> to the value associated with the condition under which you want it to run:</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>---- pset returns truthy, so prefix with `and` ---- -3 tokens (-2 if on &quot;truthy&quot; side of ternary) a=c and x or y if(not c) pset(3,3,5) --to: a=c and x or pset(3,3,5) and y</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>if the call returns falsey, it can be prefixed with <code>or</code>, but you'll have to add parens:</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>---- sfx returns falsey, so prefix with `or` and add parens ---- -1 token (-2 if on &quot;falsey&quot; side of ternary) func(c and x or y) if(c) sfx&quot;3&quot; --to: func(c and (sfx&quot;3&quot; or x) or y)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: max to coerce to num</h3> <p>you can use <code>max()</code> to coerce a potentially non-number value into 0: <code>max(nil)=&gt;0; max(false)=&gt;0; max(5)=&gt;5</code>. this saves a token over <code>x or 0</code> because if you are doing math you will pay an extra token for the parens:</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>5+(x or 0) --to 5+max(x)</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>it does have limits: be aware that <code>max(true)=&gt;0</code>, and <code>max(&quot;5&quot;)=&gt;5</code></p> <h3>2 tokens per argument: local functions</h3> <p>if a function is only used by one other function, you can define it inside the other (locally) and remove any arguments already in scope:</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 g(a,x) ... end function f() local a,b,c=... g(a,b) g(a,c) end --to: function f() local a,b,c=... local function g(x) ... end g(b) g(c) end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: table length checks</h3> <p>you can replace the expression <code>#tbl&gt;x</code> with <code>tbl[x+1]</code> (which saves a token if <code>x</code> is a literal), or the expression <code>#tbl&gt;=x</code> with <code>tbl[x]</code> (which saves a token):</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>if (#tbl&gt;0) do_x() if (#tbl&gt;=threshold) do_y() --to: if (tbl[1]) do_x() if (tbl[threshold]) do_y()</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p><strong>note: this trick won't work if the table can contain <code>false</code>!</strong></p> <h3>??? tokens: practical bit ops</h3> <p>sometimes bit ops can be useful to save a bunch of tokens. pardon the somewhat specific examples, hopefully these get you thinking:</p> <p><code>x!=0 and y!=0</code> to <code>x&amp;y!=0</code> (-2 tokens)<br /> <code>x==0 and y==0</code> to <code>x|y==0</code> (-2 tokens)</p> <p>(these also have the advantage of not using a boolean operator, so if you invert one of these (e.g. <code>x|y!=0</code> instead of <code>x!=0 or y!=0</code>), you won't need parens if you <code>and</code> it with something)</p> <p><code>x^^=0xf0</code>: if x starts at 0, this toggles between 0 and a high number. if passed into e.g. <code>camera(x)</code>, can be used to toggle showing/hiding something</p> <p><code>0!=~0</code>, so if you want to toggle something, say a controls option that you want to store in cartdata, you can do it pretty tersely with the binary not operator <code>~</code>:</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>menuitem(1,&quot;toggle foo&quot;,function() dset(0,~dget&quot;0&quot;) end) ... if dget&quot;1&quot;==0 then --default, since cartdata defaults to 0 else --toggled end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: pal color 1</h3> <p>another highly specific one, but if you're using <code>pal</code> on color 1, save a token with <code>pal(1,x)</code> to <code>pal{x}</code></p> <h3>1 token: mid for range-checking</h3> <p><code>mid()</code> can sometimes save a token when doing a range check of the form <code>x &gt;= 0 and x &lt; y</code> (or negated):</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>x&gt;=0 and x&lt;8192 --to: mid(x,8191)==x</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>one extra edge case with this not involving 0 that i thought was cool:</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>dt&lt;.1 or dt&gt;.25 --to: mid(dt,.1,.25)!=dt --wait it's the same number of tokens... --but the first one has an `or`, so if it needs --to be combined with `and`, it'll need parens!</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>(context here is flashing a selection once, <code>dt</code> is the time since the selection)</p> <h3>2 tokens: do you really need cls()?</h3> <p>it's common to add <code>cls()</code> when starting a new project, but if you fill the whole screen each frame with e.g. <code>map()</code> you might not need it!</p> <h3>??? tokens: control codes</h3> <p>a LOT can be done with control codes, this is a large topic. but in general, i've found that if you are printing to the screen you generally don't ever need to pass (constant) args to print (that is, <code>?</code>) since you can move the cursor with <code>\^j</code> and <code>\^+</code>, and set color with <code>\f</code>.</p> <p>moreover, if you are printing a bunch of hard-coded text, e.g. a title screen, you generally only need a single call to print (<code>?</code>) since you can jump around on the screen so easily. need a shadow? first print your text in a dark color, then use <code>\^j</code> or <code>\+</code> to jump backwards and slightly up and re-print your text in the foreground color.</p> <p>i've spent so much time tweaking horrible controlcode-ridden strings, i kind of want to write a utility cart that is a kind of WYSIWYG editor for text that will spit out a single horrible string to print. <strong>EDIT: <a href="https://www.lexaloffle.com/bbs/?tid=52315">i've done this!</a></strong></p> <h3>1 token: return value of ?</h3> <p>this one is a little hacky, but since <code>?</code> is an alias for print that can be called without parens, you can still capture its return value if you need to get the max x-drawn. just make sure you add a newline after the last argument to <code>?</code>:</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>-- line x1 will be max_x-3, the -3 has to be on the next line line( ?text_of_unknown_length -3,80,8,80,9) x+=?text_of_unknown_length</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <h3>1 token: .. operator with numbers</h3> <p>the <code>..</code> string concat operator can be weird with numbers. if it's placed directly after a number, the parser will complain, expecting a fractional component following the first <code>.</code>. but if you add a space...:</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>?&quot;minutes: &quot;..t\60..&quot; seconds: &quot;..t%60 --malformed number near '60..'! --i thought this meant that you needed parens: ?&quot;minutes: &quot;..(t\60)..&quot; seconds: &quot;..t%60 --but you can save that token if you just add a space! ?&quot;minutes: &quot;..t\60 ..&quot; seconds: &quot;..t%60</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> https://www.lexaloffle.com/bbs/?tid=51445 https://www.lexaloffle.com/bbs/?tid=51445 Wed, 01 Feb 2023 21:50:35 UTC