NuSan [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=11048 Path of Aratron <p> <table><tr><td> <a href="/bbs/?pid=146737#p"> <img src="/bbs/thumbs/pico8_pathofaratron-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=146737#p"> pathofaratron</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=146737#p"> [Click to Play]</a> </td></tr></table> <br /> The game is also available on <a href="https://nusan.itch.io/path-of-aratron">nusan.itch.io/path-of-aratron</a></p> <p>In Path of Aratron, you can send a little demon to visit Dreams. You can control the demon by placing stones on Glyphs. </p> <p>Each stone is part of a path and will trigger one after the other once you start the summoning ritual.</p> <p>Solve 25 puzzles of increasing difficulty, you will need to carefully plan ahead and reuse part of your stones, as you don't have an infinite number of them.</p> <p>This is a programming game inspired by the game Lightbot, it has been made in 72h for Ludum Dare 55<br /> <img style="margin-bottom:16px" border=0 src="/media/11048/ld55_03_3.gif" alt="" /> <img style="margin-bottom:16px" border=0 src="/media/11048/ld55_03_5.gif" alt="" /><br /> Controls:</p> <ul> <li> <p>Mouse (recommanded)</p> <ul> <li> <p>maintain left click on a stone to drag it</p> </li> <li> <p>right click on a stone to delete it</p> </li> <li>left click on a path to insert an intermediary stone</li> </ul> </li> <li> <p>The keyboard can emulate the mouse (key c for left click and v for right click)</p> </li> <li>Gamepad also works</li> </ul> <img style="margin-bottom:16px" border=0 src="/media/11048/ld55_03_4.gif" alt="" /> https://www.lexaloffle.com/bbs/?tid=141759 https://www.lexaloffle.com/bbs/?tid=141759 Tue, 16 Apr 2024 08:40:53 UTC Inspect parameters and code of all picotron's lua functions <p> <table><tr><td> <a href="/bbs/?pid=143479#p"> <img src="/bbs/thumbs/pico64_function_tool-2.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=143479#p"> function_tool</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=143479#p"> [Click to Play]</a> </td></tr></table> <br /> I made a useful tool to look at what parameters are expected from picotron's api<br /> You can also see in what file it is implemented and inspect the code!</p> <p>It only works for functions that are implemented in lua, when it's done in c it's not visible, and some lua functions don't have much code in them and redirect a variable amount of parameters toward a c function</p> <img style="margin-bottom:16px" border=0 src="/media/11048/functions_tool.png" alt="" /> <p>I also exported the list of functions with their parameters if I could find them:<br /> <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> <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>table USERDATA C USERDATA:__add C USERDATA:__band C USERDATA:__bor C USERDATA:__bxor C USERDATA:__div C USERDATA:__index C USERDATA:__len C USERDATA:__mod C USERDATA:__mul C USERDATA:__newindex C USERDATA:__sub C USERDATA:__tostring C USERDATA:add C USERDATA:attribs C USERDATA:band C USERDATA:bor C USERDATA:bxor C USERDATA:clear C USERDATA:convert C USERDATA:copy C USERDATA:cross C USERDATA:distance C USERDATA:div C USERDATA:dot C USERDATA:get C USERDATA:height C USERDATA:magnitude C USERDATA:matmul C USERDATA:matmul2d C USERDATA:matmul3d C USERDATA:mod C USERDATA:mul C USERDATA:set C USERDATA:sort C USERDATA:sub C USERDATA:transpose C USERDATA:width table _G string _VERSION C _fetch_local C _fetch_remote Lua _process_event_messages () C _signal C _store_local C _update_buttons Lua abs (a) C add Lua add_line (s) Lua all (c) C apply_diff C assert C atan2 C blit C btn C btnp C camera C cd C chr C circ C circfill Lua clear_key (scancode) C clip C cls C cocreate C collectgarbage C color Lua coresume (c, ...) table coroutine C coroutine:close C coroutine:create C coroutine:isyieldable C coroutine:resume C coroutine:running C coroutine:status C coroutine:wrap C coroutine:yield C cos C costatus C count Lua cp (f0, f1) C create_diff Lua create_gui (head_el) Lua create_process (prog_name, env_patch, do_debug) Lua create_undo_stack (, ...) C cursor Lua date (format) table debug C debug:debug C debug:gethook C debug:getinfo C debug:getlocal C debug:getmetatable C debug:getregistry C debug:getupvalue C debug:getuservalue C debug:setcstacklimit C debug:sethook C debug:setlocal C debug:setmetatable C debug:setupvalue C debug:setuservalue C debug:traceback C debug:upvalueid C debug:upvaluejoin C del C deli C dtime Lua env () C error Lua exit (exit_code) Lua fetch (location, ...) Lua fetch_metadata (filename) C fget C fillp C flip C flr Lua foreach (c, _f) C fset C fstat C fullpath Lua function_info (fun) C get Lua get_clipboard () Lua get_display () Lua get_draw_target () Lua get_spr (index) C getmetatable Lua include (filename) C ipairs Lua key (scancode) Lua keyp (scancode) C line C load C ls Lua map (ud, b, ...) table math C math:abs C math:acos C math:asin C math:atan C math:ceil C math:cos C math:deg C math:exp C math:floor C math:fmod number math:huge C math:log C math:max number math:maxinteger C math:min number math:mininteger C math:modf number math:pi C math:rad C math:random C math:randomseed C math:sin C math:sqrt C math:tan C math:tointeger C math:type C math:ult Lua max (a, b) C memcpy Lua memmap (addr, a, offset, len) C memset Lua menuitem (m, a, b) Lua mid (a, b, c) Lua min (a, b) Lua mkdir (p) C mount Lua mouse () C music Lua mv (src, dest) C next C note Lua notify (msg_str) Lua on_event (event, f) C ord C oval C ovalfill C pack C pairs C pal C palt C pcall C peek C peek2 C peek4 C peek8 Lua peektext (i) C pget C pid Lua pod (obj, flags, meta) C poke C poke2 C poke4 C poke8 Lua print (str, x, y, col) Lua printh (str) C pset C pwd Lua pwf () C rawequal C rawget C rawlen C rawset Lua readtext (clear_remaining) C rect C rectfill Lua reset () Lua rm (f0) C rnd C select C send_message C set Lua set_clipboard (, ...) Lua set_draw_target (d) Lua set_spr (index, s, flags_val) C setmetatable C sfx Lua sgn (a) C sin C split C spr C sqrt C srand C sspr C stat Lua stop (txt, ...) Lua store (location, obj, meta) Lua store_metadata (filename, meta) table string Lua string:basename (self) C string:byte C string:char C string:dump Lua string:ext (self) C string:find C string:format C string:gmatch C string:gsub Lua string:hloc (self) C string:len C string:lower C string:match C string:pack C string:packsize Lua string:path (self) C string:rep C string:reverse C string:sub C string:unpack C string:upper Lua sub (str, ...) C t table table C table:concat C table:insert C table:move C table:pack C table:remove C table:unpack Lua theme (which) C time C tline3d C tokenoid Lua tonum (, ...) C tonumber Lua tostr (val, as_hex) C tostring C type Lua unmap (a) C unpack C unpod C userdata table utf8 C utf8:char string utf8:charpattern C utf8:codepoint C utf8:codes C utf8:len C utf8:offset C vec Lua vid (mode) C warn Lua window (w, h, attribs) Lua wrangle_working_file (save_state, load_state, untitled_filename, get_hlocation, set_hlocation) C yield</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div></p> https://www.lexaloffle.com/bbs/?tid=140783 https://www.lexaloffle.com/bbs/?tid=140783 Sun, 17 Mar 2024 11:40:30 UTC all of picotron functions <p>I extracted all the public names of functions and tables that are visible to Picotron, it might be useful to explore everything that is available<br /> printh doesn't seems to work like in Pico 8 so I couldn't easily make it into a text format, so here is just a screenshot of all the globals functions (in white), tables (in dark blue) and others (in red):</p> <img style="margin-bottom:16px" border=0 src="/media/11048/functions.png" alt="" /> <p>And I also got the functions inside all the tables:</p> <img style="margin-bottom:16px" border=0 src="/media/11048/tables.png" alt="" /> https://www.lexaloffle.com/bbs/?tid=140687 https://www.lexaloffle.com/bbs/?tid=140687 Fri, 15 Mar 2024 16:40:48 UTC sfx and music bugs <p> <table><tr><td> <a href="/bbs/?pid=141108#p"> <img src="/bbs/thumbs/pico8_resmguse-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=141108#p"> resmguse</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=141108#p"> [Click to Play]</a> </td></tr></table> <br /> Hi, I found some strange behaviors with sfx and music that looks like bugs<br /> ( I marked with &quot;X&quot; when it's behaving strangely)</p> <p>bug 1: releasing loop of an sfx:</p> <ul> <li>play a looping sfx</li> <li>release it's loop: sfx(-2)</li> <li>the sfx will complete it's loop and then stop emitting sound<br /> X but if you check with &quot;stat&quot; you can see that the sfx is still &quot;playing&quot; with no sound<br /> X it can still block a channel that the music will not be able to use, for ever until it's replaced by another sfx</li> </ul> <p>bug 2: playing a sfx that is already playing in the music</p> <ul> <li>play a looping music that contain a specific non-looping sfx using music()</li> <li>play the same sfx using sfx()</li> <li>the sfx will play over the music's channel with the same sfx<br /> X the music's sfx will not start again once the sfx ended<br /> X whatever channel was playing the sfx is now blocked forever from playing music, even when going to the next pattern<br /> X the stat for music still show that music is still playing even if you cannot ear it<br /> X a new call to music() will be the only way to make the channel working normally again</li> </ul> <p>I understand that those corner cases are rarely used so I get why it's not being a major problem, but I still think it can give some very strange behavior to the sound, potentially blocking all of the music</p> https://www.lexaloffle.com/bbs/?tid=140089 https://www.lexaloffle.com/bbs/?tid=140089 Sun, 04 Feb 2024 13:38:10 UTC Smooth Ride <p> <table><tr><td> <a href="/bbs/?pid=135090#p"> <img src="/bbs/thumbs/pico8_smoothride-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=135090#p"> smoothride</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=135090#p"> [Click to Play]</a> </td></tr></table> <br /> One button tiny physic-based platformer made in Pico 8 with code under 1024 compressed bytes, made for #Pico1k jam 2023</p> <p>Use RIGHT ARROW to accelerate when on the ground<br /> Release RIGHT ARROW to fall faster when in the air<br /> Press UP ARROW to start a new level<br /> Press ENTER and select SEED in the menu to start from level 1 if you want a fixed series of levels</p> <img style="margin-bottom:16px" border=0 src="/media/11048/smooth_ride_large_5.gif" alt="" /> <p>Number on top left is a timer for your run of the level, counted in frames (at 60fps), on top right is the sum for all the levels in your session. For example a casual run of all the seeded levels from 1 to 10 took me 14027 frames. My PB for level 1 is 1071.</p> <p>Source as p8 are available on <a href="https://nusan.itch.io/smooth-ride">https://nusan.itch.io/smooth-ride</a> in two versions, the &quot;comment&quot; version is what I coded with additional comments, so it's readable but the &quot;minimal&quot; version uses a tool (<a href="https://thisismypassport.github.io/shrinko8/">https://thisismypassport.github.io/shrinko8/</a>) to makes it even tinier, it's not readable but it fits into the 1k compressed bytes target </p> https://www.lexaloffle.com/bbs/?tid=54349 https://www.lexaloffle.com/bbs/?tid=54349 Fri, 29 Sep 2023 07:19:34 UTC floodedcaves <p> <table><tr><td> <a href="/bbs/?pid=109673#p"> <img src="/bbs/thumbs/pico8_floodedcaves-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=109673#p"> floodedcaves</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=109673#p"> [Click to Play]</a> </td></tr></table> </p> <p>it's flood season, dig caves to redirect an inevitable rising water until you can save everyone<br /> Also available on itch.io for binaries <a href="https://nusan.itch.io/flooded-caves">https://nusan.itch.io/flooded-caves</a></p> <img style="margin-bottom:16px" border=0 src="/media/11048/ld50_12_0.gif" alt="" /> <p>This game has been made in 48h for game jam Ludum Dare 50</p> <p>Your digging power is limited, it recharges over time. At regular interval, you can save someone by sending your rescue team</p> <p>You can play the game using a keyboard (action is key C ), a gamepad or a mouse</p> https://www.lexaloffle.com/bbs/?tid=47247 https://www.lexaloffle.com/bbs/?tid=47247 Mon, 04 Apr 2022 11:40:49 UTC Dami &amp; Flo <p> <table><tr><td> <a href="/bbs/?pid=87011#p"> <img src="/bbs/thumbs/pico8_damiandflo-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=87011#p"> damiandflo</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=87011#p"> [Click to Play]</a> </td></tr></table> <br /> Game is also available on Itch.io: <a href="https://nusan.itch.io/dami-and-flo">nusan.itch.io/dami-and-flo</a></p> <img style="margin-bottom:16px" border=0 src="/media/11048/santa_15_3.gif" alt="" /> <p>Dami and Flo are two workers on a delivery train. At each station, they have to take input packages at the bottom and store them somewhere in the wagon so they can be easily delivered at a future train stop.</p> <p>There is a one player mode or a two player local splitscreen mode. You can choose your difficulty with &quot;Timer&quot; option and &quot;Items&quot; quantity option. You can also choose to have more than one wagon, but it can be very difficult especially if you are not playing with a friend.</p> <p>Between stations, take your time to sort and store all the packages in the wagon. Clear the input area as fast as possible so new packages can be inserted at the next stop. Delivery requests for the next station are displayed at the top of the screen.</p> <p>You can drop off requested items in the output area, they will be picked up automatically at the next station. You can stack items on the ground up to 5 items high and your character can carry up to 10 items at a time.</p> <p>Controls:</p> <p>Player 1:</p> <p>pick package: Z or C or N<br /> drop package: X or V or M<br /> move character: ARROW KEYS<br /> Player 2:</p> <p>pick package: LEFT SHIFT<br /> drop package: TAB or W or Q or A<br /> move character: S+F+E+D<br /> Open main menu: ENTER to se stats or toggle the music</p> <p>You can also use a game controller</p> https://www.lexaloffle.com/bbs/?tid=41341 https://www.lexaloffle.com/bbs/?tid=41341 Sun, 31 Jan 2021 14:27:16 UTC SnowBall [LD45] <p> <table><tr><td> <a href="/bbs/?pid=68594#p"> <img src="/bbs/thumbs/pico8_snowballnusan-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=68594#p"> snowballnusan</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=68594#p"> [Click to Play]</a> </td></tr></table> </p> <p>Small game made for Ludum Dare 45 in 48h.</p> <p>Roll your way through 7 levels and collect snow to make a happy snowman.</p> <p>Controls:</p> <ul> <li>left/right: move</li> <li>up: jump</li> <li>x/c: reset level</li> </ul> <p>The Ludum Dare page is here if you want to vote: <a href="https://ldjam.com/events/ludum-dare/45/snowball">https://ldjam.com/events/ludum-dare/45/snowball</a></p> https://www.lexaloffle.com/bbs/?tid=35583 https://www.lexaloffle.com/bbs/?tid=35583 Sun, 06 Oct 2019 22:14:39 UTC DeFacto <p> <table><tr><td> <a href="/bbs/?pid=48407#p"> <img src="/bbs/thumbs/pico48406.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=48407#p"> DeFacto</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=48407#p"> [Click to Play]</a> </td></tr></table> <br /> DeFacto is a game about building a large automated factory chain.<br /> You must install miners to collect raw resources, transform them in furnaces, and combine them in factories.<br /> The final goal is to send rockets full of robots and by doing so continue to explore the galaxy.<br /> DeFacto is heavily inspired by the game Factorio.<br /> You can also play and support the game on : <a href="https://nusan.itch.io/defacto">nusan.itch.io/defacto</a></p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/DeFacto/defacto_tweet_proper.gif" alt="" /> <p><object width="640" height="400"><param name="movie" value="https://www.youtube.com/v/4CSqwqjR6Ig&hl=en&fs=1&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="https://www.youtube.com/v/4CSqwqjR6Ig&hl=en&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="400"></embed></object></p> <p>Controls :<br /> The game can be played either with the mouse or keyboard</p> <p>Keyboard :<br /> C : Use selected tool/Interact with factories<br /> V : Open tool selection panel/quit menu<br /> enter : Open main menu (save, load, toggle music ...)</p> <p>Mouse :<br /> Left clic : Interact/Use selected tool<br /> Right clic : Open tool selection panel/quit menu</p> <p>To save your game, press enter and select save (button C)</p> <p>To construct conveyor, inserter or bridge :<br /> you have to keep pressing C/left clic and then enter the direction (arrow key or mouse motion) where the conveyor/inserter/bridge will put it's products<br /> if you only press c/left clic without entering direction, it will place the conveyor, inserter or bridge along the last used orientation</p> <p>Bridges can also be used to take and insert product, and are faster than inserters.</p> <p>Starting tips :</p> <ul> <li>start by opening the tool panel (V/Right clic) and choosing the miner tool</li> <li>place the miner onto a raw resource field (C/Left clic), the miner will start filling</li> <li>open the tool panel and select the inserter tool</li> <li>place the inserter against the miner by pressing and holding C/left clic and then entering the direction where you want to put the products</li> <li>open the tool panel and select the selling station</li> <li>place the selling station at the end of the inserter</li> <li>if the inserter is oriented correctly, it will start moving resources collect by the miner to the selling station and you will start gaining money</li> <li>once you have a bit more money, start putting raw resources into furnaces, the resulting products will sell at higher prices</li> <li>the next step is to use factories, choose the recipe (C/Left clic on the factory) and insert the required input products to make more advanced ones</li> </ul> https://www.lexaloffle.com/bbs/?tid=30631 https://www.lexaloffle.com/bbs/?tid=30631 Sat, 20 Jan 2018 14:00:54 UTC Juggle Jam (LD40 jam) <p> <table><tr><td> <a href="/bbs/?pid=46983#p"> <img src="/bbs/thumbs/pico46982.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=46983#p"> Juggle Jam (LD40 jam)</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=46983#p"> [Click to Play]</a> </td></tr></table> </p> <p>&quot;Juggle Jam&quot; is more of a toy than a game. It's based on some mathematical theory behind juggle patterns. It has been made in 48h during LDJAM 40.<br /> You can :</p> <ul> <li>load premade juggling patterns</li> <li>create your own juggling patterns using a dedicated in-game editor</li> <li>create a random juggling pattern and let it hypnotise you</li> </ul> <p>If you know how to juggle, you can try to follow the motion and the patterns.<br /> Let me know if you have managed to reproduce some patterns with real balls.<br /> Time spent during the jam actually juggling instead of coding : too much</p> <p>Controls :</p> <ul> <li>keys C and V and arrow keys</li> <li>return : menu/help<br /> In play mode :</li> <li>c:toggle view</li> <li>v:go to editor mode<br /> In editor mode :</li> <li>c:grab a ball or a throwing node</li> <li>v:go to play mode</li> </ul> <p>This idea was inspired by a video from Numberphile, you can check it if you want to know more about some math theory of juggling :<br /> <a href="https://www.youtube.com/watch?v=7dwgusHjA0Y"><a href="https://www.youtube.com/watch?v=7dwgusHjA0Y">https://www.youtube.com/watch?v=7dwgusHjA0Y</a></a></p> <p>You can find the Ludum Dare page here :<br /> <a href="https://ldjam.com/events/ludum-dare/40/juggle-jam"><a href="https://ldjam.com/events/ludum-dare/40/juggle-jam">https://ldjam.com/events/ludum-dare/40/juggle-jam</a></a></p> https://www.lexaloffle.com/bbs/?tid=30371 https://www.lexaloffle.com/bbs/?tid=30371 Sun, 03 Dec 2017 21:27:08 UTC Alone in Pico : Postmortem of a 3d demake <p><strong>Plan :</strong></p> <ol> <li>Inception</li> <li>Gameplay</li> <li>How to render 3D meshes in Pico 8</li> <li>Pre-rendered magic</li> <li>Extracting meshes from Alone in the Dark</li> <li>Character animation</li> <li>Memory layout, packing and ram management</li> <li>Camera placement, tools</li> <li>Interactions, objects animations, sounds</li> <li>Optimization</li> <li>Triangle strips</li> <li>Future</li> </ol> <p>Disclaimer : my english skills are a bit low and I wrote that post a litlle too fast ...</p> <p>The final project can be found <a href="https://www.lexaloffle.com/bbs/?tid=3792">here</a><br /> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/AloneFinal_01.gif" alt="" /> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/AloneFinal_02.gif" alt="" /></p> <p><strong>1. Inception</strong></p> <p>The idea of making a Pico 8 demake of the classic cult game Alone In The Dark started at Halloween 2015. I was searching for a mockup to do using Pico 8 color palette, and Alone in the Dark seemed a pretty good fit. After looking at it, the original is a lot more detailed than what would be possible in Pico 8. Getting from like VGA 256 colors to 128x128 16 colors would not be easy. But the mesh complexity in Alone in the Dark seemed doable in Pico 8. I had just finished a small 3D demo in Pico 8 so I knew that I could probably draw the character at 30 fps and pre-render the background.</p> <p>The mockup that started everything :</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/Mockup.png" alt="" /> <p>The mockup was well received, but after that I was quite afraid by the quantity of work to do to make it real, so I let the project sleep. Until 3 month later, I get a twitter reply by Frederick Raynal himself, director of the original Alone in the dark. It turns out that he is quite interested in Pico 8.</p> <p>This was enough to give me motivation to push the project forward.</p> <p>You can play in you browser to the original Alone in the Dark <a href="http://playdosgamesonline.com/Alone-in-the-dark.html">here</a></p> <p><strong>2. Gameplay</strong></p> <p>Alone in the Dark is a good fit for Pico 8 as the original can be played with only 2 buttons beside direction keys. One button is to open an inventory, where you can choose a �stance� for your character (like �search�, �push�, �fight� �) or see your items. The second button apply your stance around you, so your character will search, or push something around or start fighting (using direction keys to choose fight animations).<br /> For each item in the inventory, you can do a large quantity of actions, like read a letter, throw an object, put it on the floor, open boxes, drink potions � This is an heritage of the text-based adventure game I think. Nowadays everything is contextual, but back then you would have to know which precise action to do with every item and background object.<br /> I first wanted to do all of that. But it was tricky on several level. First, the fighting needed a lot of animations, which is not easy to do. I also wanted an exploration game more than an action game. By replaying the original, I saw that you could bypass nearly every combat in the first two floors. So I decided to leave the fight out, and make the player find �peaceful� ways to progress.<br /> The complex inventory system of Alone in the Dark was a bit much for Pico 8. With the 128x128 16 colors, there was not enough details to give you hints about what action you could do where. So I did a more �modern� system, where there is a visible prompt when you can do an action somewhere, and the action is contextual. It makes the game really simple, but it fit the format of a short exploration trip much better. With more tokens/time, I would have added some keys icons to show progress. And maybe some Lovecraft books to read ...</p> <p><strong>3. How to render 3D meshes in Pico 8</strong></p> <p>I started by using the tools from my previous 3D demo in Pico 8, <a href="https://www.lexaloffle.com/bbs/?tid=2734">Space Limit</a><br /> I was originaly going to remake everything, from characters to background images. So I made in Blender a crappy looking character that vaguely resembled Edward Carnby. My custom-made exporter enable to have a color per triangle.<br /> Here is the Blender plugin i made : <a href="http://lezanu.fr/Pico8/io_mesh_pico.7z">plugin blender</a></p> <p>My first hand-crafted Edward model :</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/PICO-8_1.gif" alt="" /> <p>I also latter modified a mesh stripping sample from coder corner to load, strip and convert meshes to character strings. More on that in the �Triangle strips� section.<br /> My tools produce two strings : a vertex buffer and an index buffer. Each string can be copy/pasted in Pico 8 and some code will decode it and store it in arrays.<br /> The vertex buffer encode the position (x,y,z) of each vertex of the mesh, and the index buffer describe the triangles of the mesh. It stores the index of the 3 vertices making the triangle, as well as the color of the triangle. To make it easy to use, each value is between 0 and 255 and is stored in hexadecimal, so two characters. The color is the only value that can be stored as 1 character, as there is only 16 values, thanks to Pico 8 limitations. The vertex buffer is also linked to a scaling value, to transform from 0-255 to the desired mesh size. The mesh can only have up to 256 vertices, as we store their index as 2 hexadecimal strings, but it�s usually enough.</p> <p>The objects I made in Blender :<br /> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/Objects01.png" alt="" /><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/Objects02.png" alt="" /></p> <p>Here is a sample code to decode the �val� vertex buffer string into the �verts� array:</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 avlist(verts,scale,val) local maxsize = 5.0*scale while(#val &gt; 0) do local cur = sub(val,1,6) val = sub(val,7,#val) local x = flr(&quot;0x&quot;..sub(cur,1,2)) local y = flr(&quot;0x&quot;..sub(cur,3,4)) local z = flr(&quot;0x&quot;..sub(cur,5,6)) x = (x/256.0 - 0.5) * maxsize y = (y/256.0 - 0.5) * maxsize z = (z/256.0 - 0.5) * maxsize verts[#verts+1] = newver(x,y,z) end 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>Then when we want to draw the mesh, here are the steps :<br /> First transform each vertex in the viewport 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> local tverts = {} local tv = #verts for i=1,tv do local cur = clone(verts[i]) local side = 1 if(cur.z&lt;0) side = -1 cur = v_add(cur,v_sub(ent.loc,campos)) local cur2 = clone(cur) cur2.x = dot(cur,camright) cur2.y = dot(cur,camup) cur2.z = dot(cur,camdir) local invz = 64.0*(1.0/max((cur2.z),0.1)) cur2.x = (-cur2.x * invz + 63.5) cur2.y = (-cur2.y * invz + 63.5) tverts[i] = cur2 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>The vector camright, camup and camdir are the 3 vectors of the camera space. In the final project, those vector and the campos are pre-transformed with the rotation of the mesh, making possible to turn the main character with a really small cost.<br /> The next step is to sort the triangles. In a modern renderer, we would use a z-buffer but then you need to store it with good precision, and it can hurt your framerate. Each pixel need to be compared to the z and you can�t use a simple rectfill to draw several pixel together.<br /> Sorting can be fast, but come with some issues.<br /> To sort our triangles, we need to compute it�s distance to the camera (or simply the average of the 3 vertices z location). Then we can either sort them completely, which is slow, or iteratively, spreading the work over several frames. We will do a complete sort only when changing camera, and iteratively each frame so the dynamic objects keep beeing about sorted. If you rotate the character too fast, you will see triangles being wrongly sorted. We sort by swapping values in the triangle array, which is persistent between frames. We can even adjust how many passes of sorting we do per frame depending of the framerate.</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> local tn = #tris for i=1,tn do local ct = tris[i] ct.avg = tverts[ct.v1].z + tverts[ct.v2].z + tverts[ct.v3].z end if tele_cam then sortalltris(tris) else sorttrisloop(tris,2) 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>And finally we will render the triangles on the screen. To make a real 3D renderer, you should here compute the intersection of the triangle with the near clip plane. Depending of the intersection, you would draw up to 2 triangles. To save some framerate, I don�t do that, but it would be required for a generic 3D renderer. It�s only an issue when triangles goes out of the screen and behind the camera. We will have to place our camera carefully to avoid those glitchs.</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 i=1,tn do local ct = tris[i] local v1 = tverts[ct.v1] local v2 = tverts[ct.v2] local v3 = tverts[ct.v3] -- We check if the triangle is facing the camera or not local back = (v2.y-v1.y)*(v3.x-v1.x) - (v2.x-v1.x)*(v3.y-v1.y) local minx = min(min(v1.x,v2.x),v3.x) local maxx = max(max(v1.x,v2.x),v3.x) local miny = min(min(v1.y,v2.y),v3.y) local maxy = max(max(v1.y,v2.y),v3.y) local minz = min(min(v1.z,v2.z),v3.z) -- Compute the bounds of the triangle, cull it if outside the screen local clip = maxx &lt; 0 or minx &gt; 128 or maxy &lt; 0 or miny &gt; 128 or minz &lt; 0.01 if back&gt;=0 and not clip then otri(flr(v1.x),flr(v1.y),flr(v2.x),flr(v2.y),flr(v3.x),flr(v3.y),ct.c) end 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>The only part left is the rendering of the triangle itself. Here we separate the triangle in two, with an horizontal slice. Each line will then be rendered using rectfill.</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 otri(x1,y1,x2,y2,x3,y3,c) if y2&lt;y1 then if y3&lt;y2 then y1,y3=swap(y1,y3) x1,x3=swap(x1,x3) else y1,y2=swap(y1,y2) x1,x2=swap(x1,x2) end else if y3&lt;y1 then y1,y3=swap(y1,y3) x1,x3=swap(x1,x3) end end y1 += 0.001 local miny = min(y2,y3) local maxy = max(y2,y3) local fx = x2 if y2&lt;y3 then fx = x3 end local cl_y1 = clampy(y1) local cl_miny = clampy(miny) local cl_maxy = clampy(maxy) local steps = (x3-x1)/(y3-y1) local stepe = (x2-x1)/(y2-y1) local sx = steps*(cl_y1-y1)+x1 local ex = stepe*(cl_y1-y1)+x1 for y=cl_y1,cl_miny do rectfill(sx,y,ex,y,c) sx += steps ex += stepe end sx = steps*(miny-y1)+x1 ex = stepe*(miny-y1)+x1 local df = 1/(maxy-miny) local step2s = (fx-sx) * df local step2e = (fx-ex) * df local sx2 = sx + step2s*(cl_miny-miny) local ex2 = ex + step2e*(cl_miny-miny) for y=cl_miny,cl_maxy do rectfill(sx2,y,ex2,y,c) sx2 += step2s ex2 += step2e end 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><strong>4. Pre-rendered magic</strong></p> <p>The main technical trick that Alone in the Dark use is : pre-rendered backgrounds. This made possible having complex environments while using real-time characters. It gave a �cinematic� feel to the game.<br /> In the case of my Pico 8 demake, it�s a bit different. I could not realy store backgrounds in a large quantity inside the limited space of a Pico 8 cartridge. With some compression, it may be possible but only up to a certain point.<br /> Instead of storing backgrounds, I can render them from 3D objects, but only when the camera change. I can store a large quantity of 3D meshes, and reuse them at will. I can also put a lot of camera angles, without using more memory.<br /> Rendering the background only when the camera change leave all the cpu free to draw the dynamic objects (the main character for example). The only issue is that we need to store the background between frame, so we can start each frame with just the background, and draw dynamics on top of it.</p> <p>Here is a sample code to do that. The function fillsorted() will add meshes in the background and dynamics arrays. The memcpy store and put back the background when needed (in two separate chunks to make it fit nicely where there is space left in memory). The variable need_paste is set to false when there is a camera change.</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 _draw() if need_paste then memcpy(0x6000,0x3E00,0x0400) memcpy(0x6400,0x4300,0x1c00) else cls() fillsorted() foreach(background, draw_tris) bground = {} memcpy(0x3E00,0x6000,0x0400) memcpy(0x4300,0x6400,0x1c00) need_paste = true End foreach(dynamics, draw_tris) 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>We don�t want to sort every triangles of every object between them, as it would be slow and not realy frame consistent when objects pass each other. So we sort first each object according to their location before drawing their sorted triangles. In the end I sorted objects using the 2D coordinates on the floor, as it was better than using 3D coordinates of the location.<br /> The background mesh of each room was too big and it didn�t make sense to try to sort it with the static objects in the scene. So there is a list of background room meshes that is draw before everything, and a list of background object meshes drawn on top of it. And then the list of dynamic objects.</p> <p>The most important issue with prerendering background is that objects from the background cannot pass in front of the main character. So for example the pillars in the cellar would always be behind, which break the immersion of the scene.<br /> My solution was simply to manually select the most problematic objects for each view angle, and draw it dynamically so it�s sorted with the player. There is some tricks to it that I will describe in the �Optimization� section.<br /> When the player is entering/exiting using a door in the second floor, I need a bit of masking so it seem like he pass inside the door. But the background is pre-rendered. So I made some half door covering, that I put in front of each door.</p> <p>A half-doorway used to cover the character :</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/HalfDoor.png" alt="" /> <p><strong>5. Extracting meshes from Alone in the Dark</strong></p> <p>The three characters I ripped from alone in the Dark :</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/Characs.png" alt="" /> <p>After I tweeted a gif of my first mesh, Frederick Raynal gave me a good idea : using the meshes directly from the original game. I first searched if someone already had ripped the characters and made them available, but no luck. Frederick saved the day again and found an animation/mesh viewer a fan made for Alone in the Dark. As I got the source code of the viewer, I could try to add an export option. But it was a lot of work, so instead I used an openGL interceptor that can save textures and 3D models from �any� openGL game. That way I could directly get a .obj of all dynamic meshes from Alone in the Dark. Obviously the background where not in 3D, so I still needed to make that myself in Blender.<br /> So I ripped Edward, Emily and a zombie mesh. After that I only needed to choose a color for each triangle, trying to mimic the original. The color must comme from the Pico 8 palette of 16 colors of course.</p> <p>Here is the openGL interceptor I used :<br /> <a href="https://github.com/dtrebilco/glintercept">GLIntercept</a><br /> There was also an amateur open source project called �Free in the Dark� but it�s difficult to find now. Plus with that project the openGL injector kept giving me view-space meshes, which is not very practical.</p> <p><strong>6. Character animation</strong></p> <p>I decided quite early that I will not have fighting, so that leaved mainly the character walking animation. I did it procedurally, using only simple maths. I use y and z location of vertices to decide which part of the body it belong. Then using sinuses and offsets between body parts, I tried to give it a walking animation. There is also some interpolation, so the character take some frame to get back to the idle pause. I reset the animation time when the character doesn�t move, so he/she always start at the beginning of the walking loop. The code is a mess, but you can find it in the draw_tris_anim() function.<br /> Both Edward and Emily are animated the same way, with just some slight adjustments to the limits between each body parts.</p> <p><strong>7. Memory layout, packing and ram management</strong></p> <p>Making everything fit in the limited space of a Pico 8 cartridge was a bit of a challenge. The layout of data was not completely static, as different data where needed at different times. I needed space to store vertex/index buffers. I needed space to store the backbuffer between frames. I needed space to store collisions.<br /> First, there was the two character mesh data, which was required only in the selection screen. Then we would keep the selected character data in RAM, throw away the other, and would not need the cartridge memory anymore.<br /> To reduce the collision footprint, I compressed it as 1-bit per cell. So each cell would only be traversable or not. After the selection screen, the collision data are put back in the �map� memory location, where the two characters data where stored. Having collision in the map memory make it easy to check and easy to change in gameplay (when moving objects, opening doors �)<br /> The backbuffer between two frames is stored for the most part in the �user data� section of the memory, which is not stored in the cartridge anyway. The small part that doesn�t fit in the �user data� is put at the end of the �sfx� section, leaving about 32 sounds free for music/sfx.</p> <p>Another issue was the RAM size. I started the project with the 0.1.5 version of Pico 8, which had only 512k of ram. With 0.1.6 it�s now 1024k of ram and it�s a lot easier. I still need to clean all meshes data between each room to save ram. The top floor fit totally in memory, but the second floor does not. Between each room, I clear all meshes (by setting their references to nil) and construct only the required ones from the cartridge data. The same bit of code also decide in what draw list each mesh go (prerendered background, prerendered sorted background, dynamics).<br /> Some meshes are not stored in cartridge data but directly in strings inside the code. I did that because I was nearly out of cartridge space. But it�s quite risky as this will count in the �compressed size� limit, which is one of the main limitation.</p> <p><strong>8. Camera placement, tools</strong></p> <p>To make working on the game a bit easier, I made a debug mode where you could change the camera position, the location of it�s target, and the distance between them. The camera position and target position were printed on screen, so I could take notes when placing cameras. This mode rendered everything in real time, at like 3fps, but made debugging simpler.<br /> To pack my datas in the cartridge, I used others cartridges as tools. Instead of having to figure out how memory is packed in the .p8 file, I just put the strings I want to insert in a fresh cartridge. A small code read the string and put each value in the memory at a specified address. The code then make a cstore() to save the memory in the .p8. I can then copy the cartridge data from that temporary cart to my final cart using a text editor.</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> local vadd = cur_addr for i=1,#verbuf,2 do local value = &quot;0x&quot;..sub(verbuf, i,i+1) poke(vadd,value) vadd += 1 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>View of the second floor in blender without dynamics objects :</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/floor02.png" alt="" /> <p><strong>9. Interactions, objects animations, sounds</strong></p> <p>To make the interaction system, I wanted to merge everything in a single system to keep token count low. I also wanted to experiment with LUA flexibility.</p> <p>So here is how I declare a simple text interaction at the location x=10,y=20 over a circle range of radius 4 in the floor #1 :</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> newact(10,20,4,1,&quot;a simple chair&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>But I can extend the functionality if needed, here for an interaction that give you a key, specifying what text to print when you pickup the key :</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> ac_pianokey=give(newact(-52,-10,4,2,&quot;nothing left&quot;),&quot;you find the piano key&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>Here is some extreme case, where a door will only open if you have the piano key, and then remove some collisions and animate the door over a fixed period of time to open it. It will also say �open� in the prompt instead of �look� and play the sfx #14 :</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> ac_door3=newact(-35,2,7,2,&quot;you use the small key&quot;, function() setcol(0,112,16,3,2) end, function() r2_door3.rot -= 0.0166 end,nil,&quot;open&quot;,14) ac_door3.need=ac_pianokey </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>To keep the door open if you come back later, I have some code in the scene construction code (�.anim� let me know if the action has been made) :</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(not ac_door3.anim) setcol(1,112,15,3,4) r2_door3.rot = ac_door3.anim and -0.7 or -0.25 </pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Some interaction start automatically when the player enter the range. Here is a zombie trap that will launch itself, change the music to #9 and make the sound #15</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> ac_zomb3=auto(newact(-23,-29,11,2,&quot;it's a trap&quot;, function() setmusic(9) end,nil,nil,nil,15)) </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 also made an interaction type that just change the footstep sound when in range. Now that I think of it, I should just have specified a footstep sound per camera, it would have been much easier.<br /> You can find the interaction code in the function doaction(action), but I would advise you to write your own because this one is messy and tweaked for my need.</p> <p><strong>10. Optimization</strong></p> <p>Here I will just show some funny tricks I used to maintain a nearly constant 30fps.<br /> In the first camera of the game, you see a green table covering the whole bottom of the screen. The view was really heavy and it was sad that the first screen the player see was not at 30fps. So I simply removed the table, and I manually draw a gigantic green circle on the bottom, to simulate the table on this camera angle only.<br /> I did most of my test using the mesh of Edward Carnby, as it�s the first I made/ripped. But when I ripped the mesh of Emily Hartwood, it had a lot more triangles. To try to keep it about the same, I sadly removed some triangles from Emily�s mesh. The most obvious is the hair area, which were a lot rounder than for Edward. I also simplified her high heels, but I don�t think it�s visible. Even after that, she is heavier than Edward, and you can lose some frame sometimes.<br /> For some scenes I draw some objects only when I think the player is near them. So the object are drawn in the pre-rendered background, and can be also rendered dynamicaly on top if the player is near. For example some pillars in the cellar are drawn like that.<br /> In the bedroom, downstair, it was quite hard to reach the 30fps. So there is a custom clip rectangle for dynamic objects, to limit what I need to draw to the area the player can go.</p> <p>The clipping rectangle in the bedroom :</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Alone/Clip.png" alt="" /> <p>To keep some ram, objects in the second floor are not duplicated, but simply shared between rooms and moved. In the last room, with the double-wings stair, I even use the same altar, mirror and zombie for the two sides.</p> <p>The tokens and compressed size limits were frequently reached. The compressed size is a bit less hard, because in recent versions of Pico 8, it only shows itself when you export in html or upload on the BBS. The tokens size is a hard limit, if you are above you can�t run the game. Regularly I would optimize and get back some tokens, mainly by removing old code, useless parenthesis or �inlining� function that were used only once.<br /> Near the end, I even needed to remove the debug tools (and not even leave it commented as that still count in the compressed limit) for camera, cheats � I added some specialised functions to insert in a specific array instead of specifying the array each time to save tokens/compressed. Ternary operator are also useful (IF x THEN v=a ELSE v=b END become v= x AND a OR b)</p> <p><strong>11. Triangle strips</strong><br /> While I was making the game, I always thought that I would not be able to put the two characters inside the same cartridge. They are in fact the largest meshes and I hadn�t enough space to keep them and store objects for the two floors. Without even thinking about storing the zombie. So I was going to release two cartridge, one with Emily and one with Edward. But the options on the BBS does not really allow that in good conditions. I could choose one to put in front of the thread, and the other would be just below. But only the first would appear in XPlore and be playable easily. Or I could make two separate threads, but it would divide the attention, or even make it in a competition between the two characters.<br /> So while asking to Zep for advices, he kinda suggested that it should be possible, as a CHALLENGE to pack everything together. So I needed to do it �<br /> Then I had the nice idea of using triangle strips buffers, instead of the classic �triangle after triangle� index buffer. With triangle strips, you set a list of triangles where each new triangle reuse two vertices of the last. It makes the index buffer much shorter.<br /> I looked online to find a library and the one on Coder Corner seemed nice and comprehensible : <a href="http://www.codercorner.com/Strips.htm">Strips</a><br /> So I changed it to load the files that are exported by my Blender plugin. I added some code so every packing is done in one place (before that I used a crappy javascript tool I made). I could even pack all my files in one go, and print strings ready to paste in Pico 8.<br /> As my colors are per triangle instead of per vertices, I needed to do the stripping separately for each colors. Each strip would store : the length of the strip (8 bits), the color of the strip (8 bits because using 4 bits would add a lot of trouble when decoding with peek) and then each vertices index (8 bits).<br /> Here is my modified version (to use with visual 6) : <a href="http://lezanu.fr/Pico8/PicoStrips.zip">PicoStrips.zip</a><br /> This worked very well, each index buffer became about half as long, putting it at about the same size as the vertex buffer. With that optimization, I could repack everything with enough space to store the two characters, the zombie, and even enough sfx space to store songs and proper sound effects. All of that in one single cartridge.<br /> For now, the triangles are not stored in ram using strips as I would need to rework a lot of things. Plus as I use the triangle list to sort everything, a strip version would not be faster.</p> <p><strong>12. Future</strong><br /> I am really glad I finished that project. It took about 6 months, and some hurdles were hard to overcome, but the result is nice. I want to thanks Frederick Raynal for the amazing game that is Alone in the Dark, and for his help in making this small project.<br /> For now this project if finished, adding more of the original game would require several cartridge, and implementing more complex interactions (fight, throwing, shooting �)<br /> But if some people are interested, I may make a simple 3D application as a sample.<br /> I would polish my tools so others can use them, and make a simple tutorial.<br /> Beside, I want to continue experimenting with Pico 8 and 3D, maybe with procedurally created worlds ...</p> <p>If you have any questions or advices, feel free to post here.</p> https://www.lexaloffle.com/bbs/?tid=3804 https://www.lexaloffle.com/bbs/?tid=3804 Mon, 11 Jul 2016 13:02:21 UTC Alone in Pico <p> <table><tr><td> <a href="/bbs/?pid=24899#p"> <img src="/bbs/thumbs/pico25149.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=24899#p"> Alone in Pico</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=24899#p"> [Click to Play]</a> </td></tr></table> </p> <p>&quot;A suspicious suicide. A chilling curse. A malevolent power. And a wicked dark secret. This is Derceto ...<br /> Choose between Emily Hartwood and Edward Carnby to explore this virtual adventure game inspired by the work of H.P.Lovecraft.&quot;</p> <p>Controls : Arrow keys to move, 'c' or 'v' to interact<br /> Music can be turned off in the menu ('enter' key)</p> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/AloneFinal_01.gif" alt="" /> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/AloneFinal_02.gif" alt="" /></p> <p>I wrote a (long) post-mortem of this project : <a href="https://www.lexaloffle.com/bbs/?tid=3804">here</a><br /> This is a small &quot;demake&quot; of Alone in the Dark. You can explore and find your way out of the top two floor of the mansion. I tried to keep it close to the original, but there is no fight (if you see an enemy you will die) and the interaction system is simpler. The 3D meshes of the characters are ripped from the original game.</p> <p>I used a <a href="http://lezanu.fr/Pico8/io_mesh_pico.7z">plugin</a> I made to export meshes from Blender with color per triangle and I modified a mesh stripping sample from <a href="http://www.codercorner.com/Strips.htm">coder corner</a> to load, strip and convert meshes to character strings. You can find my version (to use with Visual 6) <a href="http://lezanu.fr/Pico8/PicoStrips.zip">here</a>.</p> https://www.lexaloffle.com/bbs/?tid=3792 https://www.lexaloffle.com/bbs/?tid=3792 Fri, 08 Jul 2016 11:23:17 UTC Multiple carts in same thread <p>Hi, for a game I'm working on, you can choose between two characters. But I dont have enough space to store the two characters in the same cart. I was thinking about releasing two carts, each one with a character.</p> <p>The issue is that a given thread in the BBS only show the most recent cart in the &quot;splore&quot; of Pico 8.<br /> Do I need to make two separate threads then ? Or is there a way to show or choose what cart is shown in splore ?</p> <p>I could also try a multiple cartridge thing, like with a main cart with a selection menu, and two cart for the characters. But I dont think it work in web player or in splore. You would need to download the 3 carts manualy.</p> <p>Is there a better idea than two separate threads ?</p> https://www.lexaloffle.com/bbs/?tid=3590 https://www.lexaloffle.com/bbs/?tid=3590 Sun, 12 Jun 2016 06:32:22 UTC Combo Pool <p> <table><tr><td> <a href="/bbs/?pid=21515#p"> <img src="/bbs/thumbs/pico8_combopool-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=21515#p"> combopool</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=21515#p"> [Click to Play]</a> </td></tr></table> </p> <p>Update : I made an Android version, with special touch controls, <a href="https://nusan.itch.io/combo-pool">on my Itch.io page</a><br /> Update 2 : I improved collisions and fixed the &quot;keep perfectly vertical&quot; way of cheating.<br /> Update 4: You can now use the mouse to point and shoot! Also accessibility option to display numbers on the balls, so you can better identify them</p> <p>Here is Combo Pool, my entry to p8jam2. It's a game where you throw colored marbles against each other. If two marbles of the same color make contact, they merge and upgrade to the next color. Your lifebar diminish with the number of balls on the field. If you lifebar is empty, you enter in a sudden death mode, and your last ball must save you by removing some balls.</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/combo_intro.png" alt="" /> <p>Thanks for your feedback!</p> <p>Pro-tips :</p> <ul> <li>dark and grey balls do more damage to your life bar, so avoid keeping to many at the same time</li> <li>score system depend on number of rebound before merging and on balls colors</li> <li>a usefull technique is to quickly throw two dark ball and make them merge at the first collision</li> <li>try to avoid exploding pink balls as long as you can, you can expect making more point in the final explosion</li> </ul> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/combo_4.gif" alt="" /><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/combo_3.gif" alt="" /></p> <p>If someone want to use the physic/collision code, here is a kinda simple and clean version with just that :<br /> <table><tr><td> <a href="/bbs/?pid=21515#p"> <img src="/bbs/thumbs/pico21797.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=21515#p"> Simple ball collision/physics</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=21515#p"> [Click to Play]</a> </td></tr></table> </p> <p>Old versions :<br /> <table><tr><td> <a href="/bbs/?pid=21515#p"> <img src="/bbs/thumbs/pico26895.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=21515#p"> Combo Pool</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=21515#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=21515#p"> <img src="/bbs/thumbs/pico21659.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=21515#p"> Combo Pool</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=21515#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=21515#p"> <img src="/bbs/thumbs/pico21561.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=21515#p"> Combo Pool 0.6.1</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=21515#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=21515#p"> <img src="/bbs/thumbs/pico21513.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=21515#p"> Combo Pool</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=21515#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=3467 https://www.lexaloffle.com/bbs/?tid=3467 Fri, 27 May 2016 20:37:46 UTC P.Craft <p> <table><tr><td> <a href="/bbs/?pid=19680#p"> <img src="/bbs/thumbs/pico24981.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=19680#p"> P.Craft</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=19680#p"> [Click to Play]</a> </td></tr></table> </p> <p>P.Craft is a crafting game. You wake up on a deserted island, and you have to survive. Gather materials and build your tools. Explore the area and find a cave. Will you find a way to escape the island ?</p> <p>Update : A new version, P.Craft Deluxe Edition has been released on Itch.io :<br /> <a href="https://nusan.itch.io/pcraft"><a href="https://nusan.itch.io/pcraft">https://nusan.itch.io/pcraft</a></a></p> <p>With a saving system, a boss and a few new items to discover.<br /> This new version use the multicartridge system in a complex way that is not yet compatible with the BBS, so I can't upload it here. You can however find the source .p8 files on the Itch.io page as well as binaries for windows, linux and mac.</p> <p>Controls :<br /> Button 1 (C/Z/N) : open inventory / cancel menu<br /> Button 2 (V/X/M) : use equiped item / valid menu</p> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/pcraft01.gif" alt="" /> <p>The game is heavily inspired by Notch's Ludum Dare entry &quot;Minicraft&quot; witch seemed a perfect idea for pico 8</p> <p>Quick start if needed :</p> <ul> <li>Start by punching one or two trees.</li> <li>Avoid zombies (water is you friend)</li> <li>Open your inventory and select the workbench</li> <li>Place the workbench on the ground</li> <li>Use the workbench to craft a wood haxe</li> <li>Select the wood haxe in your inventory</li> <li>Cut trees until you can craft a sword on the workbench</li> <li>Search the island until you find a cave opening</li> <li>You can pickup your workbench again to move it near the cave</li> <li>Your goal now is to craft a boat</li> <li>Be carefull</li> </ul> <p>Old version :<br /> <table><tr><td> <a href="/bbs/?pid=19680#p"> <img src="/bbs/thumbs/pico19679.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=19680#p"> P.Craft</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=19680#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=3200 https://www.lexaloffle.com/bbs/?tid=3200 Sun, 10 Apr 2016 09:49:07 UTC The Serpent's Shadow <p> <table><tr><td> <a href="/bbs/?pid=17761#p"> <img src="/bbs/thumbs/pico17759.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=17761#p"> The Serpent's Shadow</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=17761#p"> [Click to Play]</a> </td></tr></table> </p> <p>Get all rabbits and grow a good amount of grass to go to the next level.</p> <p>Parts of lit areas can be turned to grass once isolated from light using your snake body.</p> <p>Controls :<br /> Arrow keys : move snake</p> <p>There are rabbit holes, so lure rabbits outside by growing grass around them.<br /> Yhen eat them, it will make the snake grow</p> <p>Moles are your enemies, mainly because they have lasers. Don't let them see you so you can eat them</p> <p>This game has been made in 48 hours during Ludum Dare 34<br /> You can find a Timelapse made during the LudumDare, followed by a bit of gameplay :<br /> <object width="640" height="400"><param name="movie" value="https://www.youtube.com/v/9aJZc0cdk6U&hl=en&fs=1&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="https://www.youtube.com/v/9aJZc0cdk6U&hl=en&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="400"></embed></object></p> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/LD34/LD_03.gif" alt="" /> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/LD34/LD_04.gif" alt="" /></p> https://www.lexaloffle.com/bbs/?tid=2893 https://www.lexaloffle.com/bbs/?tid=2893 Sun, 13 Dec 2015 19:29:56 UTC Rain Culture <p> <table><tr><td> <a href="/bbs/?pid=17210#p"> <img src="/bbs/thumbs/pico17207.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=17210#p"> Rain Culture</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=17210#p"> [Click to Play]</a> </td></tr></table> </p> <p>Rain Culture :<br /> Use your divine powers to help nature grow back.<br /> Your main tool is a rain cannon and severall kind of mirrors to redirect water over the plants.</p> <p>Controls :<br /> C or Z to take control or release the nearest tool<br /> Outside a tool : use arrow keys to move your character<br /> Inside a tool : use arrow keys to rotate the tool</p> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/raincult_1.gif" alt="" /><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/raincult_2.gif" alt="" /></p> <p>That jam was prety cool, even if I only worked two days on it. I tryed to add another mecanic, where fire could spread from leaves to leaves, but it was too random so I removed it.</p> https://www.lexaloffle.com/bbs/?tid=2837 https://www.lexaloffle.com/bbs/?tid=2837 Sun, 29 Nov 2015 18:07:09 UTC Space Limit <p> <table><tr><td> <a href="/bbs/?pid=16315#p"> <img src="/bbs/thumbs/pico8_spacelimit-0.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=16315#p"> spacelimit</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=16315#p"> [Click to Play]</a> </td></tr></table> </p> <p>Space limit is a little 3D demo (so no gameplay yet) inspired by Kerbal Space Program.<br /> If it's too slow in your browser, try directly in Pico8</p> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/spacelimit_1.gif" alt="" /><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/spacelimit_2.gif" alt="" /><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/spacelimit_3.gif" alt="" /></p> <p>There is nothing in the sprite/tile data, the code is (realy) messy and triangles can only be monochrome.<br /> For this project, I made a blender exporter (based on the .ply one) that can be found <a href="http://lezanu.fr/Pico8/io_mesh_pico.7z">here</a></p> <p>I also made a mesh converter to vertex/index string (one byte per location, scale limited to 2.5 around origin) to import in Pico 8. I made it in Javascript (If some people want it, I could port it in the blender exporter instead).<br /> <a href="http://lezanu.fr/Pico8/Convert_obj.html">here</a> is the converter from obj, and <a href="http://lezanu.fr/Pico8/Convert_pico.html">here</a> for my custom format from blender.</p> <p>Have fun !</p> https://www.lexaloffle.com/bbs/?tid=2734 https://www.lexaloffle.com/bbs/?tid=2734 Fri, 06 Nov 2015 14:51:29 UTC Pico 8 webgl display with Three.js <p>The new export option is amazing, so I started messing with three.js (a webgl library) to use it with Pico 8.<br /> And it works, so I made a simple proof of concept by drawing the pico 8 output in a sort of cathodic display style. The next step will be to import a 3D model of a computer screen or maybe an arcade cabin. So we can finaly see this great fantasy console.<br /> Feel free to use the source code and mess with it. You can change your game just by replacing the game .js by the one exported from Pico 8.</p> <p><a href="http://lezanu.fr/Pico8/HTMLCabin/">Here is the page</a><br /> <a href="http://lezanu.fr/Pico8/HTMLCabin/src.zip">Here is the source code</a></p> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/HTMLCabin/PicoThree.jpg" alt="" /><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/HTMLCabin/PicoThree2.gif" alt="" /></p> https://www.lexaloffle.com/bbs/?tid=2555 https://www.lexaloffle.com/bbs/?tid=2555 Sat, 03 Oct 2015 05:51:30 UTC Cyclo 8 <p> <table><tr><td> <a href="/bbs/?pid=14642#p"> <img src="/bbs/thumbs/pico25047.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=14642#p"> Cyclo 8</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=14642#p"> [Click to Play]</a> </td></tr></table> </p> <p>Last update : made a better lamp in front of the bike</p> <p>Here is my first Pico 8 game, it's a &quot;physic&quot; bike game, a bit like the &quot;Trials&quot; series. I tried some experimental &quot;distance field&quot; collision and it work prety well.</p> <ul> <li>7 levels of increassing difficulty</li> <li>15 collectibles per levels</li> <li>Time clock for speedruns</li> <li>Deathless is possible</li> <li>Deathless with all collectibles is possible but is insanely difficult</li> </ul> <p>Controls :<br /> <strong><br /> up = gas<br /> down = brake<br /> left-right = rotate the bike<br /> c to flip bike<br /> v to retry</strong></p> <p><img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Cyclo_10.gif" alt="" /> <img style="margin-bottom:16px" border=0 src="http://lezanu.fr/Pico8/Cyclo_11.gif" alt="" /></p> <p>Old versions :<br /> <table><tr><td> <a href="/bbs/?pid=14642#p"> <img src="/bbs/thumbs/pico14970.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=14642#p"> Cyclo 8</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=14642#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=14642#p"> <img src="/bbs/thumbs/pico14712.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=14642#p"> Cyclo 8 0.8</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=14642#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=14642#p"> <img src="/bbs/thumbs/pico14705.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=14642#p"> Cyclo 0.7</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=14642#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=14642#p"> <img src="/bbs/thumbs/pico14660.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=14642#p"> Cyclo 8 0.6</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=14642#p"> [Click to Play]</a> </td></tr></table> <br /> <table><tr><td> <a href="/bbs/?pid=14642#p"> <img src="/bbs/thumbs/pico14641.png" style="height:256px"></a> </td><td width=10></td><td valign=top> <a href="/bbs/?pid=14642#p"> Cyclo 8 0.5</a><br><br> by <a href="/bbs/?uid=11048"> NuSan</a> <br><br><br> <a href="/bbs/?pid=14642#p"> [Click to Play]</a> </td></tr></table> </p> https://www.lexaloffle.com/bbs/?tid=2521 https://www.lexaloffle.com/bbs/?tid=2521 Sat, 26 Sep 2015 15:27:27 UTC