<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="http://kokoscript.com/atom.xml" rel="self" type="application/atom+xml" /><link href="http://kokoscript.com/" rel="alternate" type="text/html" /><updated>2024-11-02T14:49:23-05:00</updated><id>http://kokoscript.com/atom.xml</id><title type="html">kokoscript</title><subtitle>posts of fox (girl)</subtitle><entry><title type="html">A Year of WordHopper - Development Retrospective</title><link href="http://kokoscript.com/2024/015.html" rel="alternate" type="text/html" title="A Year of WordHopper - Development Retrospective" /><published>2024-11-02T00:00:00-05:00</published><updated>2024-11-02T00:00:00-05:00</updated><id>http://kokoscript.com/2024/015</id><content type="html" xml:base="http://kokoscript.com/2024/015.html"><![CDATA[<p><em>Editor's note: I originally started writing this as part of a larger 1-year anniversary celebration for WordHopper. However, a combination of less free time + suddenly many more side projects to take care of pushed the anniversary out a bit, to the point where things will have to happen piecemeal for now. I decided the retrospective dev journal would be the best place to start, just to dust off the blog a bit :)</em></p>

<hr />

<p>At the start of 2023, I decided to set a resolution for myself: release a game project. Didn't matter how big or small, it had to be <em>something</em>.</p>

<p>I rarely do New Year's resolutions. Hell, I didn't set one this year. I'd set similar gamedev-related goals for myself in years past, but they never stuck. So I gave up trying to hold myself to anything. And yet, little did I know I'd eventually end up fulfilling my 2023 resolution when I set it. Just... not in the way I expected, for better or worse.</p>

<hr />

<p>The story with <em>WordHopper</em> starts pretty early on in 2020, just before the pandemic. It was the start of a new semester at university, and I was between game projects; I'd just shelved the <em>FoxGame</em> prototype, and <em>Endless Attack REMIX</em> was out another year, so I wasn't really working on... well, much of anything.</p>

<p>Around that time, I was also revisiting PopCap's <em>Bookworm</em>, a childhood favorite game of mine. Upon doing further research, I was displeased to see that it'd been erased from all storefronts by this point. EA (who now owns PopCap) even refuses to acknowledge the game's existence! Noticing the severe lack of word games that weren't your typical ad-riddled mobile game sludge, I decided to take matters into my own hands.</p>

<p>Recognizing the appeal in <em>Bookworm's</em> core "letter stringing" mechanic, I started work on a new game built around the same concept. In place of <em>Bookworm's</em> puzzle-like, slower gameplay, I wanted to aim for something more arcade-like, with a faster pace of play. I also decided to eschew <em>Bookworm's</em> staggered grids in favor of a more traditional grid.</p>

<p>And thus, <em>WordHopper</em> was born.</p>

<p><img src="/blogImg/015/wh-proto.png" alt="Screenshot from an early prototype of WordHopper" /></p>

<p>And promptly died two days later.</p>

<p>I'm... not quite sure why I shelved it so quickly! I definitely had the skills needed to finish it in the engine I was working with, and was looking for a small, easier project I could reasonably see myself completing. And yet, I <em>still</em> lost interest. Oh well, so it goes...</p>

<hr />

<h3 id="off-the-shelf">Off the shelf</h3>

<p>The story picks back up at the start of 2023, when I set that resolution for myself. The plan I had in mind would be to finish the first "chapter" of the RPG project I was working on at the time (<em>thyme</em>), and release that as a demo by the end of Summer. Failing that, I decided plan B would involve picking <em>WordHopper</em> back up, finishing it in a few months, and releasing it on mobile- just to have something to point towards if I wanted to look for work in the games industry (spoiler alert, this didn't happen).</p>

<p>Development on <em>thyme</em> progressed smoothly for a bit, but schoolwork and cancer in the family slowed things down tremendously. All I got done was a near-complete dialog language interpreter, before Summer started and I found myself fighting with the engine to get things working in the way I wanted (namely, asset loading/memory management). Ultimately I decided it was more work than it was worth, so <em>thyme</em>'s fate was up in the air by this point.</p>

<p>Not too long after, I learned of an upcoming DOS game jam. This was where the script completely flipped. With <em>thyme</em> starting to languish in development hell, pivoting to a smaller project for the time being sounded like a good idea. And, having built up a solid (if a bit lacking) foundation for DOS development with <em>REMIX</em>, a DOS-themed jam was a perfect fit for me. So, I decided to take a walk, and brainstormed for a bit.</p>

<p>One of the first ideas I had for the jam was an overhead action-puzzle dodgeball sort of game, where the player would pick up and throw objects to activate things and take down enemies.But because the idea was a bit too open-ended, would take time to solidify the design, and would require <em>a lot</em> more work on the engine, I decided to move on. Another idea I had was to bring back and flesh out <em>Gap Jumper</em>, an endless runner prototype I made way back in 2013. But I wasn't too enthusiastic about that, so again, I moved on.</p>

<p>Eventually, things clicked; <em>WordHopper</em> was my plan B anyway, the design work was already complete, and wouldn't require much from the engine (in fact, I ended up <em>removing</em> more from the engine than I added); why not rework it as a DOS game?</p>

<p>At 7:23 PM on May 28, 2023, I copied <em>REMIX's</em> source over to a new directory, and began gutting the game-specific parts. <em>WordHopper</em> was my new "main" project, now under the codename <em>parsley</em>. Deadline was July 16; I had a month and a half to finish it. No sweat, right?</p>

<hr />

<h3 id="early-development">Early development</h3>

<p>The first few weeks of development were relatively uneventful, and admittedly a bit slow; I'd pretty much go about my day, and whenever I had the time, I'd spend an hour or two working on the game. Having learned my mistakes from prior projects where "set dressing" would take priority before the actual game, I decided to channel my focus on getting the game's design <em>fully implemented</em> before touching any of the visual elements. So for a while, <em>WordHopper</em> looked like this:</p>

<p><img src="/blogImg/015/a1.png" alt="Screenshot from a later prototype of WordHopper, now a DOS game. The letter grid is represented by pink squares with letters in them." /></p>

<p>This was after the first day of development; the script to generate source files for the game's dictionary and RNG weight values, playfully titled <em>Dictomorph</em>, had just been written. Over the next couple days, focus shifted to the game's "failsafe" system, whereby the game would choose a set of "guaranteed" words to hide in the field (in the event RNG fails to produce anything usable to the player).</p>

<p><img src="/blogImg/015/a2.png" alt="Screenshot from a later prototype of WordHopper. A start letter is picked for the center of the grid (T), with failsafe word &quot;leaves&quot; picked and placed in the top left of the grid." /></p>

<p>The next day, I added a generic stack to the engine in order to support my next targets: paths, player movement, and word validity. Any time the player were to make a move, the coordinates and corresponding letter from the field were pushed onto a stack, and when it came time to render the path or check if the entered word is valid, that stack would be traversed.</p>

<p><img src="/blogImg/015/a3.png" alt="Screenshot of WordHopper just before the test field was replaced, showing a letter path and stack." /></p>

<p>It seems like I took a week-long break after implementing stacks, but from there I was able to knock out a few things one after another over the next few days: the aforementioned word validity checks, a (basic) gameover screen, scoring, an additional "jump" control scheme (numpad was the sole control scheme until then), and the ability to pause the game.</p>

<p>Somewhere in the middle of implementing those, I considered the core game "complete" enough to the point where I marked the first alpha version, v1.00a. I gave a build of this version to my girlfriend to get some ideas for the game's music (I somehow knew in advance I wouldn't have time to write it), and left her to it. This was followed by v1.10a, before it was finally time for me to move onto set dressing (or as I called it at the time, the "fancy field").</p>

<p>So for those keeping track, it was now June 15. I'd spent a third of the jam getting the core game together, which wasn't <em>bad</em>. But for what I had in mind, there was still much more work left.</p>

<hr />

<h3 id="fancy-field">Fancy field</h3>

<p>This was probably the most interesting part of the game's alpha cycle, both in terms of getting to iterate quicker thanks to the base gameplay already being in place, but also because I completely Fucking Forgot what my target hardware was.</p>

<p><img src="/blogImg/015/a4.png" alt="Screenshot of WordHopper with a near-final version of the field. The path doesn't have connecting segments, only circles drawn over visited letters." /></p>

<p><img src="/blogImg/015/a5.png" alt="Screenshot of WordHopper with a near-final version of the field. Path segments are in place, but diagonals produce a wide checkerboard pattern instead." /></p>

<p>Despite a minor hiccup in getting diagonal path segments drawn (seen above), it didn't take long to get the fancy field draw routines implemented. So, I decided to mark it as v1.20a, went to test on real hardware (a Pentium 75 system), and...</p>

<p><img src="/blogImg/015/pain.jpg" alt="WordHopper running on real hardware. The word &quot;pain&quot; is on the letter path, and a framerate graph in the upper-right of the screen reports the game running at 14 FPS." /></p>

<p>Oh... yeah that's not great. That, combined with a bug I'd just discovered where some words were mistakenly always deemed invalid ("dead" was one) meant I had both a performance <em>and</em> a logic issue to take care of. I decided to start with addressing the fancy field performance issues first.</p>

<p>Unfortunately, the first thing I did was change the pattern of the path when it's valid from a diagonal stripe to a horizontal stripe. The way I implemented the diagonal pattern was pretty naive; I was just drawing diagonal lines across the <em>entire</em> screen in a blended mode (such that they'd only end up on the path). Aside from how much wasted effort this is for the CPU, it's not cache-friendly at all; so I switched to horizontal lines. I also limited the pattern to a bounding rect based on the min/max coordinates found in the player's path. In hindsight, the diagonal pattern could've just as easily been implemented using calls to <code class="language-plaintext highlighter-rouge">hline</code> and a bounding rect instead, and had similar performance.</p>

<p>The next performance improvement involved redoing how horizontal/vertical path segments were drawn. For <em>whatever</em> reason, I was using groups of calls to allegro's <code class="language-plaintext highlighter-rouge">fastline</code>- much like I was with diagonal segments and the old diagonal pattern- which, contrary to what the name implies, <em>aren't</em> fast if all you need is a horizontal line. And, as it just so happened, I was using these <code class="language-plaintext highlighter-rouge">fastline</code>s to draw a rectangle... which would be better served by <code class="language-plaintext highlighter-rouge">fillrect</code>, which essentially does several <code class="language-plaintext highlighter-rouge">hline</code>s under the hood.</p>

<p>The calls to <code class="language-plaintext highlighter-rouge">fastline</code> for the diagonal path were also switched out for a single call to <code class="language-plaintext highlighter-rouge">polygon</code>. At first I tried using <code class="language-plaintext highlighter-rouge">hlines</code> for this, but it didn't look great as the shape needed to follow the tangent of the circles that made up the "nodes" of the path. This, plus some culling on off-screen letter sprites and replacing the path circles with sprites, led to the game running at a full 70 FPS on real hardware. Phew!</p>

<p>And so, freed from performance issues, I wrote a placeholder title screen, added in additional difficulties, "zen" mode, aaaaand...</p>

<p>Forgot to work on the game.</p>

<hr />
<h3 id="july-8">July 8</h3>

<p>8 days before the jam deadline.</p>

<p>The game was technically still in alpha. Despite trying to continue work on it, I'd been tied up in prior obligations the preceding few days. All I could do was make a checklist of what needed to be done. Whether I liked it or not, I was about to self-impose my first crunch.</p>

<p><img src="/blogImg/015/checks10.jpg" alt="Checklist: get basic menu working, take inventory of memory allocation across gamestates (do last?), implement sound effects, implement music, implement grid slide animation, implement in/out transition, implement bnuuy, implement typing controlscheme, jump control scheme aim hint, make tutorial gamestate, prettify title, credits gamestate, fix certain words being invalid (add checks to dictomorph), alternate weighting method? (lowest possible priority), bigger dictionary, filter dictionary, readme and other docs n stuff, demo version? (small dict - filter that, 10 word limit, no HS, no modes aside from timed easy), packaging for win/mac/linux, packaging for web. only a few of these are checked off." /></p>
<h5 id="the-checklist-on-the-10th-imagine-everything-being-unchecked">The checklist on the 10th; imagine everything being unchecked</h5>

<p>The following week essentially boiled down to chipping away at the checklist, so I'll try to breeze through what happened when.</p>

<ul>
  <li>On the 8th, I put together the tutorial and credits screens.</li>
  <li>hold on im going to break out of the list for this one</li>
</ul>

<p>On the 9th, I finally tracked down the source of the "invalid valid words" bug. Initially, I thought this was an issue with the binary search done over the game's dictionary. When stepping through the search routine with a bugged word, I noticed one of the comparisons threw the "tightening" of the search bounds away from the target word. Upon looking at which words were being compared, I found my answer: the dictionary wasn't in sorted order.</p>

<p>Because the origin word list contained words with accents, these accented letters were swapped with their non-accented variants in order to work with the design of the game. Although that word list was sorted in alphabetic order, words with accented letters were outliers; they didn't follow the same order had the accents been removed.</p>

<p>Because Dictomorph didn't verify the order of the word list, the game would end up with a "mostly" sorted dictionary. And because the theory behind binary search relies on a sorted dataset, well...</p>

<p>Needless to say, I chucked the word list into <code class="language-plaintext highlighter-rouge">sort</code>, re-Dictomorphed it, recompiled, and the previously invalid words became valid. An extra check was then added to Dictomorph to prevent that from happening again.</p>

<ul>
  <li>On the 10th, I took care of some memory management between game states (essentially freeing sprites when they're about to become unused), a bit of high score logic, and implemented the calls to play/loop the music (and probably tweaked the MIDIs my GF gave me, too).</li>
  <li>The 11th was dedicated to the word entry transition animation. Working with tweens in my engine is kind of a nightmare, hence why it took a while...</li>
  <li>On the 12th, I wrote the driver setup screen and the save file ("profile") system (mostly ported from <em>REMIX</em>). I then had to tie the latter into the game for things like high scores, zen longest word, and driver settings.</li>
  <li>The 13th was largely a polishing day. The "slide" animation for the field was added, the "jump" control scheme was given a reticle, sound effects were sprinkled in, the gameover screen was given its little animation sequence, and the rabbit sprite was <em>finally</em> added into gameplay.</li>
</ul>

<p><img src="/blogImg/015/checks14.jpg" alt="Same checklist as prior, with many more items checked off" /></p>
<h5 id="the-checklist-on-the-14th-down-to-the-wire">The checklist on the 14th, down to the wire</h5>

<p>Aside from having to track down that sorting bug, development was going rather uneventfully; just faster and more stressful than I'd anticipated. That was until the 14th, when I discovered an <em>amazing</em> exploit: <em>the double-hop</em>.</p>

<p>I suddenly realized I was handling control schemes separately from each other. In other words, each frame I was handling the actions for two different input methods, without them canceling each other out. Noticing this, I conducted an experiment: jumping by pressing space and pressing a numpad key on the same frame. It took a few tries, but lo and behold:</p>

<p><img src="/blogImg/015/b1.png" alt="Screenshot of a near-final version of WordHopper. An invalid move has been made (jumping 2 across, 1 down)" /></p>

<p>I was immediately faced with a dilemma: do I keep the exploit in as a fun technique? Or do I patch it out, seeing as the rest of the game was completely<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> bug-free? Instead, I went with a middle-ground: left it in, but with some devastating consequences. If you have a copy of the game, go ahead and try it out (maybe not in zen mode, though) <img class="emj" src="/img/emoji/tooHappy.gif" /></p>

<p>The last few days were, once again, uneventful. On the 14th, I added in the exploit check and added the "warping field" effect to the word entry transition. On the 15th, I added in the "type" control scheme (despite personally finding it a pretty bad way to play), and filtered out the valid word list. On the 16th- the final day- I quickly put together a "proper" title/menu screen (including the rabbit vector used in the promo art/title), sprinkled in some <code class="language-plaintext highlighter-rouge">#ifdef</code>s to make a demo version, and zipped up the builds. 38 minutes before the deadline, I fulfilled my new year's resolution: <em>WordHopper</em> was released.</p>

<p><img src="/blogImg/015/devsheet1.jpg" alt="Development notes and drawings for WordHopper, primarily failsafe placement and animation notes" />
<img src="/blogImg/015/devsheet2.jpg" alt="Development notes and drawings for WordHopper, primarily path drawing and tile culling" /></p>
<h5 id="single-piece-of-scratch-paper-i-used-during-development-some-plaintext-notes-were-also-taken-but-those-are-lost-to-time">Single piece of scratch paper I used during development; some plaintext notes were also taken, but those are lost to time...</h5>

<p>The following morning, I pushed the announcement posts out to my socials, and stepped outside for the first time in over a week. On that walk, I learned the macOS version was broken for literally everyone. Not the best thing to hear after I thought I was done...</p>

<p>Because of course, I wasn't. It was time for v2.00, along with a new deadline: September 9, to coincide with that year's VCFMW.</p>

<hr />
<h3 id="round-two">Round Two</h3>

<p>I don't really remember much of v2.00's development process, aside from the very last day. I was starting from an already-established base, had a checklist already put together to figure out what I wanted to do for the update (based on player feedback and ideas cut from v1.00), and had a bit more time to finish it. Naturally, I used the first couple of weeks to take a break from working on the game.</p>

<p>There was a bit of a thorn in my side this round; eventually, I'd have to manage university at the same time, seeing as my final semester started mid-way through August. Cue me frantically cobbling the rest of the update together on my laptop during classes and on the train for a few weeks before the deadline (still got straight-As that semester, so no harm done!). There was even a point where I was working on some of the mouse-related code from my phone over SSH.</p>

<p>That being said, I was ramming <em>straight</em> into the deadline yet again, this time a little harder than before. It was about 4 AM when I decided to officially mark v2.00 for release, and I was supposed to wake up at 6 that morning. As a result, v2.00 released in a <em>tiny bit</em> of an unpolished state. But, nevertheless, it was done, and I was able to enjoy tabling for the first time at VCFMW. Because yes, <em>WordHopper</em> would be demoed there!</p>

<p><img src="/blogImg/015/table.jpg" alt="WordHopper on display at a convention table" /></p>

<p>It was pretty fun getting to watch people play the game firsthand. I'd never conducted any playtests before (and hadn't for v1.00), so watching people play something I'd spent the last few months of my life on was kind of fascinating (even if I didn't take notes and ultimately didn't get much out of it).</p>

<p>And so, with VCFMW coming to a close and me being a perfectionist, I got to work on v2.01 straight after the show. Perfect timing too, because I learned I had something important to address:</p>

<p><img src="/blogImg/015/comment.png" alt="itch.io comment: &quot;Zen mode just let me play one round and then would exit back to DOS prompt.&quot;" /></p>

<p>The game was crashing on Windows XP.</p>

<p>Throughout development, I never thought to test the DOS version of the game under Windows 2000/XP, which both ship with a DOS emulator of their own called NTVDM. I kind of assumed that anyone wanting to play under XP would do so under DOSBox, and because no crash reports came in for v1.00, I had no reason to think anything of it. But, after pulling out my trusty ThinkPad R40 and trying out zen mode, I could indeed reproduce the crash.</p>

<p>Since I was travelling between home and my dorm, I couldn't actively debug the issue (my backpack could only carry so much). But, from the couple of weekends where I could go through a bisect of the game's code, I was able to find which commit introduced the crash. Oddly enough, the game was crashing on a call to <code class="language-plaintext highlighter-rouge">realloc</code> that dealt with updating the longest zen word, which had sat there unchanged since v1.00. I even tried testing v1.00 under NTVDM, and it was completely fine. So, I decided to try swapping out the <code class="language-plaintext highlighter-rouge">realloc</code> for a <code class="language-plaintext highlighter-rouge">malloc</code>, and that got past the crash.</p>

<p><img src="/blogImg/015/ntvdmdiff.png" alt="Diff for replacing a call to realloc with alloc instead" /></p>

<p>Obviously this wasn't an ideal fix since it would leak memory (though, by small amounts); that's a bug by definition, even if it's invisible. After spending some time to fix the things I'd originally intended to fix for v2.01, I came back to the NTVDM issue and eventually found the root cause. Or, rather, a step away from it.</p>

<p>During the bisect, I never checked to see if certain game settings would change the crashing behavior. At some point I was using a save file from my development copy, which had the sound drivers disabled, and I didn't see the crash happen. I didn't put two and two together at the time, and assumed it had something to do with a longest word already being in memory from the save file. But, after coming back to get rid of the leak, I tried disabling sound on a whim on the latest commit. No crash. Enabled it again, sure enough, it came back.</p>

<p>So, I decided to take the nuclear approach and just prevented the sound drivers from loading if the game detected it was running under NTVDM. There was no way I was delving into Allegro's sound driver code to figure out the real source of the issue. And besides, NTVDM's sound emulation <em>sucks</em>; MIDI events don't have good timing, and SB samples always play at a weird rate. I decided disabling sound outright was good enough because of this. It was either that or a leak.</p>

<p>With a bit of work afterwards, v2.01 was marked and released in a state I was finally happy with, and I decided to call the game complete at this point. No more updates aside from bug fixes, which... there were none (that I knew of, at least)!</p>

<hr />

<h3 id="no-more-updates-and-other-fun-jokes-you-can-tell-yourself">"No more updates" and other fun jokes you can tell yourself</h3>

<p>A month later, I got an idea for a potential leaderboard system. That was something <em>WordHopper</em> was sorely lacking; there wasn't much of a community, and no one was really comparing scores. Having a leaderboard would've solved this, but... how do you get scores from a DOS system to the internet? Sure, TCP/IP stacks exist, but not everyone wants to network their DOS systems. That's where I had an ace up my sleeve:</p>

<p><img src="/blogImg/015/post.png" alt="Post from me, Nov 8 2023: &quot;I know I've said I'm done with WH for the time being, but there's a part of me that really wants to add online leaderboards - though tbh I think putting QR codes in a DOS game would give some people a heart attack&quot;" /></p>

<p>Using QR codes as a one-way connection to the internet sounded like such a good plan. And, given phones have QR scanners baked into their OSes by this point, the flow I had in mind was perfect. Unfortunately, the more I wrote down plans for this system, the more complicated it got. Eventually, it got to the point where the amount of code I foresaw myself writing for it rivaled the amount I'd written for the game itself.</p>

<p><img src="/blogImg/015/gameover-qr.png" alt="Mockup screenshot of WordHopper's game over screen, with a QR code added for online score submission" /></p>

<p><img src="/blogImg/015/boards.png" alt="Screenshot of an in-progress leaderboard page on WordHopper's website" /></p>

<p><img src="/blogImg/015/sk-greeter.png" alt="Screenshot of an in-progress score entry page on WordHopper's website" /></p>
<h5 id="mockup-for-the-modified-gameover-screen-and-the-in-progress-leaderboardentry-pages">Mockup for the modified gameover screen, and the in-progress leaderboard/entry pages.</h5>

<p>Marshaling score data from a QR code into a scorekeeping server is easy, and if that was <em>all</em> I had to do, I'm sure v2.10 would've actually been out by now. In reality, most of the planning for this system dealt with guarding against the potential for cheated scores, and in the end, it still would've been trivial to reverse; just hook a debugger up to the game, or use something like Ghidra. Because writing reverse-resistant code is something I've never looked into, all I could do was add more server-side checks, increase the complexity of the score "ticket" sent to the server, and hope that it'd be enough.</p>

<p>By the time I realized there was nothing I could do to completely thwart cheated scores, I'd started getting burnt out. I reduced the scope of v2.10 to just an extra difficulty mode and some size/speed optimizations, but halfway through those, I finally lost steam, and postponed the update until sometime after the game's first anniversary.</p>

<p>Life events happened, <em>WordHopper</em> fell by the wayside, and that brings us to today. It's been a little over a year since the last update to the game was released (aside from the PPC OS X port).</p>

<h3 id="looking-back">Looking back</h3>

<p>I have a lot to thank <em>WordHopper</em> for. But, at the same time, I kind of resent it. Let's start with the good.</p>

<p>Depending on your definition of "complete", <em>WordHopper</em> was the first complete game project of mine. I'd been working on my own games for a decade prior to its release, but none of them ever came close to being a "finished" product; always small prototypes or art tests. I had a few small games release here and there, but they were either minuscule and derivative (<em>DOSWord</em>) or jokes (<em>Elite Frog</em>), so I hesitate to call them complete products.</p>

<p>Not only was <em>WordHopper</em> my first complete game, it was also my first <em>commercial</em> game. I don't really want to share numbers here, but for a $2 PWYW DOS word game, it made a decent chunk of change- not enough to live off of comparative to how much time I put in, but definitely a bit more than I was expecting. It was enough for me to get some fun things from that year's VCFMW and MFF.</p>

<p>All that being said... <em>WordHopper</em> was also the first time I'd experienced crunch for a game. Which was a bit startling, since for the past decade I'd grown to treat game development as a fun hobby, something like creating a painting only to solve a puzzle made from it. In the final days of v1.00 and v2.00's development, I'd work on the game for a few hours, only to be met with a deep gut feeling which I can only describe as some weird mix of anxiety, boredom, and nausea. I really shouldn't have been pushing myself that hard for a casual game jam with no competition, but hindsight is 2020... 3.</p>

<p><img src="/blogImg/015/gitgraph.png" alt="Graph of line changes to a git repo over time; the graph exponentially increases just before both major releases of the game" /></p>
<h5 id="graph-of-total-line-changes-to-the-game-over-time-visualizing-the-crunch-before-each-release">Graph of total line changes to the game over time, visualizing the crunch before each release</h5>

<p>Going back to something I mentioned a few times earlier, <em>WordHopper</em> was also always meant to be a "plan B", and not a true representation of the skills I'd developed over the past decade. That was supposed to be <em>thyme</em>. And yet, <em>WordHopper</em> <em>still</em> gets in the way of me starting work on larger-scale projects, as I have an obligation to finish up v2.10 (which has since evolved into a semi-engine update). Speaking of v2.10...</p>

<h3 id="and-looking-forward">...and looking forward</h3>

<p>This post is the first in a series of "anniversary celebrations" for <em>WordHopper</em>. The v2.10 update will be next, which will add an extra difficulty, graphical tweaks, and some rewrites of the codebase/engine. Alongside this, the game's soundtrack will be released; it'll contain renders of the game's tracks as played with a Sound Canvas soundfont and on real OPL3 hardware, as well as CD/"Red Book" versions designed for the soundtrack release.</p>

<p>Lastly, the game will go open source. The plan is to dual-license the game as a whole; the assets will still fall under "proprietary" territory, requiring a purchase of the game. However, I may lax this requirement in the future and release them under the same license as the code (likely some form of GPL).</p>

<p>More info will be posted as these releases happen.</p>

<h3 id="and-thats-it">And that's it!</h3>

<p>If you're one of the many that played <em>WordHopper</em> and stuck around to the end of this post, thank you. As much as I love to see people play the finished game, it makes me even happier when people express interest in its development. Talking about what makes the game tick (and the process it took to make it tick)- even though it's relatively small- is something I really enjoy. It's part of why I'm releasing the source as soon as I can.</p>

<p>I hope you all look forward to these upcoming celebrations!</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>v1.00 shipped with a really bad memory leak but shhhh let's not discuss that <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[Editor's note: I originally started writing this as part of a larger 1-year anniversary celebration for WordHopper. However, a combination of less free time + suddenly many more side projects to take care of pushed the anniversary out a bit, to the point where things will have to happen piecemeal for now. I decided the retrospective dev journal would be the best place to start, just to dust off the blog a bit :)]]></summary></entry><entry><title type="html">NEW RELEASE: WordHopper, for DOS</title><link href="http://kokoscript.com/2023/012.html" rel="alternate" type="text/html" title="NEW RELEASE: WordHopper, for DOS" /><published>2023-07-18T00:00:00-05:00</published><updated>2023-07-18T00:00:00-05:00</updated><id>http://kokoscript.com/2023/012</id><content type="html" xml:base="http://kokoscript.com/2023/012.html"><![CDATA[<p><img src="/blogImg/012/gameplay.gif" alt="a short gameplay gif of wordhopper, showing a rabbit hopping across a grid of letters, spelling out the word &quot;rabbit&quot;" /></p>

<p>Hey hey! Long time no see! Definitely haven't forgotten about this site. Nope.</p>

<p>A couple nights ago, I released my first commercial game, WordHopper! It's a little arcade-like word game, which is designed for Pentium-class PCs running DOS. I made it as part of a <a href="https://itch.io/jam/dos-games-june-2023-jam">game jam centering around DOS games</a>, and as a result I didn't post any development journals for this. I might do a retrospective one sometime in the future, but... Anyway!</p>

<p><a href="https://kokoscript.itch.io/wordhopper">You can buy it on its itch.io page</a>. MSRP is $2, with a minimum of $1- or, if you like what I've been doing all these years, now's the perfect time to pay extra! There's also a demo version available for download, if you'd like to try it out (either on real hardware or an emulator) before buying. You can also <a href="https://hare.kokoscript.com/demo/js/wordhopper-demo.html">play the demo online</a> (<strong>click in the DOSBox window as early as possible to avoid a loud sound</strong>- I'm working on fixing that)!</p>

<p><img src="/blogImg/012/screen1.png" alt="wordhopper title screen/menu" /></p>

<p><img src="/blogImg/012/screen2.png" alt="wordhopper screenshot; spelling the word &quot;game&quot; on a grid of letters" /></p>

<p><img src="/blogImg/012/screen4.png" alt="wordhopper screenshot; spelling out &quot;quiet&quot; on the grid of letters, aiming towards the last letter needed" /></p>

<p><img src="/blogImg/012/screen3.png" alt="wordhopper screenshot; spelling a very long word (minicomputer)" /></p>

<p>That's all for now, cya!</p>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Bought a horrible little device today</title><link href="http://kokoscript.com/2023/014.html" rel="alternate" type="text/html" title="Bought a horrible little device today" /><published>2023-05-12T00:00:00-05:00</published><updated>2023-05-12T00:00:00-05:00</updated><id>http://kokoscript.com/2023/014</id><content type="html" xml:base="http://kokoscript.com/2023/014.html"><![CDATA[<p><em>Originally posted to my cohost. Few edits may have been made where I saw fit, but I've kept these to a minimum. Note that this post was actually a series of posts made over a few days; breaks between posts are denoted by their headers.</em></p>

<p><img src="/blogImg/014/bastardDevice.jpeg" alt="the sandisk ibi, next to its box. it is a cylindrical white device" /></p>

<p>visited a thrift store with some friends today, and spotted this: a SanDisk "ibi" (ee-bee) networked photo/video storage Thing. you use some app (that'll <em>totally</em> still exist in 7 years) to access it. right off the bat, not interested seeing as setting up a NAS is just another Thursday night to me.</p>

<p>but what caught my eye, and what ultimately made me take the $20 plunge was the "2TB" marking on the back of the box. it also helped that it was still sealed (can you believe no one bought this?). I assumed it was just a standard SATA laptop disk plugged into some fancy board on the inside, and sure enough, it was.</p>

<p>curious, I attached the disk to my desktop, just to see what came on it from the factory. I was immediately bombarded with a shit ton of partitions; this is starting to sound familiar...<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p><img src="/blogImg/014/diskLayout.png" alt="gparted open and showing 24 different partitions on a 2TB disk" /></p>

<p>the story took a different turn once I started exploring the auto-mounted partitions. unlike the 4K media player, which had some bespoke sony OS on it, this one felt... a little bit familiar. then I saw the jars. it might not be a surprise to those familiar with WD's "My Cloud" range of horrible little devices, but it was to me: <strong>this runs android</strong>. an untouched version from (presumably) 2018.</p>

<p><img src="/blogImg/014/theJars.png" alt="file manager open to a folder on one of the disk's partitions, containing a bunch of jar files for various frameworks" /></p>

<p>originally I was going to use the disk as an extra backup for my NAS, but now I want to play with the device itself. I'm a bit clueless when it comes to android, but I'm sure there's some way to get in and root it- especially if it's an outdated install. first I'll have to figure out the best way to grab an image of the disk without wasting 2TB of space... maybe later</p>

<h3 id="small-update">small update</h3>

<p><img src="/blogImg/014/uhoh.png" alt="patch note for the ibi client. &quot;Onboarding experience: For users onboarding a new device, if the firmware is old, a critical security update will be installed on the device before the user can complete the device onboarding journey.&quot;" />
<img src="/blogImg/014/lmao.png" alt="Starting June 2023, Western Digital will no longer be supporting the ibi Desktop software." /></p>

<p>did a bit more searching and found some new info:</p>

<ul>
  <li>according to a reddit comment (the only one about the device beyond a consumer standpoint), it runs Android Things, not <em>full</em> Android. still, the core of it is there. curiously, they also mentioned that a later update <em>switched out the entire OS</em> for some other linux-based one. no specifics on what version or when. based on somewhat-recent events, I'm assuming WD doesn't readily provide old firmware updates, which makes searching for that specific update nigh-impossible for me. I don't even know if you can update the ibi to specific versions anyway, because...</li>
  <li>I had suspicions that connecting this thing to the internet would be a <em>bad</em> idea if I wanted to retain the ability to hack into it. my suspicions were proven correct once I found patchnotes for the ibi client; recent versions will forcibly update the thing before you can even finish setup.</li>
  <li>I'd like to change my "7 years" prediction to "5 years", because they're already getting rid of a client</li>
</ul>

<p>still haven't backed up the disk, will do so when I find the time :v</p>

<h3 id="board-inspection">board inspection</h3>

<p>while I wait for dd and gzip to do their thing (reading 2TB of 0s over USB takes some time!), I took a closer look at the hardware.</p>

<p><img src="/blogImg/014/boardFront.jpeg" alt="a circuit board with 3 relatively large surface-mount chips, USB and power ports, a heatsink, and a 4-pin header" /></p>

<p>at the heart of the ibi is a realtek RTD1295PB, a run-of-the-mill ARM-based 4K media player SOC. it looks like WD was already using them across their other NAS offerings, so that's how it ended up here. there's 1GB of RAM spread across 2 NT5CC256M16EP-EKs. under that heatsink is the wireless chipset; it's all under an unlabeled shield I didn't want to bother bending back. to the very left of it is a winbond 25Q80DVNI, an 8Mb SOIC8 flash chip. I think the clip I have should fit, and if so, I have the means to dump it (though, the contents are likely encrypted). not sure if it's wired to the SOC or the wireless chipset, as none of the traces going to it are visible.</p>

<p>curiously, there's a 4-pin header towards the left edge of the board. I noticed this the first time I had the ibi open; it doesn't plug into anything, so my best guess is that it's an interface to a UART. odd, considering those are typically left as pads (or... y'know... removed) on consumer tech like this.</p>

<p><img src="/blogImg/014/boardBack.jpeg" alt="backside of circuit board, with not much other than some resistors, a small IC, and a SATA port" /></p>

<p>the other side isn't as interesting; just some resistors, the SATA port, and an IC I didn't even bother looking up.</p>

<h3 id="backup-complete">backup complete!</h3>

<p><img src="/blogImg/014/ugh.png" alt="left: terminal showing a dd piped to gzip, which took 14 hours to read 2TB of data. right: the resulting 2.2GiB file" /></p>

<p>this shit took the same amount of time as downloading MapleStory over DSL</p>

<p>also, quick note on the 4-pin header: I'm starting to suspect it could be another USB connection, but one which allows the ibi to act as a peripheral instead of a host (as is the case with the outward-facing port). if that turns out to be true, I could probably get in and play around with adb or something. before I try powering it on however, I'll need to dump the flash. already getting flashbacks to libreboot. ugh.</p>

<p>if I have to install android studio at some point for this project, I'm going to be mad</p>

<h3 id="how-to-kill-an-ibi-the-hard-way">how to kill an ibi, the hard way</h3>

<p>I finally got around to dumping the flash; it ended up being pretty quick since I already had everything set up from my T420 libreboot process. most of the time was spent hooking my RPi up to a TV and confirming continuity on the clip.</p>

<p>I poked around the resulting 1MB ROM (thrice-verified) a bit, but I couldn't glean much info other than:</p>

<ul>
  <li>yes it contains the bootloader</li>
  <li>no it is not encrypted (at least, most of it appears not to be)</li>
  <li>maybe the ibi is locked to a specific drive on a firmware-level? (there's a SN in there that looks like it's for a WD drive, but it doesn't match the one that came with my ibi)</li>
  <li>the device's SN and MAC are just plaintext in the firmware. not sure if editing those and reflashing will have any effect, but they're there</li>
  <li>some debug utilities are available in the ROM, as indicated by the many strings I saw</li>
</ul>

<p>here's where I jumped the gun.</p>

<p>at this point I had two options: boot up the ibi and see if USB ethernet would work (on a router disconnected from the internet), or investigate the header to see if I could access those debug tools. I chose the latter, and put together a USB-A to 4-pin header abomination. I checked to make sure nothing was shorted, verified continuity on each of the pins, found which pin on the header corresponded to ground, and plugged it in. I had dmesg open to see what would happen when I did, and as it turns out: over-currents everywhere!</p>

<p>after quickly disconnecting the ibi, I connected it to AC to see if it had survived. I had it connected (without the disk) earlier to see what it would do, and normally it should sit doing nothing with a flashing LED. but after trying to connect it over USB, the LED never flashed. I even tried connecting the disk to see if power was still getting to the SATA port, but it never spun up. it's dead.</p>

<p>I was never good with electronics to begin with; I should've stayed in my lane and avoided bothering with the header.</p>

<p> </p>

<p>...on the bright side, I got a 2TB disk for $20</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>This originally linked to a Twitter thread I did a few years back, covering a hardware-locked Sony 4K media player I started to take a look at. <a href="/blogImg/014/mediaPlayerLayout.jpg">You can see its partition layout here</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="Reverse engineering" /><summary type="html"><![CDATA[Originally posted to my cohost. Few edits may have been made where I saw fit, but I've kept these to a minimum. Note that this post was actually a series of posts made over a few days; breaks between posts are denoted by their headers.]]></summary></entry><entry><title type="html">border-image-slice without border-image-slice</title><link href="http://kokoscript.com/2023/011.html" rel="alternate" type="text/html" title="border-image-slice without border-image-slice" /><published>2023-02-14T00:00:00-06:00</published><updated>2023-02-14T00:00:00-06:00</updated><id>http://kokoscript.com/2023/011</id><content type="html" xml:base="http://kokoscript.com/2023/011.html"><![CDATA[<style>
	.ex-container {
		position: relative;
		height: 150px;
		width: 200px;
		overflow: hidden;
	}
	.ex-top {
		background-image: url("/blogImg/011/tc.png");
		height: 32px;
	}
	.ex-topleft {
		background-image: url("/blogImg/011/tl.png");
		width: 32px;
		height: 32px;
	}
	.ex-topright {
		background-image: url("/blogImg/011/tr.png");
		position: absolute;
		top: 0;
		right: 0;
		width: 32px;
		height: 32px;
	}
	.ex2-center {
		position: absolute;
		top: 32px;
		bottom: 32px;
		width: 100%;
	}
	.ex2-centerleft {
		background-image: url("/blogImg/011/cl.png");
		position: absolute;
		top: 0;
		left: 0;
		width: 32px;
		height: 100%;
	}
	.ex2-centerright {
		background-image: url("/blogImg/011/cr.png");
		position: absolute;
		top: 0;
		right: 0;
		width: 32px;
		height: 100%;
	}
	.ex2-content {
		background-image: url("/blogImg/011/c.png");
		position: absolute;
		top: 0;
		bottom: 0;
		left: 32px;
		right: 32px;
		color: white;
	}
	.ex3-bottom {
		background-image: url("/blogImg/011/bc.png");
		position: absolute;
		bottom: 0;
		width: 100%;
		height: 32px;
	}
	.ex3-bottomleft {
		background-image: url("/blogImg/011/bl.png");
		position: absolute;
		bottom: 0;
		left: 0;
		width: 32px;
		height: 32px;
	}
	.ex3-bottomright {
		background-image: url("/blogImg/011/br.png");
		position: absolute;
		bottom: 0;
		right: 0;
		width: 32px;
		height: 32px;
	}
	.ex-overflow {
		overflow: auto;
	}
	.ex-scale {
		background-size: 100% 100%;
	}
	.ex-resize {
		resize: both;
	}
	.ex4-container {
		position: relative;
		height: 300px;
		width: 381px;
		overflow: hidden;
		resize: both;
	}
	.ex4-top {
		background-image: url("/img/nsWindow/tc.gif");
		height: 147px;
	}
	.ex4-topleft {
		background-image: url("/img/nsWindow/tl.gif");
		width: 614px;
		height: 147px;
	}
	.ex4-topright {
		background-image: url("/img/nsWindow/tr.gif");
		position: absolute;
		top: 0;
		right: 0;
		width: 60px;
		height: 147px;
	}
	.ex4-center {
		position: absolute;
		top: 147px;
		bottom: 37px;
		width: 100%;
	}
	.ex4-centerleft {
		background-image: url("/img/nsWindow/cl.gif");
		position: absolute;
		top: 0;
		left: 0;
		width: 8px;
		height: 100%;
	}
	.ex4-centerright {
		background-image: url("/img/nsWindow/cl.gif");
		position: absolute;
		top: 0;
		right: 0;
		width: 8px;
		height: 100%;
	}
	.ex4-content {
		background: white;
		position: absolute;
		top: 0;
		bottom: 0;
		left: 8px;
		right: 8px;
		color: black;
	}
	.ex4-bottom {
		background-image: url("/img/nsWindow/bc.gif");
		position: absolute;
		bottom: 0;
		width: 100%;
		height: 37px;
	}
	.ex4-bottomleft {
		background-image: url("/img/nsWindow/bl.gif");
		position: absolute;
		bottom: 0;
		left: 0;
		width: 139px;
		height: 37px;
	}
	.ex4-bottomright {
		background-image: url("/img/nsWindow/br.gif");
		position: absolute;
		bottom: 0;
		right: 0;
		width: 185px;
		height: 37px;
	}
</style>

<p>A few months ago, I did a massive rewrite to my site. This involved re-doing the layout and removing the use of most CSS3 features, sticking to the bare minimum properties whenever and wherever possible. Some time later, I began to brainstorm ideas for a potential redesign, which eventually culminated in the idea of adapting my <a href="/artwork/loopyDesktop.html">desktop setup</a> to HTML/CSS. However, there was <em>one problem:</em> how would I go about making the "content" window scalable?</p>

<p>Enter: <code class="language-plaintext highlighter-rouge">border-image-slice</code>. It performs some nine-slice scaling to a source graphic, which can then be applied to a bordered element. This is perfect for conveying something like a window graphic! Unfortunately, taking a peek at <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/border-image-slice#browser_compatibility" rel="noopener noreferrer" target="_blank">MDN's browser compatibility table</a> revealed that the property wouldn't work in any browser older than ~2012. Since I wanted to avoid yet <em>another</em> massive compatibility rewrite in the future, I had to figure out an alternative. So, here's what I came up with, as a step-by-step guide.</p>

<hr />

<p>First, let's begin with a <code class="language-plaintext highlighter-rouge">container</code> div, which will hold our sliced/scaled graphic and content:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;div class="container"&gt;
&lt;/div&gt;
</code></pre></div></div>
<p>Good job! I knew you could do it.</p>

<p>Now, we need to think about things a bit here. When it comes to nine-slice scaling, you've essentially got a 3x3 grid of images, and by extension, a 3x3 grid of "cells"; or, in our case, divs. So let's break it up by rows:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;div class="container"&gt;
	&lt;div class="top"&gt;
	&lt;/div&gt;
	&lt;div class="center"&gt;
	&lt;/div&gt;
	&lt;div class="bottom"&gt;
	&lt;/div&gt;
&lt;/div&gt;
</code></pre></div></div>
<p>You can probably see where I'm going with this. Let's get those columns in there:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;div class="container"&gt;
	&lt;div class="top"&gt;
		&lt;div class="topleft"&gt;&lt;/div&gt;
		&lt;div class="topright"&gt;&lt;/div&gt;
	&lt;/div&gt;
	&lt;div class="center"&gt;
		&lt;div class="centerleft"&gt;&lt;/div&gt;
		&lt;div class="content"&gt;
			slice and dice&lt;br&gt;dice and slice
		&lt;/div&gt;
		&lt;div class="centerright"&gt;&lt;/div&gt;
	&lt;/div&gt;
	&lt;div class="bottom"&gt;
		&lt;div class="bottomleft"&gt;&lt;/div&gt;
		&lt;div class="bottomright"&gt;&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">content</code> div will— and you'll never believe this— be where our content appears. Note that I've foregone adding <code class="language-plaintext highlighter-rouge">topcenter</code> and <code class="language-plaintext highlighter-rouge">bottomcenter</code> divs (for their respective cells of the 3x3 grid); this is for reasons that'll become apparent later, once the styles are added.</p>

<p>If the structure of these divs seems confusing, try to picture it like this:</p>

<p><img src="/blogImg/011/divHierarchy.png" alt="three layers. the bottom is a large square; the middle consists of 3 rows, totalling up to the size of that square; the top layer has smaller squares within those 3 rows." /></p>

<p>...at least it'll be laid out like this <em>after</em> applying the styles. But before we can start adding them, we'll need an example image to play around with. I propose this one:</p>

<p><img src="/blogImg/011/example-9slice.png" alt="a test image, with 9 distinct sections in a 3x3 grid." /></p>

<p>Since we're aiming to be compatible with old browsers, the use of <code class="language-plaintext highlighter-rouge">background-position</code>- and thus, spritesheets- might not be the best choice. Instead, let's break it up into individual images (it'll make things easier, anyway).</p>

<table style="border: none; max-width: 300px;">
	<tr>
		<td><img src="/blogImg/011/tl.png" /></td>
		<td><img src="/blogImg/011/tc.png" /></td>
		<td><img src="/blogImg/011/tr.png" /></td>
	</tr>
	<tr>
		<td><img src="/blogImg/011/cl.png" /></td>
		<td><img src="/blogImg/011/c.png" /></td>
		<td><img src="/blogImg/011/cr.png" /></td>
	</tr>
	<tr>
		<td><img src="/blogImg/011/bl.png" /></td>
		<td><img src="/blogImg/011/bc.png" /></td>
		<td><img src="/blogImg/011/br.png" /></td>
	</tr>
</table>

<p>The styles do the bulk of the work. Let's start with the container; here we pretty much just give it some arbitrary width and height. We also add <code class="language-plaintext highlighter-rouge">overflow: hidden</code> to stop things from breaking when there's more content inside the center, or if the container is resized too small (spoiler alert...! <img class="emj" src="/img/emoji/waah.gif" alt="fearful koko" />)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.container {
	position: relative;
	height: 150px;
	width: 200px;
	overflow: hidden;
}
</code></pre></div></div>

<p>Now let's go row-by-row, starting with the top.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.top {
	background-image: url("tc.png");
	height: 32px;
}
.topleft {
	background-image: url("tl.png");
	width: 32px;
	height: 32px;
}
.topright {
	background-image: url("tr.png");
	position: absolute;
	top: 0;
	right: 0;
	width: 32px;
	height: 32px;
}
</code></pre></div></div>
<p>The reason for the lack of <code class="language-plaintext highlighter-rouge">topcenter</code> and <code class="language-plaintext highlighter-rouge">bottomcenter</code> divs should now be apparent here: instead of adding an extra div, the <code class="language-plaintext highlighter-rouge">top</code> div will take care of our 'tc' graphic. The <code class="language-plaintext highlighter-rouge">height</code> property should match the height of the top-row graphics, too. For the top left, we specify the div's width and height to prevent it from shrinking to a size of 0x0. The top right div differs a bit, in that it needs some help sticking to the right side. We'll use <code class="language-plaintext highlighter-rouge">position: absolute</code> along with a <code class="language-plaintext highlighter-rouge">top</code> and <code class="language-plaintext highlighter-rouge">right</code> of 0 to do that.</p>

<p>You should see something like this by now:</p>

<div class="ex-container">
	<div class="ex-top">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex-center">
		<div class="ex-centerleft"></div>
		<div class="ex-content">
			slice and dice<br />dice and slice
		</div>
		<div class="ex-centerright"></div>
	</div>
	<div class="ex-bottom">
		<div class="ex-bottomleft"></div>
		<div class="ex-bottomright"></div>
	</div>
</div>

<p>Looks promising! Let's keep going, continuing on with the center row.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.center {
	position: absolute;
	top: 32px;
	bottom: 32px;
	width: 100%;
}
.centerleft {
	background-image: url("cl.png");
	position: absolute;
	top: 0;
	left: 0;
	width: 32px;
	height: 100%;
}
.centerright {
	background-image: url("cr.png");
	position: absolute;
	top: 0;
	right: 0;
	width: 32px;
	height: 100%;
}
.content {
	background-image: url("c.png");
	position: absolute;
	top: 0;
	bottom: 0;
	left: 32px;
	right: 32px;
	color: white;
}
</code></pre></div></div>
<p>This is where things start to get a bit messy.</p>

<p>This time, the row div— <code class="language-plaintext highlighter-rouge">center</code>— needs to be positioned using <code class="language-plaintext highlighter-rouge">absolute</code>. If we think about it, this is in relation to <code class="language-plaintext highlighter-rouge">container</code>, so we'll need 32px from the top and bottom (the height of the respective row graphics). The width also needs to be 100% of the container.</p>

<p>Much like like <code class="language-plaintext highlighter-rouge">topright</code>, the <code class="language-plaintext highlighter-rouge">centerleft/right</code> divs need some help sticking as well. Slap <code class="language-plaintext highlighter-rouge">position: absolute</code> onto them, fix them to the top of <code class="language-plaintext highlighter-rouge">center</code> with <code class="language-plaintext highlighter-rouge">top: 0</code>, then to their respective sides with <code class="language-plaintext highlighter-rouge">left: 0</code> and <code class="language-plaintext highlighter-rouge">right: 0</code>. The widths from the graphics need to be specified as well, so say it width me: <em>32px</em>. Then, add <code class="language-plaintext highlighter-rouge">height: 100%</code> to give them the same height as <code class="language-plaintext highlighter-rouge">center</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">content</code> div is a similar story, only this time we're pinning it to the top of <code class="language-plaintext highlighter-rouge">center</code>, offset from the left by the width of the centerleft graphic. Then we expand it to the bottom of <code class="language-plaintext highlighter-rouge">center</code> as well as to its very right, minus the width of the centerright graphic. The color property is just there to make our text a bit more legible.</p>

<p>So, how does it look now?</p>

<div class="ex-container">
	<div class="ex-top">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex2-center">
		<div class="ex2-centerleft"></div>
		<div class="ex2-content">
			slice and dice<br />dice and slice
		</div>
		<div class="ex2-centerright"></div>
	</div>
	<div class="ex-bottom">
		<div class="ex-bottomleft"></div>
		<div class="ex-bottomright"></div>
	</div>
</div>

<p>Closer! Just the bottom row, and we're done.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.bottom {
	background-image: url("/blogImg/011/bc.png");
	position: absolute;
	bottom: 0;
	width: 100%;
	height: 32px;
}
.bottomleft {
	background-image: url("/blogImg/011/bl.png");
	position: absolute;
	bottom: 0;
	left: 0;
	width: 32px;
	height: 32px;
}
.bottomright {
	background-image: url("/blogImg/011/br.png");
	position: absolute;
	bottom: 0;
	right: 0;
	width: 32px;
	height: 32px;
}
</code></pre></div></div>

<p>This should be easier for you to parse by now. The <code class="language-plaintext highlighter-rouge">bottom</code> div should stick to the bottom, have a height of 32px, and take up the full width of the container. Much like <code class="language-plaintext highlighter-rouge">top</code>, it should also handle our bottomcenter graphic. <code class="language-plaintext highlighter-rouge">bottomleft/right</code> need to stick to their respective corners, and once again <code class="language-plaintext highlighter-rouge">position: absolute</code> along with <code class="language-plaintext highlighter-rouge">bottom: 0</code> and <code class="language-plaintext highlighter-rouge">left/right: 0</code> will get us there. For the size of both, you know the drill.</p>

<p>With all that said and done, you should hopefully end up with something like this:</p>

<div class="ex-container">
	<div class="ex-top">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex2-center">
		<div class="ex2-centerleft"></div>
		<div class="ex2-content">
			slice and dice<br />dice and slice
		</div>
		<div class="ex2-centerright"></div>
	</div>
	<div class="ex3-bottom">
		<div class="ex3-bottomleft"></div>
		<div class="ex3-bottomright"></div>
	</div>
</div>

<p>And there it is: something that <em>vaguely</em> looks like it was done with <code class="language-plaintext highlighter-rouge">border-image-slice</code>, using basic CSS! Let's see if we can add more content to the center.</p>

<div class="ex-container">
	<div class="ex-top">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex2-center">
		<div class="ex2-centerleft"></div>
		<div class="ex2-content">
			slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />
		</div>
		<div class="ex2-centerright"></div>
	</div>
	<div class="ex3-bottom">
		<div class="ex3-bottomleft"></div>
		<div class="ex3-bottomright"></div>
	</div>
</div>

<p>Hm. I <em>definitely</em> added more than 4 lines there. No worries; adding <code class="language-plaintext highlighter-rouge">overflow: auto</code> to <code class="language-plaintext highlighter-rouge">content</code>'s style will fix that.</p>

<div class="ex-container">
	<div class="ex-top">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex2-center">
		<div class="ex2-centerleft"></div>
		<div class="ex2-content ex-overflow">
			slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />
		</div>
		<div class="ex2-centerright"></div>
	</div>
	<div class="ex3-bottom">
		<div class="ex3-bottomleft"></div>
		<div class="ex3-bottomright"></div>
	</div>
</div>

<p>Cool! And if you want to <em>scale</em> the center graphics instead of tiling them, you can add <code class="language-plaintext highlighter-rouge">background-size: 100% 100%</code> to the <code class="language-plaintext highlighter-rouge">top</code>, <code class="language-plaintext highlighter-rouge">bottom</code>, <code class="language-plaintext highlighter-rouge">centerleft/right</code>, and <code class="language-plaintext highlighter-rouge">content</code> divs. There's a bit of a caveat to this; because we skipped adding <code class="language-plaintext highlighter-rouge">topcenter</code> and <code class="language-plaintext highlighter-rouge">bottomcenter</code> divs for simplicity, the top and bottom sections will scale across the <em>whole</em> container, instead of between the left and right divs. Here's what that looks like, regardless:</p>

<div class="ex-container">
	<div class="ex-top ex-scale">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex2-center">
		<div class="ex2-centerleft ex-scale"></div>
		<div class="ex2-content ex-overflow ex-scale">
			slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />bro i am so tired of slicing and dicing. i need a nap
		</div>
		<div class="ex2-centerright ex-scale"></div>
	</div>
	<div class="ex3-bottom ex-scale">
		<div class="ex3-bottomleft"></div>
		<div class="ex3-bottomright"></div>
	</div>
</div>

<p>You could probably fix that with what you've learned here, though <img class="emj" src="/img/emoji/wink.gif" alt="winking koko" /></p>

<p>As a cool added bonus, you can add <code class="language-plaintext highlighter-rouge">resize: both</code> to <code class="language-plaintext highlighter-rouge">container</code>'s style to allow it to be resized from the bottom right corner. It's what I did to allow the content window to scale.</p>

<div class="ex-container ex-resize">
	<div class="ex-top ex-scale">
		<div class="ex-topleft"></div>
		<div class="ex-topright"></div>
	</div>
	<div class="ex2-center">
		<div class="ex2-centerleft ex-scale"></div>
		<div class="ex2-content ex-overflow ex-scale">
			slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />slice and dice<br />dice and slice<br />ok that's enough. no longer in my slicing arc. not feeling very dicecore either
		</div>
		<div class="ex2-centerright ex-scale"></div>
	</div>
	<div class="ex3-bottom ex-scale">
		<div class="ex3-bottomleft"></div>
		<div class="ex3-bottomright"></div>
	</div>
</div>

<p>Despite its complexity, this technique <em>does</em> have a tiny advantage over <code class="language-plaintext highlighter-rouge">border-image-slice</code>, in that the widths of each graphic can be independent. Take the window graphics used for my site as an example:</p>

<table style="border: none; max-width: 100%;">
	<tr>
		<td><img src="/img/nsWindow/tl.gif" /></td>
		<td><img src="/img/nsWindow/tc.gif" /></td>
		<td><img src="/img/nsWindow/tr.gif" /></td>
	</tr>
	<tr>
		<td><img src="/img/nsWindow/cl.gif" /></td>
		<td></td>
		<td><img src="/img/nsWindow/cl.gif" /></td>
	</tr>
	<tr>
		<td><img src="/img/nsWindow/bl.gif" /></td>
		<td><img src="/img/nsWindow/bc.gif" /></td>
		<td><img src="/img/nsWindow/br.gif" /></td>
	</tr>
</table>

<p>Notice how the graphics used in the left and right columns have differing widths; this normally isn't possible with typical nine-slice scaling methods! Putting it all together, we get:</p>

<div class="ex4-container">
	<div class="ex4-top">
		<div class="ex4-topleft"></div>
		<div class="ex4-topright"></div>
	</div>
	<div class="ex4-center">
		<div class="ex4-centerleft"></div>
		<div class="ex4-content">
			<img class="emj" src="/img/emoji/tooHappy.gif" alt="smug koko" /><img class="emj" src="/img/emoji/tooHappy.gif" alt="smug koko" /><img class="emj" src="/img/emoji/tooHappy.gif" alt="smug koko" />
		</div>
		<div class="ex4-centerright"></div>
	</div>
	<div class="ex4-bottom">
		<div class="ex4-bottomleft"></div>
		<div class="ex4-bottomright"></div>
	</div>
</div>
<p>Now the magic behind my site is ruined... <img class="emj" src="/img/emoji/waah.gif" alt="fearful koko" /></p>

<hr />

<p>And there you have it: <code class="language-plaintext highlighter-rouge">border-image-slice</code> without <code class="language-plaintext highlighter-rouge">border-image-slice</code>. If you enjoyed this article or found it useful, I don't take any donations <em>but</em> you can observe <a href="/artwork/by-cat/fursona.html">my fursona</a>. please</p>

<p>Now go make something cool (and compatible with ancient browsers).</p>]]></content><author><name></name></author><category term="Programming" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Forcing a Scratch-like game engine to Not Be Like That</title><link href="http://kokoscript.com/2022/013.html" rel="alternate" type="text/html" title="Forcing a Scratch-like game engine to Not Be Like That" /><published>2022-11-19T00:00:00-06:00</published><updated>2022-11-19T00:00:00-06:00</updated><id>http://kokoscript.com/2022/013</id><content type="html" xml:base="http://kokoscript.com/2022/013.html"><![CDATA[<p><em>Originally posted to my cohost. Few edits may have been made where I saw fit, but I've kept these to a minimum.</em></p>

<p>This is just a quick dev journal that doesn't really pertain to any specific game of mine. I <em>guess</em> one could make the argument that it applies more specifically to the "new" project I've been teasing, but whatever</p>

<p>Since 2012 I've been using this 2D game engine called <a href="https://stencyl.com/">Stencyl</a>. It's a beginner-friendly game engine which revolves around a <a href="https://en.wikipedia.org/wiki/Visual_programming_language">visual programming language</a> (VPL). In the past I've described it as "Scratch on steroids", and I think that description still holds up. For a young aspiring gamedev in 2012, the VPL just clicked for me; but by the end of 2016, I'd started working on an RPG (<em>ExaStar</em>) and...</p>

<p><img src="/blogImg/013/oldbattlesys.jpg" alt="portion of an RPG battle system made using a block-based visual programming language. some lines are nested several layers deep, and generally everything's a mess." /></p>

<p>...needless to say, I was outgrowing the VPL <em>fast</em>. Luckily, Stencyl provides a way to write scripts in a "real" language: <a href="https://haxe.org/">Haxe</a>. Hooray! One problem though: Stencyl's built-in IDE looks a little like this:</p>

<p><img src="/blogImg/013/stencylide.png" alt="code editor within the stencyl toolset. it doesn't have much in terms of features, and uses a white color theme." /></p>

<p>Without any code completion (which is <em>very</em> crucial given Stencyl's API docs are slightly outdated and pretty difficult to browse through) or <em>at least</em> support for a dark theme, it's not really pleasant to work with! The keen-eyed among you may have noticed the "Open in External Editor" button though; for what it is, it works. It's still not going to give any code completion, but being able to use a text editor which isn't *gestures confusedly* <em>that</em> is nice. I think I used Kate (the KDE text editor) for a while when I first started converting all of <em>ExaStar</em>'s visual scripts over to Haxe.</p>

<p>But yes, the elephant in the room: code completion. Around the time I was breaking out of the VPL mindset, I was also learning Java. I was using IntelliJ at the time, and I knew that it had support for other languages via extensions. Haxe was, fortunately, one of them.</p>

<p>Quick (I say quick but this is multiple paragraphs) thing to note here is how Stencyl lays out its project folders: within the Stencyl workspace directory, there are two directories titled "games" and "games-generated". "games" contains the project files for each of your games, and "games-generated" contains their generated OpenFL projects.</p>

<p>The project directories inside the "games" folder are essentially useless for any IDE work; code is split up between a "code" directory (for what Stencyl calls "arbitrary" code, i.e. anything that isn't a scene or actor script), and a "snippets" directory (which contains scene and actor scripts, as well as layout files used to generate Haxe source from the VPL). The "code" directory is of special note since Stencyl allows you, the programmer, to specify package names for those "arbitrary" scripts. However, <em>every single script</em> is simply dumped into the root of that "code" directory with 0 hierarchy... hence why I said it's useless for IDE work.</p>

<p>The project directories in the "games-generated" directory, on the other hand, are completely valid OpenFL projects that can be run with a simple <code class="language-plaintext highlighter-rouge">lime test &lt;platform&gt;</code>, and can even be opened up in an IDE (provided you have a way to use Stencyl's versions of Haxe and Haxelib)! One major problem, though: these project directories are useless for development work (debugging, less so); Stencyl will simply overwrite most of the contents with corresponding copies from the "games" directory upon each test initiated from within the toolset.</p>

<p>So, I decided to experiment a bit. I knew that I needed a way to do development within the game's project folder, and <em>not</em> the generated one. My brilliant plan ended up being to work within the code directory, make subdirectories in there for scene and actor scripts, symlink them over from the "snippets" directory, fight with the Haxe extension a bit in order to make it happy, and...</p>

<p><img src="/blogImg/013/exastar-2018.png" alt="source code for a stencyl project open within intellij, along with stencyl itself opened to the right." /></p>

<p>Tada! A Stencyl project in IntelliJ. Barely. There were a few issues here:</p>
<ul>
  <li>Packages were still completely incorrect; in the screenshot you can see the IDE complaining about it at the very top of the file. It was like this for <em>every</em> file.</li>
  <li>Good luck trying to compile the game from the IDE. Still had to go through the Stencyl toolset for that.</li>
  <li>I remember having to fuck around with the classpath so much and it was <em>absolutely painful</em> since the game relied on so many extensions and libraries</li>
  <li>Scene and actor events <em>had</em> to be prefixed with "SE_" and "AE_" respectively in order to be symlinked into the appropriate directories. General scripts which applied to several scenes/actors were prefixed with "Script_", and I think they were symlinked to the root of that "GameScript" directory. I actually don't mind this one as much.</li>
  <li>One issue I never saw coming: none of this would work past 2018</li>
</ul>

<p>So 2019 comes around. The Haxe extension I was using for IntelliJ gets an update and leaves beta. Unfortunately for me, this update <em>refused</em> to play ball with the bizarre mess of a project I'd created. So I stuck with an older version of IntelliJ + the extension for a while until I eventually put <em>ExaStar</em> on an indefinite haitus and moved away from Stencyl for a bit.</p>

<p>That brings us to this week, where I put <em>Endless Attack REMIX</em> (my DOS game) on hiatus and decided to put all my efforts towards a new, smaller RPG using what was left of <em>ExaStar</em> as a base. Having gotten so used to working in a "traditional" development environment by this point, there was a huge issue I needed to tackle again: getting things working in an IDE. Because IntelliJ became digital molasses since I last used it in 2019, I decided to stick with VSCode (which I'd already been using for <em>REMIX</em>). It has several Haxe extensions (even one specifically for Lime/OpenFL), so it only made sense.</p>

<p>Except this time I had a different approach: what if I somehow "faked" an OpenFL project? The plan was simple: in the root of the game's project directory, make a directory to hold a symlinked copy of <em>most of</em> what was in the game's generated project directory. Then, for all of the scripts that were in the "code" and "snippets" directories, symlink those into their proper places. For the scripts in the "code" directory, this involved parsing the package names with awk, making directories in the source root accordingly, and symlinking them there. As for the scripts in the "snippets" directory... those are all part of a package called "scripts", so I just symlink all of them to "scripts". I wonder if I could organize them better at some point...</p>

<p>After a bit of configuration, I had vshaxe pointed towards Stencyl's distribution of Haxe and Haxelib, and with a bit more work, I had the code completion server successfully start without quitting! Except... there was no code completion. This meant that all I was <em>really</em> getting out of vshaxe was syntax coloring and nothing else. So why was this the case?</p>

<p>One thing I noticed right away was that Haxe's code completion server was spilling out a ton of errors to the tune of <code class="language-plaintext highlighter-rouge">Request textDocument/codeLens failed. Message: Error: Display file was not found in class path</code>. Weird. Clearly the project itself <em>is</em> in the classpath, otherwise the code completion server wouldn't have even started, no? I looked around for a bit for anything I could do to <em>try</em> and convince the extension that <em>yes, the project's own source files are in the classpath</em>, but I didn't really get anywhere, and called it a night.</p>

<p>Bringing us to earlier today. On a whim I decided to try hardlinking everything instead of symlinking, all the while thinking "surely this couldn't fix it, <em>right?</em>"</p>

<p><img src="/blogImg/013/stencylvscode.png" alt="source code for a stencyl-based game open within vscode, with full code completion support" /></p>

<p>OH. NEVERMIND!</p>

<p>While still <em>very</em> jank (it's still relying on linked files), this setup is so much cleaner than whatever the hell I was doing from 2017 to 2019. I even have the ability to compile and run the game from VSCode itself, so I don't have to open the Stencyl toolset just to do a quick compile.</p>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[Originally posted to my cohost. Few edits may have been made where I saw fit, but I've kept these to a minimum.]]></summary></entry><entry><title type="html">Endless Attack REMIX Development Journal #3</title><link href="http://kokoscript.com/2022/010.html" rel="alternate" type="text/html" title="Endless Attack REMIX Development Journal #3" /><published>2022-10-09T00:00:00-05:00</published><updated>2022-10-09T00:00:00-05:00</updated><id>http://kokoscript.com/2022/010</id><content type="html" xml:base="http://kokoscript.com/2022/010.html"><![CDATA[<p>Welcome to (*checks notes*) the <em>third</em> development journal? Weird, usually my projects don't make it this far before being scrapped...</p>

<p>Despite VCF coming and going without a demo prepared, development over the last few months was pretty active. There were a few weeks where I had to pause any work in order to focus on university, but other than that I've been taking any opportunity I can to <em>at least</em> mess with things.</p>

<h3 id="say-hello-to-stonecutter">Say hello to Stonecutter!</h3>
<p>At VCF, I went in with one goal: get a system solely to test REMIX on. I was looking to get a high-end 486 system of some kind, so I could get a good idea of how REMIX would run on actual, period-correct hardware. As I started going down the merchant hall, one system stood out to me: A Packard Bell Pack-Mate 3500CD. There were 3 specs listed on a sticky note that caught my eye:</p>

<ul>
  <li>Pentium 75</li>
  <li>16MB RAM</li>
  <li>Aztech sound card w/ OPL3</li>
</ul>

<p>I immediately went for it, and before leaving the show, I picked up a CRT that matched. So, here it is: the dedicated REMIX testing system!</p>

<p><img src="/blogImg/010/sc1.jpeg" alt="a packard bell pack-mate 3500CD computer" />
<img src="/blogImg/010/sc2.jpeg" alt="endless attack remix running on aforementioned computer, on a matching CRT" /></p>

<p>The Pentium 75 is <em>a little</em> faster than I would have liked, but it's still a really good indicator of how the game will run. The 16MB of RAM is split between 2 DIMMs, so I can drop down to 8MB if need be (since that's what I'm aiming for at the moment). The OPL3 sound card was a huge plus, as that's the lowest common denominator I'm writing the music for. It's cool getting to hear my music through an actual YMF262 chip.</p>

<h3 id="time-to-time">Time to time</h3>
<p>I suppose I should talk about this here since the next couple of sections mention it, but the timer system I mentioned I had trouble designing in the last journal ended up being much more simple than I expected. Instead of using identifiers for each timer, I just use variables called "timer counters" to keep track of time. The reference to a counter is passed into the timer functions, and is then compared to the target. If the counter hasn't reached the target, the function returns false. If it has, the function returns true and, in the case of periodic timers, the target is subtracted from the counter. In both cases, the counter is incremented by the time it took for the last frame to be computed, rendered, and displayed. That's really all there was to it!</p>

<h3 id="making-enemies">Making enemies</h3>
<p>There are now actual, killable enemies that spawn in the game! So far, it's just the first enemy type in the game (Spikeys), but constructs are in place for more types, how fast they should spawn per wave, and how many points they give the player.</p>

<video width="640" height="400" controls="">
	<source src="/blogImg/010/enemyDemo.mp4" type="video/mp4" />
	oh no. no video
</video>

<p>Enemies can also take more than one hit to kill.</p>

<video width="640" height="400" controls="">
	<source src="/blogImg/010/enemyDemo2.mp4" type="video/mp4" />
	oh no. no video
</video>

<p>Something I'm still trying to figure out is <em>how</em> to give each enemy their own, unique behaviors while still being able to lump them all under a single "enemy" struct. The way enemies are currently handled involves an "enemy type" field in the enemy struct, so when the enemy update function is called, the type is used to determine which "behavior" function to also invoke. Issues arise, however, when the idea of these behaviors needing additional fields than what's provided in the struct is brought up. For example, what if an enemy shoots a bullet multiple times? It needs at least an additional timer and a counter variable to tell when to stop. These can't just be added to the enemy struct, because <em>all</em> enemies would end up having these fields as well. And as more and more enemy types are implemented, the enemy struct could become huge!</p>

<p>Another idea I had was just defining a small chunk of memory in the enemy struct for each behavior to interpret differently, but if you open up a thesaurus, go to the word "fun", and look at the antonyms, you'll find "defining a small chunk of memory in the enemy struct for each behavior to interpret differently" in there. Weird!</p>

<p>This is something that inheritance in object-oriented languages is really good for. Unfortunately, I made the decision to write the game in C, which- surprise!- isn't really object-oriented, so I have to live with it. And cry. A lot.</p>

<h4 id="oh-bullets-have-health-too-by-the-way">oh, bullets have health too by the way</h4>

<p>That's not how it's going to be described in-game, but there's a "piercing" system for player bullets, which determines how many enemies a bullet can collide with before disappearing. Since I was in the middle of implementing enemy death logic, I decided to implement this as well. You can see it in action below:</p>

<p><img src="/blogImg/010/pierce1.gif" alt="player shoots at a single enemy. the bullet disappears after colliding with the enemy." /></p>

<h5 id="player-bullets-have-no-piercing">Player bullets have no piercing.</h5>

<p><img src="/blogImg/010/pierce2.gif" alt="player shoots at two enemies. the bullet disappears after going through both." /></p>

<h5 id="player-bullets-have-1-level-of-piercing">Player bullets have 1 level of piercing.</h5>

<p>There are some specifics regarding how bullet damage will work with piercing, but I still have to figure out which route I want to take with that. The game is somehow already getting pretty deep mechanic-wise!</p>

<h3 id="how-to-make-enemies">How to make enemies</h3>
<p>Sure, adding in enemies seems like a super simple task. In most game prototypes, you'll usually have basic enemies implemented within <em>at least</em> the first week, though this depends on the genre (for instance, RPGs might choose to focus on overworld first, and that's fine). Since I practically had to write a game engine before doing anything interesting, there was a solid year before I could even put the first enemy in the game (granted, most of that year was spent <em>not</em> working on the game...). It could have been sooner, but there were two important things I wanted to tackle before doing so: animated sprites and hit detection.</p>

<h4 id="animated-sprites">Animated sprites</h4>
<p>Animated sprites work using the "spritebank" system I mentioned way back in the first journal. Turns out I didn't really need to implement a dynamic array for it (though I did to some extent). Spritebanks are essentially a collection of RLE sprites, formed from each of the individual sprites in a sprite sheet. It's really more so meant to be used with sprites of equal dimensions, though in the future I could extend it to spritesheets with mixed sprite sizes as well; I digress.</p>

<p>Animated sprites are then given a pointer to a spritebank (multiple sprites can use the same spritebank, saving memory), and the current sprite to be displayed is simply a pointer to an entry in that spritebank. Animation gets controlled using an array of tuples, where each tuple is a frame: one value is how long the frame will be displayed before moving to the next, and the other is the index of the frame in the spritebank. The final tuple determines how the animation will loop; it can either go back to any frame in the animation, or simply freeze at the last frame.</p>

<p>Animated sprites also have the ability to flicker at a specified interval, which is a common effect that was used on systems without transparency support (Allegro has support for transparency, but I'd rather have the game perform well on older systems!). I'll most likely use the effect sparingly, and at slow intervals.</p>

<p>With the animated sprite system out of the way, I replaced the single-sprite system that game objects had, making it so every game object uses an animated sprite. For objects that only need a single sprite and no animation, it's pretty easy to make an animated sprite with no animation control array. As an added bonus, animated sprites were designed with the ability to use them outside of game objects in mind, in instances where hit detection or movement isn't necessary.</p>

<h4 id="hit-detection">Hit detection</h4>
<p>From the start, I knew that hit detection would be pretty costly CPU-wise. So, I decided that the engine would only support 3 collision shapes: rectangles, circles, and lines. It's enough for a game like REMIX, and in the case where more complex shapes are needed, I can just add additional shapes to a game object (spoilers!).</p>

<p>Any two shapes can be checked for collision using what I like to call a "mux function", which checks the types of the two shapes, and returns the result of the appropriate collision function (e.g. rect-rect, rect-circle, line-circle, etc.)</p>

<p>Each game object can then have collision shapes added to them one at a time, with support for positioning at an offset from the object. Collisions between two objects can also be checked; this just checks if any of the shapes between the two are colliding. There's... really not much else I can say about this. Much like animated sprites, collision shapes can be used outside of game objects, which makes them useful as regions (or sensors? I don't know what engines other than Stencyl call them).</p>

<h3 id="retracing-my-footsteps">Retracing my footsteps</h3>
<p>In the previous journal, I talked about how the engine is able to determine the framerate of the game in order to compensate for dropped frames. As a refresher, this relied on measuring the time it takes in-between each vsync, and doing some math to get the maximum possible framerate from that. Then, by measuring the time taken to compute + render a frame of the game and comparing it to the minimum frame time, the speed to run the game at could be determined. This worked for the most part, but issues started to appear that I hadn't considered.</p>

<p>When I got REMIX running on Stonecutter, at first the game ran pretty smooth. However, after a few test runs, something happened to throw my whole frame-timing system into question: the game stuttered HARD, from startup to exit. The framerate graph switched wildly between 70 and 45FPS constantly, despite the game running at a solid 70 in the previous run. When the game was restarted, it was back to a solid 70 again, as if nothing had happened.</p>

<p>This stuttering behavior would continue to show up here and there every several tests of the game on Stonecutter. So, I headed back to the drawing board. How should I make frame timing more accurate? Ah, how about changing how the minimum frame time is measured? I had noticed that, with each run of the game, the time measured was never really the same; so what about doing multiple time measurements, taking the smallest value, and calling that the "true" minimum frame time?</p>

<p>Er... let's just say I'm not an expert with display timings, because calling Allegro's vsync() multiple times in a row with only a few instructions in-between led to the game simply freezing during the time measurements. Ok, new plan: don't change anything! "Surely I can fix this one later," I thought to myself.</p>

<p>And I did, about a month later. While perusing the Allegro timer system API, I stumbled across a peculiar variable I had previously overlooked: <code class="language-plaintext highlighter-rouge">retrace_count</code>. Essentially, this is an integer that is incremented by 1 at a fixed rate (70 times per second), regardless of how "busy" the game logic is. So... I had written much of that frame time measurement system for nothing. Cool. After a week or so of determining how difficult it would be to replace the time measurements with a simple check of <code class="language-plaintext highlighter-rouge">retrace_count</code>, I went for it.</p>

<p>And I'm glad I did! The game became noticeably more stable; apparently the "stutter patterns" I mentioned in the previous journal weren't a side effect of DOSBox: it was just my frame time measurement system being inaccurate. Similarly, the more severe stuttering issues on real hardware went away as well.</p>

<p>Unfortunately, there are some caveats to using <code class="language-plaintext highlighter-rouge">retrace_count</code>, most notably the fact that the game will never be able to run any faster than 70FPS on modern platforms. There's a slight chance I could be wrong about this one though, but I'm pretty sure <code class="language-plaintext highlighter-rouge">retrace_count</code> still increments 70 times per second regardless of the display's refresh rate. And since the game speed is now tied to the difference in <code class="language-plaintext highlighter-rouge">retrace_count</code> between frames... yeah, you get the picture.</p>

<h3 id="porting-progress">Porting Progress</h3>
<p>I've only tested the Linux port between the last journal and now. I honed the build process for it down to the point where switching the build target is a simple <code class="language-plaintext highlighter-rouge">make clean</code> and <code class="language-plaintext highlighter-rouge">make TARGET=...</code> away, so I'm now able to test the port quite often in conjunction with the DOS version.</p>

<p>The use of <code class="language-plaintext highlighter-rouge">retrace_count</code> earlier and some small modifications to how timers are calculated led to the game running at a playable speed again (instead of 2000FPS- did I forget to mention that last time?), though things continue to be capped at 30FPS for reasons I still can't discern. I'm grasping at straws at this point, so if you've written stuff with Allegro 4 and might know why this is the case, please don't hesitate to get in contact!</p>

<p>Other than stability fixes, I played around with getting sound to play under the Linux port. It doesn't seem like MIDI playback works at all (even with timidity running as an ALSA MIDI server), which is a shame. On the flipside, I got streamed OGG playback to work, so that's now on the table! What I <em>didn't</em> get working was any sort of sound working under ALSA, which is concerning since that's the only "still relevant" sound system for Linux that's supported by Allegro 4. What I had to do instead was install alsa-oss and tell the game to use OSS as the sound system; then it worked fine. Not sure how I'm going to deal with this later on down the road, but at least it works under this very specific configuration.</p>

<h3 id="miscellaneous">Miscellaneous</h3>
<p>During the couple of weeks where I had to pause development, I was still able to do a few things related to the game that weren't as stress-inducing. For instance, the game now has a website!</p>

<p><img src="/blogImg/010/site.png" alt="preview of the game's site" /></p>

<p>It's not live yet; I'm still waiting until more content is added so I can make a proper gameplay reel and add some screenshots. But other than that, it <em>is</em> ready to go.</p>

<p>I also started writing the game's design document, something I had neglected doing for quite a while! So far I have most of the specifics of the game's rules written down, which should definitely help now that gameplay features are beginning to be written into the game.</p>

<p>How about smaller things related to the game/engine code? There's a new input system, which provides a way to check if a control was pressed, or if it was released. Prior to this, the only way to get input state was to read from an array that just said if a control was being held down or not, so I had to keep adding in additional "stops" to menu-related input code in order to prevent them from being unusable. Not any more!</p>

<p>I also ported over the logging system ("Tail") that I made for Stencyl and my previous engine attempt, Origami. It can write to a log file <em>and</em> stdout! Wow!!</p>

<p>There are a few other small things I implemented into the game/engine over the past few months, which I didn't really feel the need to talk about in-depth here: the shop display reference from the last journal getting rendered in-game, nineslice window scaling, asynchronous fades, a frame stepper for debugging purposes, and probably more that I'm forgetting...</p>

<h3 id="conclusion">Conclusion</h3>
<p>As you can see, a lot of the engine side of things has been completed (or at least near-final), and at this point it's mostly up to pure game development. Sounds like the demo is closer than ever, right?</p>

<p>Unfortunately, the cracks are beginning to show in how I picked out my courses for this semester. I mentioned at the beginning of the journal that I've had to ignore development at times in order to focus on these courses; they're difficult. I could complain for another paragraph about how the constant attention shift between schoolwork and personal projects is tearing me apart mentally, but I really shouldn't. Point is, I have no idea <em>when</em> the demo will be ready. I have the entirety of December free to work on the game, and the hope is that I'll have it ready some time early next year, but the next 2 months could end up with barely any extra work done on the game. Other people seem to be able to juggle university and a job just fine, but I simply can't. It really sucks!</p>

<p>Until next time, then!</p>

<h3 id="bonus-round">Bonus round</h3>
<p>Thanks for reading to the end! I noticed this journal didn't really have much to look at, so let's fix that.</p>

<video width="640" height="400" controls="">
	<source src="/blogImg/010/animSprTest.mp4" type="video/mp4" />
	oh no. no video
</video>
<h5 id="testing-the-animated-sprite-system-with-one-of-the-boss-animations">Testing the animated sprite system with one of the boss animations.</h5>

<p><img src="/blogImg/010/garbage.png" alt="the player sprite has been replaced with a visual representation of some of the game's memory, and some of the player sprites can be seen within the garbage.." /></p>
<h5 id="a-cool-visualization-of-spritebank-memory-i-got-while-trying-to-break-the-animated-sprite-system">A cool visualization of spritebank memory I got while trying to break the animated sprite system.</h5>

<video width="640" height="400" controls="">
	<source src="/blogImg/010/hardmode.mp4" type="video/mp4" />
	oh no. no video
</video>
<h5 id="hard-mode">Hard mode.</h5>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[Welcome to (*checks notes*) the third development journal? Weird, usually my projects don't make it this far before being scrapped...]]></summary></entry><entry><title type="html">Endless Attack REMIX Development Journal #2</title><link href="http://kokoscript.com/2022/009.html" rel="alternate" type="text/html" title="Endless Attack REMIX Development Journal #2" /><published>2022-07-18T00:00:00-05:00</published><updated>2022-07-18T00:00:00-05:00</updated><id>http://kokoscript.com/2022/009</id><content type="html" xml:base="http://kokoscript.com/2022/009.html"><![CDATA[<p>At the end of Journal #1, I mentioned something about publishing another journal "a month or two from now". It's been *checks notes* <em>4 and a half months</em> since then.</p>

<p><em>"Clearly you must have made a ton of progress during that time, right?"</em></p>

<p>...</p>

<p><em>"Right???"</em></p>

<p>...allow me to explain.</p>

<p>I published Journal #1 a couple months before the end of the semester, so from a few days after I had published it up until my very last exam, I avoided working on REMIX quite a bit in order to focus on assignments. Even <em>after</em> the semester was over, I somehow decided the following was much more productive:</p>
<ul>
  <li>Ruining my sleep schedule twice</li>
  <li>Redoing my entire Debian setup</li>
  <li>Building a bookshelf?</li>
</ul>

<p>...yeah, I don't really have an excuse for not working on the game during the solid month or two after classes had ended. I picked it back up a couple weeks ago and have been making somewhat steady progress ever since, however. And even between publishing Journal #1 and resuming development recently, I was still able to do a few things here and there. So, let's talk about all of that!</p>

<h3 id="saving-space">Saving Space</h3>
<p>REMIX is a DOS game, and as most know, DOS games were typically distributed via floppy disks (or CD-ROM in the case of a fancy multi-media game). I really liked the idea of distributing the demo and/or final product via floppy, but at first it didn't seem like REMIX would be a good fit (literally). Although the game's compiled code only took up around 70KiB at the time I considered it, there was a huge problem: Allegro alone, unoptimized, takes up 900KiB- nearly an entire megabyte. Since the typical floppy disk only has 1.44MB to spare, that left me with practically 540KiB to fit the game into, which wasn't ideal considering I was already dangerously close to that limit with all of the spritesheets already in the game. So, I had to start finding ways to crunch the game down.</p>

<p>Obviously I <em>could</em> have simply compressed the entire game folder into a ZIP or something and called it a day, but I felt like that was the cheap way out. Since I had already been using the obvious compiler options (such as -s), I focused my attention towards Allegro itself. Allegro has <a href="https://liballeg.org/stabledocs/en/alleg043.html" target="_blank" rel="noreferrer noopener">a documentation page on space-saving techniques</a>, and there were a couple simple ones I started out with back before I wrote Journal #1. The first involved preventing extraneous drivers and color depth support from being compiled into the game. In my main.c, I simply put something like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BEGIN_GFX_DRIVER_LIST
	GFX_DRIVER_VGA
END_GFX_DRIVER_LIST

BEGIN_COLOR_DEPTH_LIST
	COLOR_DEPTH_8
END_COLOR_DEPTH_LIST

BEGIN_MIDI_DRIVER_LIST
	MIDI_DRIVER_AWE32
	MIDI_DRIVER_ADLIB
	MIDI_DRIVER_MPU
	MIDI_DRIVER_SB_OUT
END_MIDI_DRIVER_LIST

BEGIN_DIGI_DRIVER_LIST
	DIGI_DRIVER_SOUNDSCAPE
   	DIGI_DRIVER_AUDIODRIVE
   	DIGI_DRIVER_WINSOUNDSYS
   	DIGI_DRIVER_SB
END_DIGI_DRIVER_LIST
</code></pre></div></div>
<p>Most of the space savings here come from the GFX drivers and color depths. Since the game only targets standard VGA on DOS, no other drivers <em>really</em> need to be added in. The screen rendering code is dead simple and portable to the other drivers though, so maybe I'll add them back in at some point. Without any real hardware to test the other GFX drivers though, I'm unsure of it. Again, I still need a period-appropriate PC!!</p>

<p>Tangent aside, the color depth is always going to be 8-bit, since that's all VGA mode 13h supports. No use in including 15, 16, 24, and 32-bit color if I can't use them. The only sound-related driver I ended up removing was Allegro's DIGMID system, which uses samples to play MIDI files on the sound card, instead of relying on an OPL chip or MIDI-out. I decided against using DIGMID since it takes up a large amount of memory to store the patchset, CPU time to play them back, and valuable sound card voices I'd rather use for sound effects.</p>

<p>With all that said and done, I saved a whopping... ~200KiB. Not a bad improvement, but Allegro was still taking up 700KiB. What else could I do?</p>

<p>The second space optimization Allegro's documentation mentions involves recompiling the library without support for certain color depths. But wait, didn't I disable those with the COLOR_DEPTH_LIST earlier? Well... not exactly. For some bizarre reason, the color depth list can only go so far in removing support for these color depths; there's extra stuff, still don't know what, that can only be removed by doing a recompile. This was easy though; just open include/allegro/alconfig.h, remove the <code class="language-plaintext highlighter-rouge">#define ALLEGRO_COLOR</code> defines that don't end in an 8, recompile the library, and there we go: a version of Allegro that can ONLY use 8-bit color! And how much did that save us?</p>

<p>~50KiB. Allegro was still using 650KiB.</p>

<p>I decided to live with this fact for a while. I did one more optimization after Journal #1, in which I restructured the game's source to avoid multiple defines. This saved quite a bit of space on the game's side- didn't really take a note of how much- but the 650KiB from Allegro continued to bother me. It was then that I realized I glossed over an important line in that documentation page:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"For all platforms, you can use an executable compressor called UPX, which is available at http://upx.sourceforge.net/. This usually manages a compression ratio of about 40%."
</code></pre></div></div>

<p>I took to my terminal and quickly fired a <code class="language-plaintext highlighter-rouge">sudo apt install upx</code>. Surprisingly, it worked. To make sure I didn't install some other tool that also happened to be named "upx", I ran it and sure enough: it was the same tool the Allegro documentation mentioned. I ran it on the latest EXE of the game I had at the time, and like magic, it shrunk down from 700KiB to around 280KiB! Sure, it'll introduce some waiting time when launching the game on 486 and Pentium-class systems, but that's a sacrifice I'm willing to make in order to fit the game onto a single diskette.</p>

<p>With around 1.2MB now at my disposal for the game assets, I had nothing to really worry about. But there was <em>still</em> more that I could do.</p>

<h4 id="the-easiest-most-obvious-space-saving-technique-use-a-better-format">The Easiest, Most Obvious Space Saving Technique: use a better format</h4>
<p>I mentioned in Journal #1 that the spritesheets for the game are all standard, 8-bit indexed BMP files. What I <em>didn't</em> realize at the time was that BMPs, in the context of doing graphics for Allegro-based games, are absolutely awful for space-savings. For reference, Allegro 4.2 only supports loading BMP, TGA, and 2 fringe formats: LBM and PCX. So while I knew from the start that PNG would be a more space-efficient option than BMP, there was no way I could load them in natively (unless I added in support for PNG myself, which I'd rather not). So I tried experimenting with the other formats.</p>

<p>Of the 4 formats Allegro supports, Aseprite only supports saving/loading TGA and PCX files (in addition to the BMPs I had already been working with); so I had to leave LBM off the table (sorry DPaint fans!). When comparing TGA and PCX versions of a spritesheet to the original BMP version, the choice was clear: move away from BMPs.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menu.bmp: 30.7KiB
menu.tga: 11.7KiB
menu.pcx: 10.4KiB
</code></pre></div></div>
<p>And for another comparison, with a larger spritesheet:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>b_kspike.bmp: 131.0KiB
b_kspike.tga: 31.7KiB
b_kspike.pcx: 28.6KiB
</code></pre></div></div>
<p>(To be fair, this sheet shrinks better than it should since it's unfinished and there's a lot of empty space. I'll explain why in a bit.)</p>

<p>I ended up going with PCX since it's able to squeeze out a couple KiB compared to TGA. PCX is a truly niche file format now, but at the time it was <em>the</em> image format to use on DOS. Of all the programs currently installed on my workstation, only Aseprite and GIMP can open them; I suppose the only reason Aseprite can open them in the first place is due to its Allegro heritage. With all of the spritesheets converted over to PCX, I'm currently sitting at 189KiB of spritesheets, compared to 737KiB had they all been BMPs. Impressive!</p>

<p>If you're not that much of an image format nerd, you're probably wondering <em>how</em> PCX is able to shrink the sprites down so much compared to BMP, while still maintaining the same exact data. The key difference between BMP and PCX is that PCX actually makes a (rudimentary) attempt at compression, via run-length encoding, or RLE (hey, there's that acronym again!) for short. BMP stores each individual pixel of an image, making no attempt at consolidating them together if multiple pixels in a row have the same color. With RLE, if multiple pixels in a row share the same color (in the case of PCX, 3 or more pixels), the consecutive pixels are stored as a byte containing the number of pixels, then a byte containing the palette index.</p>

<p>There's a bit more nuance as to how PCX implements RLE compression, such as the fact that it can only do runlengths of up to 63, certain colors can only be stored as an RLE pair, etc. <a href="https://en.wikipedia.org/wiki/PCX" target="_blank" rel="noreferrer noopener">Here's some additional reading if you're interested</a>.</p>

<p>RLE isn't necessarily a magic bullet for <em>all</em> cases. If you have highly-dithered sprites or art, RLE will do next to nothing. And in the case of PCX, it'll store the individual pixels much like BMP (or for certain palette indices, an RLE pair of length 1). The artstyle I'm using for REMIX just happens to have large runs of flat colors, so the spritesheets benefit from the compression quite well.</p>

<p>But with all that aside, <em>surely</em> this is the optimal way to store all of the spritesheets, right??</p>

<h4 id="well-yes-but-actually-no">Well yes, but actually no</h4>
<p>There is one <em>final</em> trick to squeezing down the spritesheets, and it's as far as I'm willing to go with space optimization:</p>

<p>Just make the spritesheets smaller.</p>

<p>I said this was a "final" trick, but it's more like a few tricks aimed at doing the same thing (reducing spritesheet size). The first trick involves rearranging the sprites within the sheet in order to reduce empty space as much as possible. This only really saves a couple KBs per sheet, but the savings can add up quickly. Take for instance the menu spritesheet; here it is before rearrangement:</p>

<p><img src="/blogImg/009/menu-pre.png" alt="menu spritesheet, pre-rearrangement" /></p>

<p>And after:</p>

<p><img src="/blogImg/009/menu-post.png" alt="menu spritesheet, post-rearrangement" /></p>

<p>Sure, the fun easter egg had to go, but did it really need to be there in the first place? Clearly the 'X' and the checkerboard could have fit there the whole time. We even get some space to add some animation to the savefile icon. The second trick is also demonstrated in the same spritesheet; only the left half of the selector animation is stored in the sheet, and is flipped on runtime in order to produce the other half. I don't use this technique as often as I'd like, since for smaller sprites (like enemies) it gets a bit tedious, and larger sprites tend to be non-symmetric. Maybe before the final release I'll do a thorough sweep of all the symmetric sprites. That brings us to the final technique:</p>

<p>If possible, don't store a sprite <em>at all</em>.</p>

<p>Instead of storing the sprite in a spritesheet, why not store the <em>instructions</em> to make the sprite? Take for instance the Sound Test screen:</p>

<p><img src="/blogImg/009/sndtest.png" alt="sound test screen, which contains sprites of 2 large speakers and a hifi" /></p>

<p>Now take a look at its spritesheet:</p>

<p><img src="/blogImg/009/sndtest-sheet.png" alt="spritesheet for the sound test screen, the speakers are absent" /></p>

<p>Hey, where'd the speakers go? Ah, here they are!</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rectfill(dblbuffer, 12, 58, 83, 198, 18);
rectfill(dblbuffer, 139, 58, 210, 198, 18);
circlefill(dblbuffer, 50, 81, 15, 17);
circlefill(dblbuffer, 50, 117, 15, 17);
circlefill(dblbuffer, 50, 166, 26, 17);
circlefill(dblbuffer, 177, 81, 15, 17);
circlefill(dblbuffer, 177, 117, 15, 17);
circlefill(dblbuffer, 177, 166, 26, 17);
int drum_offset = is_bgm_playing ? rand()%3 : 0;
draw_rle_sprite(dblbuffer, speaker_drums[1], 47 + drum_offset, 77);
draw_rle_sprite(dblbuffer, speaker_drums[1], 47 + drum_offset, 113);
draw_rle_sprite(dblbuffer, speaker_drums[0], 45 + drum_offset, 155);
draw_rle_sprite(dblbuffer, speaker_drums[1], 174 + drum_offset, 77);
draw_rle_sprite(dblbuffer, speaker_drums[1], 174 + drum_offset, 113);
draw_rle_sprite(dblbuffer, speaker_drums[0], 172 + drum_offset, 155);
rect(dblbuffer, -1, 57, 84, 199, 16);
rect(dblbuffer, 99, 57, 211, 199, 16);
rectfill(dblbuffer, 0, 58, 10, 198, 18);
rectfill(dblbuffer, 100, 58, 137, 198, 18);
fastline(dblbuffer, 11, 58, 11, 198, 17);
fastline(dblbuffer, 138, 58, 138, 198, 17);
drawing_mode(DRAW_MODE_MASKED_PATTERN, speaker_mesh, 0, 0);
rectfill(dblbuffer, 12, 58, 83, 198, 19);
drawing_mode(DRAW_MODE_MASKED_PATTERN, speaker_mesh, 1, 0);
rectfill(dblbuffer, 139, 58, 210, 198, 19);
solid_mode();
fastline(dblbuffer, 16, 58, 16, 198, 18);
fastline(dblbuffer, 17, 58, 17, 198, 18);
fastline(dblbuffer, 143, 58, 143, 198, 18);
fastline(dblbuffer, 144, 58, 144, 198, 18);
</code></pre></div></div>
<p>Sure, it's a lot less flexible and way more complex than simply storing a speaker sprite in the sheet. But considering the size and dither pattern on the speakers, drawing via code makes much more sense when aiming for space efficiency. Unfortunately, CPU usage concerns limit this technique to very specific areas (like menus, where not much is going on).</p>

<p><em>Disclaimer: I actually have no clue how much space the compiled code takes up. I'm guessing not much compared to a RLE-compressed sprite, but I haven't gone in with a disassembler to check.</em></p>

<hr />

<p>I think this is really all I can do in regards to executable and sprite storage, without getting into extremely weird, esoteric techniques à la the demoscene. There's also the topic of sound storage: MIDIs can't be made any smaller than they already are (besides getting rid of extraneous events), and WAVs are simply a balancing act of size vs. quality, with no alternatives other than Creative's VOC format (of which I have doubts about cross-platform compatibility). Again, I can't use something like OGG, since Allegro didn't support that until well after DOS support had been removed.</p>

<h3 id="porting-progress">Porting Progress</h3>
<p>I mentioned in Journal #1 that I don't want to limit REMIX to DOS, and am working to provide native Windows, Mac, and Linux ports as well. Here's the progress and findings I've made for those platforms thus far.</p>

<h4 id="windows">Windows</h4>
<p>Still haven't tried this port. Moving along!</p>

<h4 id="linux">Linux</h4>
<p>This one is... interesting. For whatever reason, the Linux port of REMIX runs at a framerate that is <em>half</em> the refresh rate of the monitor. I can remove the call to vsync() that's made before drawing the framebuffer and the game runs unlocked as expected, but I still can't seem to figure out <em>why</em> vsync() waits for every other refresh.</p>

<p><img src="/blogImg/009/remix_linux.png" alt="endless attack remix on linux" /></p>

<h4 id="macos--1014">macOS (&lt;= 10.14)</h4>
<p>I actually tried this port (and the Linux one as well) long before I had written Journal #1, just never mentioned it. It suffers from really bad performance issues, as well as (I think) the same vsync() problem that Linux has.</p>

<p><img src="/blogImg/009/remix_osx.jpeg" alt="endless attack remix on mac os x" /></p>

<h4 id="haiku">Haiku</h4>
<p>Ok, fine; I didn't even mention this one.</p>

<p>The Haiku port <em>BARELY</em> works. It runs, don't get me wrong. But it absolutely struggles. It's a combination of the weird vsync problem that Linux has, PLUS a new problem: certain gamestates <em>do not</em> update the screen. <em>Unless</em>, of course, focus is removed and replaced on the window. It's really bizarre, especially considering the fact that only certain gamestates will do this. For instance, the Epocti logo is fine (albeit slow), but as soon as the intro starts, redraws stop. I don't know if this is an issue with my rendering code, or the port of Allegro to Haiku. So for now, I'm leaving it as-is until I fix the vsync issue first.</p>

<p><img src="/blogImg/009/remix_haiku.png" alt="endless attack remix on haiku" /></p>

<hr />

<p>Of course, with each port I do, the Makefile is updated and build parameters are added accordingly, so I'm never retreading old ground. Once the initial porting work is done and the game is at least running, it's all down to fixing any weird lingering issues in the source.</p>

<h3 id="framerate-fun">Framerate Fun</h3>
<p>In Journal #1, I mentioned that I had plans for something called "dynamic framerate switching", and proceeded to not explain what that meant. It's actually a terrible name; if I were to go back and edit the post, it should <em>probably</em> be called "game speed management" instead. It really has more to do with altering the speed of the game in response to the framerate of the game; framerate isn't something that can be "switched". But I digress.</p>

<p>Initially, The only way the game could tell what framerate it was running at was via a variable that was set to either 1 or 2, called "game_tick_step". If the value was 1, that meant the game was running at 60fps. If it was 2, that meant 30fps. Essentially, the game would simulate running everything at 2x speed when running at 30fps in order to avoid slowdown.</p>

<p>There were <em>many</em> problems with this approach. First of all, the variable wasn't indicative of the current framerate at all. Tons of sprites could be drawn to the screen to the point where slowdown was occurring, but as far as the game was aware, it was still running at 60fps. The only way to tell the game to <em>properly</em> run at 30fps was to change a setting in the setup menu. Only then it would always run at 30fps to maintain a constant framerate without slowdown. There was also the fact that "fake" timers were set up to check the number of ticks that had elapsed, and used remainders that <em>had</em> to be multiples of 2 in order to maintain compatibility with 30fps mode. And another issue: what happens if slowdown occurs while running at 30fps? What about running the game faster than 60fps?</p>

<p>My first idea (actually, the idea I mentioned in Journal #1) was more of a band-aid; it used the same tick_step system, but starting at 1 = 120fps instead. There was more to it though; the idea was that the game would track how many objects were on-screen at any given time, and through <em>some sort of magic</em>, determine if the game was experiencing slowdown. If slowdown was occurring, the tick_step would increase, and the number of objects were recorded. As the game would continue, it would compare the number of objects on-screen to the list, and switch game_tick_step accordingly. I was so confident in this idea.</p>

<p>Then it hit me. <em>How</em> do we determine slowdown? A clock function. What can we do to tell what framerate we're running at? Clock function before and after calling vsync(), then compare that to the time it takes for a single screen refresh.</p>

<p>And with a bit of math, I have the <em>actual</em> speed to run at, scalable across any refresh rate. It's not exactly perfect, though; DOSBox has some stuttering issues that cause the game speed to jump to 2x for a single frame at a time, and it's a bit jarring. I added some code to take the average speed based on framerate history, but the stuttering issues still manage to leak through a bit. I guess I'll just have to work on it a bit more.</p>

<p><img src="/blogImg/009/framegraph.gif" alt="recording of endless attack remix, showing framerate profiler with graph" /></p>
<h5 id="i-even-added-a-graph-how-fancy">I even added a graph! How fancy!</h5>

<p>From the new frame timing system, I also learned that DOS games using VGA mode 13h run at 70fps, not 60. ¯\_(ツ)_/¯</p>

<h3 id="timer-troubles">Timer Troubles</h3>
<p>Oh god. Where do I start.</p>

<p>With the new frame timing system, the tick-based system has to go. Unfortunately, I tied so much time-sensitive code to it, using remainders on the elapsed number of ticks for periodic timers and whatnot. So now I'm stuck trying to come up with a new timer system that'll be easy to work with.</p>

<p>Allegro <em>has</em> a timer system, based on interrupts. I'm trying to avoid using it since that would effectively make the game <em>kinda</em> multi-threaded, and that's the last thing I'd like to deal with in C. It doesn't help that Allegro's timer system messes with the FPU state a bit, and since I've found myself using more floating point variables ever since I added the new frame timing system, that sounds like a recipe for disaster.</p>

<p>The idea I currently have is a pair of functions called "timer_do_after" and "timer_do_every", for one-shot and recurring timers respectively. The idea is to call these in the update function of a gamestate (in other words, every frame) as part of an <code class="language-plaintext highlighter-rouge">if</code> statement. The functions would return "1" if the timer is complete and "0" otherwise, meaning they can control access to a portion of code without the need to define a separate function. It would look something like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>void do_game() {
	// do some stuff
	
	if (timer_do_after(identifier, number_of_milliseconds)) {
		some_cool_value++;
		make_explosion(enemies[0]);
		kill_enemy(enemies[0]);
	}
	
	// do more stuff
}
</code></pre></div></div>
<p><em>Something like that.</em></p>

<p>Things get less simple once you turn your attention to the "identifier" parameter. The issue with this timer system is that the timers need to rely on a table of in-progress timers in order to track their progress and tell if the specified time has elapsed. Which normally wouldn't be an issue, until you realize what happens if a timer needs to be run on a per-struct basis. How do you differentiate those timers from other ones sharing the exact same program counter value? The idea I had was to generate a random identifier per each timer and use that to look them up, but the more I plan it out, the hairier it gets. Now I have to implement a hashmap? What happens if two timers coincidentally have the same generated identifier? How fast would it be to look up a timer based on its identifier? Does a one-shot timer remove its own entry after finishing? Then how would it know not to start again?</p>

<p>I'm very much open to alternative ideas for a simple, yet effective timer system like this. Maybe I should just bite the bullet and go with Allegro's own timer system. Who knows.</p>

<hr />

<p>Ok, enough engine development talk. It's definitely not for everyone, and my head hurts just writing about it. How about the player-facing stuff; y'know, the <em>game</em>?</p>

<h3 id="shop-scheming">Shop Scheming</h3>

<p>It's not implemented into the game yet, but here's what the shop will look like (besides the background, which I haven't started)!</p>

<p><img src="/blogImg/009/shop.png" alt="shop interface in endless attack remix" /></p>
<h5 id="say-hi-to-the-shop-worker-aka-rat-dude-working-a-dead-end-retail-job-thats-actually-the-sole-description-i-designed-him-around">Say hi to the shop worker, aka "rat dude working a dead-end retail job". That's actually the sole description I designed him around.</h5>

<p>Speaking of the shop, I made a pretty big alteration to the gameplay plans. In the original <em>Endless Attack</em>, the player could purchase any item they so desired during gameplay, as long as they had the funds, via the hotbar. The same held true in the gameplay plans for <em>Endless Attack X</em>. The key difference being that, during cooldown periods, the player would be able to visit an "extended shop", and change which items would be purchase-able via the hotbar. When beginning work on REMIX, I kept the same shop premise, but recently I decided to explore other options.</p>

<p>Why? I worry that, with a fully-open shop, players will simply subscribe to a meta and <em>only</em> save up for/purchase items and weapons that have a great advantage for different groups of waves. Since I want players to experiment with different items and weapons instead of immediately going for "the known best loadout", I first thought of a sort-of "weapon tree". With it, the first weapon upgrade the player makes would "lock" them to that weapon type (and upgrades for it) for a while, before giving the option of switching to a different type (and locking to that for a while as well). But I felt like this was simply too convoluted, and didn't really address the meta issue. Players would just go for the better weapon type path immediately, try as I might to balance all of them.</p>

<p>That brings us to the current shop plan: Instead of giving the player free reign, do what some roguelike/roguelite games do and provide a limited, randomized choice of items/weapons each time the shop is accessible. The set of possible items to pick from will be based on the current wave, scaling with the gradually-increasing difficulty. There's the chance of a "shop re-roll" item, to get a new set of items to pick from without needing to wait until the next shop (every 5 waves). There's also the chance of a "shop extension" item, to increase the number of items available to pick from permanently... or at least until the run ends.</p>

<p>The previous shop made the hotbar serve two different purposes, and felt a bit cumbersome since it acted as both an inventory and mini-shop at the time. With the more limited shop, the hotbar will no longer be used for purchasing items, instead acting solely as an inventory, which I feel would be a lot easier for new players to understand.</p>

<h3 id="graphics-gimmicks">Graphics Gimmicks</h3>
<p>One day, I decided to choose violence. "What if there were post-processing effects in a DOS game?", I asked myself.</p>

<video width="640" height="400" controls="">
	<source src="/blogImg/009/postprocessdemo.mp4" type="video/mp4" />
	oh no. no video
</video>

<p>This was the result. I have no clue if I'll use this effect in the final game, I just wanted to do it because I could.</p>

<p>Moving away from weird experiments, here's this cool transition effect I made. It'll be used when entering/exiting the shop, and maybe a couple other places.</p>

<video width="640" height="400" controls="">
	<source src="/blogImg/009/transitiondemo.mp4" type="video/mp4" />
	oh no. no video
</video>

<p>The "out" transition is a bit unpolished since I do a tiny bit of overdraw not visible with the "in" transition.</p>

<h3 id="conventional-conclusion">Conventional Conclusion</h3>
<p>From this journal, it feels as if I'm writing a full on <em>game engine</em> at this point. Technically, I am. I'm hoping to keep the engine side of things as separate as possible from the game itself, so at some point I can turn the engine into its own library for others to use and make their own cross-platform DOS games (without all of the pain I'm having to go through first). Allegro is very fun to work with, and my goal is to eventually make it even more accessible!</p>

<p>The hope is that, once I'm done writing the timer system and the sprite-management systems I mentioned in Journal #1, development can go full steam ahead. I've nearly finalized the fictitious game design document (which exists only in my head), so I know precisely what I'll need to work on at this point. Expect another journal soon!</p>

<hr />

<p><em>The header alliteration was originally unintentional. It was only after I realized I had done it twice that I decided to keep it going. I promise not to do it again.</em></p>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[At the end of Journal #1, I mentioned something about publishing another journal "a month or two from now". It's been *checks notes* 4 and a half months since then.]]></summary></entry><entry><title type="html">Endless Attack REMIX Development Journal #1</title><link href="http://kokoscript.com/2022/008.html" rel="alternate" type="text/html" title="Endless Attack REMIX Development Journal #1" /><published>2022-03-02T00:00:00-06:00</published><updated>2022-03-02T00:00:00-06:00</updated><id>http://kokoscript.com/2022/008</id><content type="html" xml:base="http://kokoscript.com/2022/008.html"><![CDATA[<p>So! It appears it's time for me to start yet another series of development journal posts, this time for my next-but-before-ExaStar game, <em>Endless Attack REMIX!</em> Much like ExaStar's first development journal, I'll start with a lengthy amount of history behind the game, then spend some time talking about what I have worked on thus far. Let's go!</p>

<h3 id="but-first">But first...</h3>

<p>What <em>is</em> Endless Attack? Why <em>REMIX</em>? I'll explain more through the walls of text to come, but for context: <em>Endless Attack</em> is an unreleased¹ "endless" arcade shooter game I made a long while back. It's endless in the same sense that, while you <em>could</em> keep playing a "non-endless" game after you finish it, the post-game is just going to become a whole lot of the same in the end (again, saving a full explanation for later). <em>Endless Attack REMIX</em> is a sequel to that game, with several more years of programming, spriting, and game design experience added on. And more development time than 16 hours. Did I forget to mention I'm targeting MS-DOS as the primary platform it'll run on? Because yes, making a DOS sequel to a Windows game made sense to me.</p>

<h3 id="the-history-of-endless-attack">The history of: <em>Endless Attack</em></h3>

<p>It's Summer 2014, and I'm in a 2-week-long game development "boot camp". I put "boot camp" in quotes because it was really more of a short course at a community college that I'd get driven to each weekday (so weekends were off). The class was about 2 hours long each session, for a total of 10 sessions. The first session was pretty much introductory, basic "get to know each other"-type stuff and how to use the engine we'd be using (Multimedia Fusion 2). By the second session we were essentially set off to make whatever we wanted, until the final session, where we'd play each other's games and score/rank them. That gave us a whopping <em>16 hours</em> to make a game, individually (like I did) or as a team! And to make things even more difficult, I had never used MMF before, so I had to get acclimated with its scene/room/screen (I forgot what they called it) and visual programming systems within that time. Luckily, I came up with a gameplay idea and was ready to go from the start: make an endless (or at least as close to that as I could get) top-down shooter, where the player can get upgrades and health refills using the score earned from defeating enemies in order to progress further. Simple, effective, and gave me something to work towards as I got used to the engine. I proceeded to give the game the most creative title ever: <em>Endless Attack</em>.</p>

<p>And so, the two weeks went by. Most of what I can remember during that time was a lot of people learning what emulators are for the first time, people competing in Super Bomberman instead of working, and me accidentally creating the greatest bug in my game development career, wherein the player could rack up a cataclysm of bullets and shoot them all off at once. The teacher was shocked. I never ended up figuring out how to re-implement it as an upgrade. It's definitely going to be added into <em>REMIX</em>.</p>

<p>The final day comes around, and we all start playing each other's games. One thing to note here is that MMF2 came with a lot of pre-made assets that never really looked the greatest, and since no one in the class really had any prior pixel art or programming experience... everyone used the pre-made assets. Everyone except me. I really hate to put myself on a pedestal, but <em>Endless Attack</em> stood out from the rest with its custom art, gameplay, and (though a bit fatiguing) <em>actually tolerable</em> controls. The scoring went as follows: You'd play each person's (or team's) game. You'd then give the game a point in the one category it excelled at, or just not give it one. Unfortunately, I don't remember what the scoring categories were. Regardless, given how unique my game was compared to the rest, I was eager to see which category it would win; or if it would win one at all. Unsurprisingly, it did: the "wild-card" category. Whether that meant it excelled at multiple things or that it was just an "other" category, I'll never really know. But people definitely liked <em>Endless Attack</em>, and given I had never really gotten feedback for my games before that point, it was good to see!</p>

<p><img src="/blogImg/008/ea-title.png" alt="Endless Attack title" /></p>

<h5 id="title-screen-screenshot">Title screen screenshot.</h5>

<p><img src="/blogImg/008/ea-gameplay.png" alt="Endless Attack gameplay" /></p>

<h5 id="gameplay-screenshot-note-the-sideways-spikeys-i-dont-know-why-i-rotated-them-like-that-considering-every-other-enemy-looks-correct-from-the-side">Gameplay screenshot. Note the sideways Spikeys; I don't know why I rotated them like that, considering every other enemy looks correct from the side.</h5>

<p>During the last few days, I actually copied over in-development versions of the game onto a flash drive so I could show it to my cousin, who was also taking a course at the time. What I <em>didn't</em> copy over, and I'm still kicking myself for this, was the game's actual project file. By forgetting to do this, I had pretty much sealed the game's fate: there were no more additions that I could have made, given more development time. As a result, <em>Endless Attack</em> ended up being very far from "endless"; after about 35 or so waves, or around half an hour, the last wave would just loop over and over again. The game was, whether I felt it was or not, a finished product. The loss of the project file did, however, spur the development of...</p>

<h4 id="a-sequel">A sequel</h4>

<p>Moving forward a few months, I decided it was time to recreate Endless Attack in Stencyl, my engine of choice. There was no other option; with no way to add onto/polish the original game, I would just have to start from scratch. Obviously, I didn't <em>just</em> want to recreate the game; instead, I decided to call it a sequel since the original game was "complete". So, I got to work planning and creating a prototype for <em>Endless Attack X</em>. Without the original project file, I had to create new wave data, which <em>sounds</em> easy, but it was pretty tedious trying to get it to match the original game. Beyond replicating the content of the original, there were new things I wanted to add aside from more enemies: bosses and a larger shop were the two big ones. In order to help the player gradually get further in the game and create some sort of gameplay loop, I planned a "prepoint" system. With it, the player would earn points during normal gameplay to use before starting their next run, making some shop items immediately accessible. I also toyed around with a Co-op mode by simply adding in a second player object and changing the controls for it. It worked pretty well, even if the player sprites were (confusingly) the exact same!</p>

<p><img src="/blogImg/008/eax-gameplay.png" alt="Endless Attack X" /></p>

<h5 id="the-sideways-spikeys-were-fixed">The sideways Spikeys were fixed!</h5>

<p>Funnily enough, I did most of the graphics for the game during school, in the computer lab, where they had Photoshop installed. Keep in mind I was still using Paintbrush on OS X at home for graphics work, which was really just Windows XP-level MSPaint at the end of the day; so my desire to have anti-aliased, "vector" sprites for the game meant I had to resort to the school computers for that.</p>

<p>After working on the game for a bit, development- like a lot of my other projects- suddenly just... stopped. Lack of motivation is the theme here, I suppose. Although the basic gameplay was there, I hadn't gotten to the same number of waves that were implemented in the original game. The shop wasn't even implemented, either. I didn't completely give up hope, though.</p>

<p>In 2016, I decided to try again with EAX. I started from a clean slate, implemented about the same amount of content (even adding in a main menu, unlike the prior attempt), and then stopped after 4 builds. The co-op feature didn't even get re-implemented either, and didn't make a return for future attempts. Good things never last, it seems. There are a couple of noteworthy things about this version of the game, though; for example, the "cooldown periods", which were a new addition to the gameplay. These let the player take a break (crucial for a game like EAX), and also provided a better way to access the shop. Another interesting addition was game saving, which I never originally planned for. I guess the original idea was that you'd play until you simply didn't feel like going any further, making the game seem truly endless, but eventually I realized that was a bad idea and added a save system anyway. I also redid all of the graphics for this version, as I had switched to Windows since the last version of the game, and learned of the mythical Paint.NET. The aforementioned main menu also looked pretty good for a prototype build:</p>

<p><img src="/blogImg/008/eax2-menu.png" alt="Endless Attack X attempt 2" /></p>

<h5 id="take-note-of-how-this-looks-it-comes-up-again-later">Take note of how this looks, it comes up again later.</h5>

<p>Gameplay looked mostly the same:</p>

<p><img src="/blogImg/008/eax2-gameplay.png" alt="Endless Attack X attempt 2" /></p>

<p>Later on in the Fall, I tried <em>yet again</em> to create EAX, this time in GameMaker Studio instead of Stencyl. The only reason I did this was due to performance concerns I had in Stencyl when dealing with large amounts of on-screen objects, which occurred very frequently in EAX. GMS did, in fact, handle large amounts of objects very well, but unfortunately this time I only implemented the first 4 waves before giving up. I <em>did</em> try a new control scheme with this version after playing some twin-stick shooters, though. In <em>Endless Attack</em> and prior versions of <em>X</em>, the move and aim buttons were the same: the arrow keys. This meant that, in order to shoot (space) at an enemy, the player would need to move in order to aim towards it. This resulted in a lot of running-directly-into-enemies,-not-actually-shooting-them,-then-having-to-run-back-into-them-again, or as I like to call it, "panic shooting". It was annoying. The GMS version of EAX was much better; movement was still down to the arrow keys, but aiming was moved to WASD. This wasn't perfect, though; you still had to use space to shoot, and having your thumb repeatedly pressing it just wasn't comfortable.</p>

<p><img src="/blogImg/008/eax-gms.png" alt="Endless Attack X GMS" /></p>

<h5 id="the-third-attempt-at-making-eax-yes-thats-motion-blur">The third attempt at making EAX. Yes, that's motion blur.</h5>

<p>By this point I had already gone through 3 attempts, none of which came close to the original game. I tried (and failed) again in late 2018, going back to Stencyl and creating a version that used pixel art instead of anti-aliased, high-res graphics. I only got as far as implementing basic enemy logic in this version, but it was notable for introducing a new player character that wasn't a top-down view of a stick figure.</p>

<p><img src="/blogImg/008/eax4-planning.jpg" alt="Endless Attack X development notes" /></p>

<h5 id="development-notes-from-the-fourth-attempt-looks-like-i-was-pretty-geared-up-to-work-on-this-but-development-only-lasted-2-days-also-whyd-i-use-a-hashmap-for-wave-data">Development notes from the fourth attempt. Looks like I was pretty geared up to work on this, but development only lasted 2 days. (Also, why'd I use a hashmap for wave data...?)</h5>

<p><em>Four</em> attempts, then. Surely I'd have given up by this point. Except... I wasn't quite done. And to explain how I got to attempt #5, we need to look at my history with the library I'm using to develop <em>REMIX</em>.</p>

<h4 id="meanwhile">Meanwhile...</h4>

<p>In early 2019, I was playing around with my childhood PC, which I had recently gotten running again thanks to a PSU swap. I had the idea to try and experiment with 2000s-era game development, as after all, that's when the PC was from. I don't quite remember how I knew about it beforehand, but I remembered a library for game development called <a href="https://liballeg.org" target="_blank" rel="noreferrer noopener">Allegro</a>. I decided to try out version 4 of the library, which was in its prime by the 2000s. I only made a small "hello world" program before deciding I'd had my fun, and quickly went back to the comfort of modern gamedev.</p>

<p><img src="/blogImg/008/allegHewwo.jpg" alt="Allegro 4 hello world" /></p>

<h5 id="yikes">...yikes!</h5>

<p>Later on in the year, I decided to try and create my own game engine... which, if you've already read through the <a href="/programming.html" target="_blank">programming page of my website</a>, you'll know how that ended up. Before I decided to write the engine under OpenFL using Haxe, I attempted to write it under Allegro 5 with C++. This... didn't last long, as I only had a few weeks of experience with C++ and wasn't very used to how it handled OOP.</p>

<p>By the time it was early 2021 and I had installed DOS on that childhood PC from earlier, Allegro quickly jumped to the forefront of my mind. Thanks to messing with it twice now, I knew that Allegro (specifically Allegro 4) <em>had</em> support for DOS. What I <em>didn't</em> know up until then was that it was originally <em>made</em> for running under DOS², unlike my previous assumption that it was some half-baked port. So, with nothing better to do, I decided I would try and write a quick demo with Allegro 4 in C. Figuring out how to get the tools up and running was simple; someone had already put together an <a href="https://www.mrdictionary.net/allegro/" target="_blank" rel="noreferrer noopener">Allegro + DJGPP bundle</a>, making compilation extremely easy. After spending a bit of time reading up on Allegro documentation, namely how to go about safely initializing the library, as well as the API references for bitmap drawing, I had a small demo up and running!</p>

<p><img src="/blogImg/008/allegdemo.png" alt="My simple Allegro 4 demo" /></p>

<h5 id="hard-to-tell-since-its-an-image-but-those-are-bouncing-around">Hard to tell since it's an image, but those are bouncing around!</h5>

<p>I was having a lot of fun. I really wanted to keep going. This was unfortunate, since I had already started a lot of throwaway projects around this time, such as a game made in HyperCard, a minesweeper clone, and a couple of TI calculator apps, so I was a bit wary of starting anything new. Making something bigger than a small demo- in a language I was only slightly familiar with- seemed like a recipe for disaster. Regardless, I decided to shelve any extraneous projects I had going on at the time, leaving just ExaStar (which was, and still is, barely alive), VixenKit, any small 1-or-2 day projects that could cross my mind, and this new DOS game. But... what would this new game be?</p>

<h4 id="its-all-coming-together">It's all coming together</h4>

<p>Somehow, everything fell into place, and I had my answer almost immediately: <em>Endless Attack X</em> would be developed as a DOS game using Allegro 4, written with C, and under a new title: <em>Endless Attack REMIX</em>. The new title sort of alludes to my feelings towards the relationship between the original game and its "sequel"; <em>REMIX</em> is, technically, what the original <em>should</em> have been, given more development time and experience. It's not exactly a sequel in that regard, more of a "half-sequel". The "X", to me at least, just made the game sound more like a true sequel, thus not really fitting. But <em>REMIX</em> is still <em>Endless Attack X</em> at the end of the day, as you'll see; in fact, a lot of <em>REMIX</em>'s source code still refers to the game as "EAX" in places where a short acronym is needed (because EAR just sounds weird).</p>

<h3 id="deciding-requirements-or-how-low-can-i-go">Deciding requirements, or "how low can I go"</h3>

<p>One thing every game developer or studio needs to consider when first making a game is what platform (or platform<em>s</em>) the game will be distributed for, and if the game will even be a good fit for it (...or them). I had to think long and hard about <em>which</em> DOS systems I wanted to support, as "DOS" in general is a pretty broad platform. You could almost call each CPU family a platform <em>within</em> DOS. Anything like a Pentium III or newer was a safe bet, considering I have a PIII system ready to test the game on in case I need to, and they're just plain overkill for any DOS game. But systems with a PIII typically run Windows 98, 2000, or XP (or a *nix!), and of those three (four?), only 98 has DOS support. The PIII seemed just a bit too new for me to be targeting, so I decided that I would at least want the game playable on a Pentium II.</p>

<p>But that steep of a requirement for a DOS game still bugged me. Surely the game could run on something <em>older</em>, right? I proceeded to do some tests using a modified version of the small demo I made prior, with double-buffering added in. Since I didn't have any hardware slower than a PIII on hand (still don't, anyone wanna donate me a 486 or Pentium system?), I had to use DOSBox, which is where I usually test the game. DOSBox offers the ability to limit the number of clock cycles, and there's even <a href="https://www.dosbox.com/wiki/Performance#Emulated_CPU_equivalency" target="_blank" rel="noreferrer noopener">a list</a> with approximate cycle counts to run it at the speed of various CPUs, so I started messing with the number of bitmaps to draw at different cycle counts.</p>

<p>Beginning with 100 bitmaps at the speed of an average PII, I slowly decreased the cycle count. The demo, surprisingly, continued to run at 60fps when clocked at average Pentium speeds. I started going lower to see at <em>what</em> point the demo would drop down to 30fps. That point was, shockingly, halfway between a 66MHz 486DX and a 100MHz Pentium. This is when I finally decided to look into something I saw in the Allegro API: RLE sprites. According to the API, these would draw much faster than bitmaps do.</p>

<p>...But I clearly wasn't prepared for <em>how</em> fast. After learning about the tradeoffs (you can't scale, rotate, or draw on an RLE sprite, fine by me) and familiarizing myself with the functions, I tweaked the demo once more to convert the bitmap to an RLE sprite before drawing. I ran the demo again, and started to decrease the cycle count. It ran at 60fps at 486 speeds! It continued to run at 60fps up until somewhere between a 66MHz 486DX and a 33MHz 386, so it was clear that a game like <em>REMIX</em> could run comfortably on most 486 systems. I say "most" since both Allegro and the game itself require an FPU, however the game can be built with floating point emulation if needed. This does incur a performance penalty, though given how many sprites even a slower 486+FPU could handle, the game should probably still run at a solid 30fps.</p>

<p>I have to take these results with a huge grain of salt, though. At the end of the day, DOSBox is an emulator, and an emulator is... well, an emulator. <em>Not</em> real hardware. DOSBox doesn't emulate slower memory speeds, so the demo was able to use 2017's memory speeds while running at 1991's CPU speeds. Not the best scenario for testing, but until I can run the game and/or demo on a real 486, I'll have to work with the estimated speed. The demo tests also didn't take into account the runtime needed for game procedures, but I'm hoping that's not very significant compared to sprite rendering.</p>

<p>As for hardware outside of the CPU, I decided to go with the lowest common denominator for video and audio support. The game will run on any standard VGA-compatible display adapter, given it provides mode 13h. That's a graphics mode with a resolution of 320x200 and support for 256 colors, if you weren't aware. This is actually the second most primitive video mode that Allegro supports (text mode being the first), so hardware support should be pretty high. Likewise with audio; I'm writing the music with AdLib and Sound Blaster cards in mind, which many DOS sound cards strove to be compatible with.</p>

<p>I'm not too certain on RAM requirements just yet. It seems like sound effects will use the most memory, so I'll need to limit how many I have loaded in at any given time. I have ideas in mind for determining which spritesheets should be loaded and unloaded during gameplay (the menus are pretty easy to manage), so memory usage from sprites shouldn't be too high (though I do use some massive sprites at times...). Memory usage from music isn't a concern, since MIDIs only take up ~30KB at a time.</p>

<p>Now, DOS isn't a particularly accessible game platform nowadays. Selling <em>REMIX</em> as a DOS-only game would limit the game to people who already know how to use DOSBox, or those with a PC running DOS (we exist! in small numbers!). Here's where Allegro really shines; I don't <em>need</em> to limit the game to DOS. In fact, here's where it could run, provided I take the time to test for any platform-specific quirks:</p>

<ul>
  <li>DOS, which is the main development target</li>
  <li>Windows (95 and later, still haven't tested anything newer than XP!)</li>
  <li>macOS (10.1 and later, up until and including 10.14; 10.15 and later need to run the game in a customized DOSBox wrapper, unfortunately)</li>
  <li>Linux (tested recently)</li>
  <li>BeOS/Haiku and QNX (maybe there's a market...?)</li>
</ul>

<p>So most PCs should be able to run the game, and the hope is that I can distribute versions of the game to run on modern systems alongside the original DOS version.</p>

<h3 id="development-thus-far">Development thus far</h3>

<p>The very beginning of development on <em>REMIX</em> consisted of further experiments using the demo I made as a base. First I started with getting a single bitmap from a spritesheet.</p>

<p><img src="/blogImg/008/remix-test1.gif" alt="Endless Attack REMIX early testing" /></p>

<p>That was simple enough, so I turned my attention towards music (MIDI) playback. Also easy. Then I got into more advanced work; things such as double-buffering and redirecting the game to a (placeholder at the time) setup screen when given the right argument.</p>

<p><img src="/blogImg/008/remix-setup.gif" alt="Endless Attack REMIX early testing" /></p>

<p>I took a break for a few months, but when I returned I realized the single source file got a bit too unruly, so I broke the source up and created a state machine for the game's room/scene system. This immediately made development feel a bit more "at home"; each state has an enter and update function, much like how I'm used to Stencyl's scripts containing essentially the same functions. Different, however, is the distinction between "updating" and "drawing"; Stencyl has a special function per script where primitive drawing should take place (things such as lines, squares, etc). This is due to the fact that Stencyl has separate update and render timings; while the game updates much more than 60 times per second, drawing/rendering only needs to happen on each frame, so the two run at different rates. In my "engine", both updating and rendering take place at the same time. This is partly because having more updates per second than the current framerate is just a bit too much for a PC running DOS, and partly because it makes things simple for me. Luckily, because updating and rendering happen at the same time, I can make an "on draw" function per state and have the update function call that.</p>

<p>With the state machine out of the way, development got even faster on the front end: I made the intro screen for my development studio, the title screen, and then the main menu.</p>

<p><img src="/blogImg/008/remix-title.png" alt="Endless Attack REMIX title" /></p>

<p><img src="/blogImg/008/remix-menu.png" alt="Endless Attack REMIX menu" /></p>

<p>Then I moved back to more structural additions, such as defining gameobjects and linked lists, in order to add the player and bullet shooting. Instead of using space to shoot like in the GMS version of EAX, shooting became automatic when holding any of the aim keys. It was an immediate improvement. I also took the time to add in the setup screen.</p>

<p><img src="/blogImg/008/remix-setup.png" alt="Endless Attack REMIX setup" /></p>

<p>Afterwards, I took a few more months off, before coming back to rework the build system (more on that later) and write this post. And that's pretty much where I'm at now!</p>

<h3 id="immediate--future-plans">Immediate &amp; future plans</h3>

<p>As for things I'm working on right now:</p>

<ul>
  <li>Dynamic arrays. Far from the hardest thing to implement in C, but I'm writing one to support...</li>
  <li>Spritebanks, to hold sprites loaded in from a sprite sheet in a more easily-addressable format</li>
  <li>A better sprite loading system; I've taken care to optimize the game as much as I can early on, and one of those optimizations is, as I mentioned, the use of RLE sprites. However, it seems their speed benefit is either negligible or detrimental on other platforms, so I'll need a way to switch between using RLE sprites and bitmaps at compile-time before it becomes <em>too</em> daunting to add.</li>
  <li>Dynamic framerate switching; I have an idea for how this will work, but I fear it might not work as well as I want it to... we'll see!</li>
</ul>

<p>And more general plans for the future:</p>

<ul>
  <li>Obviously, getting the core gameplay in: enemies, waves, shop, etc.</li>
  <li>A co-op mode- yes, my little experiment from the game's first incarnation is a key part of the roadmap!</li>
  <li>An interactive tutorial and practice mode</li>
  <li>Adding in other unlockable modes after that</li>
  <li>When the game is complete, a physical release?</li>
</ul>

<p>The <em>ideal</em> goal is to get the game finished, or in a near-finished state, by the time VCFMW 17 rolls around (at least I hope it happens this year... please get vaxxed). This is so I can set up a playtesting table to get some feedback and generally get the word out about the game to other DOS gamers. But given how often I lose focus, that definitely won't happen in time. So the <em>minimum</em> goal is that I get the first 30 waves implemented, including co-op support, by that time. It's a daunting task and I still have a lot of doubt that I'll make it (thanks to uni taking up much of my time), but we'll see!</p>

<h3 id="the-workflow">The workflow</h3>

<p>Ooh, the fun part! Here I get to explain <em>how</em> I'm making a DOS game in 2022 (technically 2021 as well). As mentioned earlier, I typically test the game in DOSBox, only rarely copying the game to real hardware. At first, I would compile the game in DOSBox as well; but this process was slow, as GCC had to run with the overhead of emulation. It made complete recompiles of the game take around a full minute, and the game was only <em>starting</em> development at that point! So a month or two ago I looked into cross-compilation; luckily, a <a href="https://github.com/andrewwutw/build-djgpp" target="_blank" rel="noreferrer noopener">cross-compiler <em>specifically</em> for DJGPP on DOS</a> exists, and after a couple hours of tinkering and writing a new makefile, compilation could then be done outside of DOSBox. Full recompiles now take just a few seconds at most!</p>

<p>I program the game inside of Visual Studio Code; I've found it integrates very nicely with DOS development. As long as I have the C extension set up to use the cross-compiler, and have the include path pointing towards Allegro's headers, I get proper syntax highlighting and IntelliSense while writing. I can even build the game from VSCode and have it launch the game in DOSBox for quick testing.</p>

<p>Making graphics for the game couldn't be any easier; I'm able to use my favorite sprite editor, Aseprite, to create BMP files compatible with the game. You might be thinking, "any image editor can export a BMP, so what do you mean by 'compatible'?" Allegro only expects a 256-color BMP, and having more or less colors in one will crash the game. Aseprite comes with a really good palette system, so I'm able to save a BMP with a 256-color palette- and, get this- it comes with the default VGA palette. It's a pretty versatile palette, and while I <em>could</em> have my own palette for the game if I really wanted to, I just find the default one easy to work with. So making graphics for the game is pretty much the same process as any other game I would make; I just need to switch palettes beforehand.</p>

<p>Music is a tricky area that I haven't completely figured out yet. Thus far I've only made a couple of tracks for the game, and getting them to loop properly + have the correct instruments was a bit of a chore. It doesn't help that I made them using Logic Pro, which I don't have ready access to since switching to Linux a few months ago (rebooting my MacBook into macOS just for Logic is a bit annoying...). So, I have to relearn the process of getting program changes (in non-MIDI terms, let's call those "instrument mappings") and loop points correct in a different DAW, like LMMS. While we're on the topic of music, I asked my girlfriend for a bit of help on this front. She started writing music while I was taking a long break from doing so, and as a result, she's now a bit more skilled than I am. The only problem is, our songwriting styles are completely different! While I favor strong melodies with not many effects added on top, she tends to write faster songs with shorter, repeating melodies, better drumlines, darker tones, and <em>lots</em> of VSTs. Since I want the game to have a more arcadey soundtrack, we need to somehow find a way to blend our styles together. She also has to avoid reliance on VSTs for a bit, since the soundtrack needs to be pure General MIDI!</p>

<p>Sound effects are just heavily-crushed WAVs in order to save on memory usage. Nothing special there.</p>

<h3 id="in-conclusion">In conclusion</h3>

<p><em>REMIX</em> won't be a hard game to make. The concepts I made for it are almost 8 years old (ooof...) by this point, so I know exactly what I want to make here. The only "real" hard part is getting used to C, but I've definitely already learned a lot and gotten used to non-object-oriented programming. I suppose the main hurdle for me, then, isn't the game itself; rather, staying focused on it and trying not to get distracted by side-projects. Lest it ends up like ExaStar³; I wouldn't really want that to happen again.</p>

<p>I hope to come back with another journal a month or two from now, and if that doesn't happen... then hey, <a href="/contact.html">bug me</a>, why don't you?</p>

<hr />

<p>1: I'd love to release it, and had plans years ago to do so, but unfortunately 2014 me had the brilliant idea of putting my old website link (deadname included) on the title screen. In 2019 I tried reverse engineering the game (again, don't have the project file) to try and edit it out. I got as far as extracting the title screen's background and converting it to and from a PNG, but my efforts to stuff it back into the executable were fruitless. There are supposedly some ways to decompile MMF2 games, but all I could find were either incomplete tools, FNAF-specific tools (yes, same engine), or tools for MMF2.5. Maybe some day I'll try again...</p>

<p>2: Allegro wasn't <em>originally</em> a DOS library. It actually started out on the Atari ST! But not much was done for that platform compared to DOS, so I like to say it was originally a DOS library just to keep things simple.</p>

<p>3: I'm still in the process of getting Dev Journal 8 out for that. I started writing it all the way back in <em>2019</em>, but ended up leaving it around 3-quarters of the way done. Then I had to "soft-restart" development <em>twice</em>. So now I'm left trying to figure out how to reformat the whole thing with those restarts (plus any new developments from the past ~3 years) in mind.</p>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[So! It appears it's time for me to start yet another series of development journal posts, this time for my next-but-before-ExaStar game, Endless Attack REMIX! Much like ExaStar's first development journal, I'll start with a lengthy amount of history behind the game, then spend some time talking about what I have worked on thus far. Let's go!]]></summary></entry><entry><title type="html">Post-what? - Using PostPet in 2022</title><link href="http://kokoscript.com/2022/007.html" rel="alternate" type="text/html" title="Post-what? - Using PostPet in 2022" /><published>2022-01-05T00:00:00-06:00</published><updated>2022-01-05T00:00:00-06:00</updated><id>http://kokoscript.com/2022/007</id><content type="html" xml:base="http://kokoscript.com/2022/007.html"><![CDATA[<p>残念ながら、私は機械翻訳の助けなしに日本語を書くことはできません。 そのため、このガイドは英語でのみご利用いただけます。 <a href="https://kokoscript-com.translate.goog/2022/007.html?_x_tr_sl=en&amp;_x_tr_tl=ja&amp;_x_tr_hl=en" target="_blank" rel="noreferrer noopener">機械翻訳版を読むことはできますが、正確ではない場合があります。</a> 謝罪いたします。</p>

<h3 id="contents">Contents</h3>
<ul>
  <li><a href="#intro">Introduction</a>
    <ul>
      <li><a href="#intro-1">1 year later...</a></li>
      <li><a href="#intro-2">There's more than just the Pocket PostPet</a></li>
      <li><a href="#intro-3">So, why doesn't PostPet work as-is?</a></li>
    </ul>
  </li>
  <li><a href="#req">Requirements</a></li>
  <li><a href="#s1">Step 1: Getting Your Account Ready</a>
    <ul>
      <li><a href="#s1-1">Generating an app password for Gmail</a></li>
      <li><a href="#s1-2">Enabling POP3 for Gmail</a></li>
    </ul>
  </li>
  <li><a href="#s2">Step 2: Setting up the Proxies</a>
    <ul>
      <li><a href="#s2-1">SMTP Proxy</a></li>
      <li><a href="#s2-2">POP Proxy</a></li>
    </ul>
  </li>
  <li><a href="#s3">Step 3: Setting up PostPet</a>
    <ul>
      <li><a href="#s3-1">V2 Setup</a></li>
      <li><a href="#s3-2">Distinction between the "Call Pet" and "Call Postman" buttons</a></li>
      <li><a href="#s3-3">The pet simulator side of things</a></li>
      <li><a href="#s3-4">Japanese V2 Setup</a></li>
      <li><a href="#s3-5">V1 setup</a></li>
    </ul>
  </li>
  <li><a href="#end">Closing Notes</a></li>
  <li><a href="#more">Bonus: Other Mail Software</a></li>
  <li><a href="#credit">Credits/Sources/More info</a></li>
</ul>

<hr />

<h3 id="introduction">Introduction</h3>
<div id="intro" class="anchor"></div>
<p>I suppose I should provide some backstory as to why I'm writing this guide.</p>

<p>A little over a year ago, I learned about the Pocket PostPet through <a href="https://twitter.com/FroyoTam/status/1327710741130928128" target="_blank" rel="noreferrer noopener">a Twitter post by Froyo Tam</a>, showing some homebrew software running on the device. I was immediately in love with it; it was unlike anything I had seen before! A small, handheld computer, running Windows CE, with- most importantly- pink accents all over the place. I was intrigued, so I proceeded to search for more information about the device... however, to my surprise, English documentation regarding the device was <em>extremely</em> scarce. In all, there was:</p>
<ul>
  <li><a href="https://www.hpcfactor.com/forums/forums/thread-view.asp?tid=10893&amp;start=1" target="_blank" rel="noreferrer noopener">a forum thread in which someone documented what software they were running on their imported Pocket PostPet</a>,</li>
  <li><a href="https://wiki.squeak.org/squeak/1685" target="_blank" rel="noreferrer noopener">information on a port of Squeak to the device</a>,</li>
  <li><a href="https://twitter.com/FroyoTam/status/1291107161653653504" target="_blank" rel="noreferrer noopener">and earlier posts about the device from Froyo</a>.</li>
</ul>

<p>This was a bit disappointing, yet understandable seeing as the device was only ever sold in Japan. In all, I was able to deduce that the device was a combination dedicated email terminal and pet simulator. Cute! But... I <em>still</em> wanted to know more. So, what was the next best thing I could do to learn more about the device, other than meticulously putting Japanese websites through machine translation?</p>

<p>Order one.</p>

<p>To my surprise, Yahoo Auctions had a listing for a Pocket PostPet, complete in box, and in <em>very</em> good working condition (except the battery, but that's to be expected nowadays...). Better yet, it was at a reasonable price, even after factoring in shipping costs from Japan to the US. So, without hesitation, I got one ordered as a Christmas gift to myself. In the meantime, I didn't do much research on the device; besides, I'd learn more about it when it arrives, right?</p>

<h4 id="1-year-later">1 year later...</h4>
<div id="intro-1" class="anchor"></div>
<p>Okay, so it didn't exactly arrive on time... And given the pandemic, I had already expected some sort of delay. But a whole year...? I had almost completely forgotten about the Pocket PostPet by this point, when suddenly I got a notification that the shipping proxy had it in escrow, and were waiting for me to pick a shipping option. Finally!</p>

<p>So, Christmas 2021 rolls around, and I finally have a Pocket PostPet for myself. Upon doing another search with rekindled interest in the device, I saw that nothing had changed in terms of English documentation for the device. So, after exploring what little I could of it, I set it upon myself to provide extensive documentation of the device. Except...</p>

<h4 id="theres-more-than-just-the-pocket-postpet">There's more than just the Pocket PostPet</h4>
<div id="intro-2" class="anchor"></div>
<p>I forgot how I came to learn about this- perhaps it was through doing a Japanese Google search for "pocket postpet" and translating the Wikipedia page for "PostPet"- but the Pocket PostPet wasn't just some standalone product. PostPet was actually a series of email clients for Windows and MacOS <em>before</em> getting its own, standalone email device. Now, one interesting thing to note about the Pocket PostPet is that the only connection it has to the outside world is via a PDC phone connector. This particular connection is found primarily on cell phones sold by NTT DoCoMo back in the early 2000s, as a way for mobile devices to connect to the internet via a cell phone. Think of it as a primitive form of "Mobile Hotspot", just without the wireless-ness. Now, here's where the problem lies: I don't have a Japanese cell phone from the early 2000s. And if I did, there's another problem: I don't live in Japan. And even if I did, there's one last problem: NTT axed the network these phones used for this functionality back in 2010. So, by all means, the Pocket PostPet can no longer be used for what it was intended for: sending and receiving emails.</p>

<h4 id="so-why-doesnt-postpet-work-as-is">So, why doesn't PostPet work as-is?</h4>
<div id="intro-3" class="anchor"></div>
<p>After learning about the Windows and MacOS versions of PostPet, I had a bit of hope. No weird connector means it should pretty much "just work" with any mail server over a standard internet connection, right?</p>

<p>Well, mostly. Remember, PostPet (at least the versions I'll be covering here) is software from the late 90s, and as such, it was built with mail servers from that time period in mind as well. And you know what that means: no secure connection requirements! Since every (good) email provider nowadays requires a secure connection to its servers in order to send and receive mail, I needed to work around this requirement. Here's what I came up with.</p>

<hr />

<h3 id="requirements">Requirements</h3>
<div id="req" class="anchor"></div>
<p>To follow this guide, you'll need:</p>
<ul>
  <li>PostPet V1 or V2 (... which can be found in <a href="https://archive.org/details/postpet-archive" target="_blank" rel="noreferrer noopener">this archive I made</a> of all the trial versions I could pull from the Wayback Machine. English readers may specifically want to follow along with the "pp214enutrial.exe" executable.)</li>
  <li>Windows 95 or later, or Wine on Linux (If you're going to use the Japanese versions of PostPet, then those require a Japanese version of Windows. I haven't been able to get Japanese PostPet working on Wine, even with <code class="language-plaintext highlighter-rouge">LC_ALL=ja_JP.utf8</code>.)</li>
  <li>A Linux server (preferably Debian or Ubuntu) on your <strong>local network</strong>
    <ul>
      <li>I cannot stress this enough: the server <strong>needs</strong> to be on your local network, otherwise you risk sending your emails unencrypted over the internet. You could probably use a VPN to a remote server, but I'm not going to cover that setup.</li>
    </ul>
  </li>
  <li>An account with an email provider that supports SMTP and POP3 connections (for example, Gmail)</li>
</ul>

<h3 id="step-1-getting-your-account-ready">Step 1: Getting Your Account Ready</h3>
<div id="s1" class="anchor"></div>
<p>Initially I was <em>going</em> to start with instructions on setting up the proxy services, but making sure your existing mail account will work in the first place is a bit more important. If you're using Gmail, I can already confirm that it will work, and I'll be using it as an example in this guide. But if you're using a different provider, you'll need to make sure it allows the following:</p>
<ul>
  <li>Checking for mail via POP3</li>
  <li>Sending mail via SMTP</li>
  <li>Login from "insecure" clients (in other words, the ability to use app-specific passwords while 2FA is enabled)</li>
</ul>

<p>If you're not using Gmail, you'll unfortunately have to look for instructions on enabling these things on your own. If you <em>are</em> using Gmail, then follow along.</p>

<h4 id="generating-an-app-password-for-gmail">Generating an app password for Gmail</h4>
<div id="s1-1" class="anchor"></div>
<p>If you don't have two-factor authentication enabled on your Google account, all you need to do is <a href="https://myaccount.google.com/lesssecureapps" target="_blank" rel="noreferrer noopener">visit this page</a> to enable logging in from insecure clients. This isn't really recommended though; I strongly suggest looking up how to enable 2FA on your account first. If you already have 2FA enabled though, you'll need to head to <a href="https://myaccount.google.com/security" target="_blank" rel="noreferrer noopener">your Google account's security settings</a> and click the "App Passwords" button.</p>

<p><img src="/blogImg/007/googleAppPassword.png" alt="Google's app password creation page" /></p>

<p>On this page, select "Mail" on the "Select app" dropdown, select "Other" on the "Select device" dropdown, and enter something like "PostPet" for the name. Click "Generate". You'll be presented with a password that can be used for PostPet to log into your account; copy this password somewhere else first, as this is the only time you'll be able to see it!</p>

<h4 id="enabling-pop3-for-gmail">Enabling POP3 for Gmail</h4>
<div id="s1-2" class="anchor"></div>
<p>Once this is done, head over to Gmail. Click the gear icon in the top right corner, then "See all settings". Go to the "Forwarding and POP/IMAP" tab. Here we'll be able to configure how Gmail will behave when accessed via PostPet.</p>

<p><img src="/blogImg/007/gmailFullOption.png" alt="Gear icon in Gmail, with &quot;See all settings&quot; button below" />
<img src="/blogImg/007/gmailPopOption.png" alt="Gmail's POP settings" /></p>

<p>If your account has a lot of mail in it, it might be a good idea to select "Enable POP for mail that arrives from now on", as I don't know what kinds of limits PostPet has when it comes to large amounts of mail. But if you keep your account relatively clean, you can select "Enable POP for all mail". There's also an option to choose what will happen with your mail when it is accessed via POP. I strongly recommend "keep Gmail's copy in the inbox".</p>

<p>And with that out of the way, your Gmail account is (almost) ready to be used with PostPet!</p>

<h3 id="step-2-setting-up-the-proxies">Step 2: Setting up the Proxies</h3>
<div id="s2" class="anchor"></div>
<p>If PostPet can only connect to mail servers with no security, then we'll need to use something that can take PostPet's connection as an input and pass it along to a email provider with the proper security. That's where a proxy comes in. In our case, we'll use two different proxies: one to handle SMTP connections, and one to handle POP3.</p>

<h4 id="smtp-proxy">SMTP Proxy</h4>
<div id="s2-1" class="anchor"></div>
<p>Let's start with the SMTP proxy. We'll be using <a href="http://emailrelay.sourceforge.net/" target="_blank" rel="noreferrer noopener">emailrelay</a> for this. Download its package from <a href="https://sourceforge.net/projects/emailrelay/files/emailrelay/2.2/" target="_blank" rel="noreferrer noopener">this page</a>, using the .deb if you're on Debian/Ubuntu, the .rpm on Fedora, or compile it from source via the .tar.gz. In my case, I used the .deb, and installed it on my server via <code class="language-plaintext highlighter-rouge">sudo dpkg -i emailrelay_2.2_amd64.deb</code>. If you're on Fedora (or worse, need to compile the proxy from source), you're on your own!</p>

<p>After emailrelay is installed, we'll need to configure a few things. First, emailrelay will already be running; we'll need to stop it, so use <code class="language-plaintext highlighter-rouge">sudo service emailrelay stop</code>. Next, we'll edit emailrelay's config file with <code class="language-plaintext highlighter-rouge">sudo nano /etc/emailrelay.conf</code>. Go to the bottom of the file and add these lines, replacing the <code class="language-plaintext highlighter-rouge">as-proxy</code> and <code class="language-plaintext highlighter-rouge">client-auth</code> values accordingly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>as-proxy smtp.gmail.com:587
client-tls
client-auth /home/kokoro/secret
</code></pre></div></div>

<p>If you aren't using Gmail, replace <code class="language-plaintext highlighter-rouge">smtp.gmail.com:587</code> with the server and port your provider uses for SMTP. Then there's also the <code class="language-plaintext highlighter-rouge">client-auth</code> setting, which tells emailrelay where to find your login information. For this example, I placed it in my home folder with the filename <code class="language-plaintext highlighter-rouge">secret</code>. The contents of that file should look something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>client plain example@gmail.com YourPassword
</code></pre></div></div>

<p>Of course, replacing "example@gmail.com" with your own email address, and "YourPassword" with the app password you generated earlier (or your actual password if you don't have 2FA). All that's left is to start up emailrelay via <code class="language-plaintext highlighter-rouge">sudo service emailrelay start</code>, and the SMTP proxy is ready to go!</p>

<h4 id="pop-proxy">POP Proxy</h4>
<div id="s2-2" class="anchor"></div>
<p>One proxy down, one to go. For POP, we'll be using xinetd. This is already included in the repositories for many Linux distributions, so if you're on Debian/Ubuntu, <code class="language-plaintext highlighter-rouge">sudo apt install --no-install-recommends xinetd openssl</code> is all you need. On other distributions, the package names could be a bit different. Now, we'll need to configure xinetd to handle our POP connection; to do this, create a file at <code class="language-plaintext highlighter-rouge">/etc/xinetd.d/pop2pops</code> with the following contents:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#
# Description:
# A service tunneling POP3 requests to mail.example.com via TLS, making
# it a POP3S connection. Purpose is to allow old email clients not supporting
# modern TLS to mail servers requiring it.
#
service pop2pops
{
  type            = UNLISTED
  flags           = NORETRY
  socket_type     = stream
  protocol        = tcp
  wait            = no
  user            = nobody
  instances       = 10
  server          = /usr/bin/openssl
  server_args     = s_client -connect pop.gmail.com:995 -quiet -verify_quiet
  port            = 110
#  log_type        = FILE /var/log/pop2pops.log
#  log_on_success  = PID HOST USERID DURATION
#  log_on_failure  = HOST USERID
}
</code></pre></div></div>

<p>I should probably mention by now that this config is originally from <a href="https://www.jump-ing.com/traumflugs/simple_pop3_proxy" target="_blank" rel="noreferrer noopener">this guide here</a>, so kudos to the author. Again, if you aren't using Gmail, replace <code class="language-plaintext highlighter-rouge">pop.gmail.com:995</code> with the server and port your provider uses for POP. Restart xinetd with <code class="language-plaintext highlighter-rouge">sudo service xinetd restart</code>, and we're all done setting up the proxies! Now, on to PostPet.</p>

<h3 id="step-3-setting-up-postpet">Step 3: Setting up PostPet</h3>
<div id="s3" class="anchor"></div>
<p>I'll go over setting up 3 different versions of PostPet: The Japanese-only V1, and both the Japanese and English versions of V2. I'll primarily be covering the English version of V2 here. It's very similar to the Japanese version, but with one slight difference. I've listed where this difference is in the English version's setup guide, so follow it even if you're using the Japanese version.</p>

<h4 id="v2-setup">V2 setup</h4>
<div id="s3-1" class="anchor"></div>
<p>Run the setup executable for the version of PostPet you wish to use. After the installation finishes and PostPet is launched, you'll be greeted with this setup prompt.</p>

<p><img src="/blogImg/007/ppEnSetup.png" alt="PostPet english setup" /></p>

<p>Simply put the address of your server into the SMTP and POP server fields, the part of your email address before the "@" into the POP account field, and the generated app password (or your actual password if not using 2FA on Gmail) into the password field. (This is where the Japanese version differs a bit, <a href="#s3-4">check this part of the guide before continuing.</a>) The email address and real name fields are self-explanatory. I also recommend setting "Keep Mail on Server" to "Yes". After this is done, you'll get to select your pet. This part is pretty self-explanatory too. Once that's done, you'll arrive at the main interface!</p>

<p><img src="/blogImg/007/ppEnUi.png" alt="PostPet english interface" /></p>

<p>To make sure the POP proxy is working, click the "Check Mail" button. If it worked, your emails should show up in the Inbox! If it didn't work, make sure the POP proxy is set up properly; you can also troubleshoot the server using the guide I linked earlier. If you get a message like "You have no mail", then you most likely have POP set up such that only new messages will get downloaded. Wait for some mail (or send mail to yourself) before trying again.</p>

<p>Sidenote: I ran into a bizarre issue with Gmail where PostPet reported that I had no mail, when my POP settings were set to get all mail in my inbox. What I had to do was log into Gmail on my browser, which gave me a weird "your account is unavailable" error; clicking the "Try Again" button there fixed it. So if you get a "No mail" report from PostPet, try logging into Gmail from a browser and try again.</p>

<p>The last thing to check is the SMTP proxy. Click the "Write Mail" button to bring up the composer, specify an email address (could be yourself or a friend) and subject, and write something in the message box. When you're ready, click the "Call Postman" button to send your message. If all goes well, you should be able to check your sent messages via a browser and see the message you've sent via PostPet. If it didn't work, make sure you set up the SMTP server correctly.</p>

<p>And that's pretty much all there is to it! If both sending and receiving mail works, then PostPet is fully functional, aside from the 10-day trial limitation.</p>

<h4 id="distinction-between-the-call-pet-and-call-postman-buttons">Distinction between the "Call Pet" and "Call Postman" buttons</h4>
<div id="s3-2" class="anchor"></div>
<p>When in the composer, you may have noticed two different "Send" buttons: One for a pet, and one for the Postman. What's the deal with that?</p>

<p>Well, if you know someone else that uses PostPet, you can send your pet to visit them. It essentially sends a regular email to the other person, but with a .ppd file containing information about your pet. Once the recipient checks their mail, your pet will show up on their screen, play with their pet, and then leave. This then sends an email from the recipient back to you, letting PostPet know that your pet is back. Otherwise, your pet will still come back after some amount of time has passed without a reply.</p>

<p>Now, sending this weird ppd file to people that don't use PostPet can be a bit annoying; for one, your pet won't come back for a while, and the recipient didn't want that pet file in the first place! That's where the Postman comes in; it allows you to send plain old mail without your pet as an attachment.</p>

<h4 id="the-pet-simulator-side-of-things">The pet simulator side of things</h4>
<div id="s3-3" class="anchor"></div>
<p>I'm not really going to go into how the pet simulator works; that's for you to explore! But here are a few tips:</p>
<ul>
  <li>Clicking your pet will make them angry, only do it if they do something you don't want them to do</li>
  <li>Moving your cursor over your pet will pet them</li>
  <li>As your pet grows older, the types of foods they like will change</li>
</ul>

<h4 id="japanese-v2-setup">Japanese V2 setup</h4>
<div id="s3-4" class="anchor"></div>
<p>The Japanese version of PostPet is very similar to the English one, with a slight difference in the setup screen.</p>

<p><img src="/blogImg/007/ppJpSetup.png" alt="PostPet Japanese setup" /></p>

<p>Notice the lack of "POP username" field; instead, it goes into the same field as the server, like "username@your.server.address". Everything else from the English version is the same, so follow the instructions from earlier!</p>

<h4 id="v1-setup">V1 setup</h4>
<div id="s3-5" class="anchor"></div>
<p>The setup for V1 is very similar to the setup found in the Japanese version of V2, so you'll need to use the same POP server format.</p>

<p><img src="/blogImg/007/ppv1Setup.png" alt="PostPet V1 Japanese setup" /></p>

<p>Unfortunately, V1 seems to suffer from an issue where it can't receive any mail. This issue seems to be common with other old email clients I've tried, notably PinkRabbit, where the client will connect to the proxy just fine, but report back with a "no mail" message. Sending mail still works, though.</p>

<h3 id="closing-notes">Closing Notes</h3>
<div id="end" class="anchor"></div>
<p>Unfortunately, I haven't been able to get V3 of PostPet (which contains full 3D graphics) to work. After setting it up, the software simply errors and exits. I tried running it on a Japanese Windows XP install too, which is what it was originally designed to run on, but it still refuses to run. Perhaps it's a limitation of the trial, because the full version seems to work to this day on Windows 10. If you're curious, <a href="https://www.youtube.com/watch?v=sNZqvDzNJj0" target="_blank" rel="noreferrer noopener">here's a video of that</a>.</p>

<p>Speaking of trial limitations, I haven't found a way to disable PostPet's trial timer. It's 10 days long, which is enough to play around the software for a bit, but if you're <strong>serious</strong> about using PostPet, that timer will eventually become a problem. I'm sure I could spend a day with WinDbg or Ghidra and trick the trial versions of PostPet into getting registered, but I have more important things to do.</p>

<p>You're probably wondering: "Why did you go through the trouble of figuring out how to get some random old email software to work, and then write a whole post about it?" The answer is simple; I simply find PostPet interesting, and wanted to preserve it. It might not be important to you, but it's still a part of internet history, even if it's extremely unimportant compared to other old pieces of software like, for example, Netscape. Getting PostPet to work again and providing a guide on how to do so allows people who may have never heard of, and thus never used the software, to see what it was like with full functionality. It also allows those who have fond memories of talking with their friends via PostPet to experience it once again. That's the whole point of software preservation: ensuring software can still be used in future generations, without making any sacrifices along the way, so users both new and old can enjoy it.</p>

<p>And with that, PostPet is preserved and usable in the modern age... mostly. Unfortunately, all MacOS versions of the PostPet trial have been lost to time, as they were never archived and put into the Wayback Machine. Additionally, there are only 2 archives of full versions of PostPet distributed via CD (at least that I could find), and even then there are no scans of the box, manual, etc. So, if you have archives of any MacOS trial versions, or have created a complete copy of a full PostPet disc, please <a href="/contact.html">get in touch</a>- preferrably via PostPet ;)</p>

<p>If you're as interested as I am in PostPet, you'll be happy to know that I'm working on creating some more in-depth documentation, specifically for the Pocket PostPet. As I'm starting my next semester soon, this could take a while for me to get to, so please be patient in the meantime!</p>

<h3 id="bonus-other-mail-software">Bonus: Other Mail Software</h3>
<div id="more" class="anchor"></div>
<p>If you followed the guide to completion, then the proxies you have set up also work for other old email software! Here are some examples:</p>

<ul>
  <li><a href="https://jessicastokes.net/blog/2021/06/10/unlocking-pinkrabbit/" target="_blank" rel="noreferrer noopener">PinkRabbit</a> is a cute email client that uses the TML format for messages. Like PostPet V1, receiving mail doesn't work, but sending mail does.</li>
  <li>Classilla's mail client can also send mail using the SMTP proxy, but like PostPet V1, it can't receive mail. (It <em>does</em> have TLS/SSL support, which means it should work with Gmail without even needing a proxy, but it still returns no mail.)</li>
  <li>Same story with Microsoft Entourage 2001; sending works, receiving doesn't. I'm starting to notice a trend...</li>
  <li>The guide I used to set up the POP proxy mentions the Mail app for OS X 10.4, so you could use that (and some older versions, too). Except I wasn't able to get POP to work with it. Again.</li>
</ul>

<p>The fact that I got PostPet to work was enough for me. But now I'm curious as to why it worked with the POP proxy, while nothing else did. I'll have to do some more digging...</p>

<h3 id="creditssourcesmore-info">Credits/Sources/More info</h3>
<div id="credit" class="anchor"></div>
<p>The following pages were instrumental in getting things to work, shedding some light on PostPet, etc.</p>
<ul>
  <li><a href="https://twitter.com/FroyoTam/status/1291107161653653504" target="_blank" rel="noreferrer noopener">Froyo's original thread about the Pocket PostPet</a></li>
  <li><a href="https://archive.org/details/post-pet-2001" target="_blank" rel="noreferrer noopener">EarthlingPlant's archive of the Japanese PostPet V2 CD</a></li>
  <li><a href="https://ja-m-wikipedia-org.translate.goog/wiki/PostPet?_x_tr_sl=ja&amp;_x_tr_tl=en&amp;_x_tr_hl=en-US" target="_blank" rel="noreferrer noopener">The Japanese Wikipedia page about PostPet (machine-translated to English)</a></li>
  <li><a href="https://www.jump-ing.com/traumflugs/simple_pop3_proxy" target="_blank" rel="noreferrer noopener">The POP proxy guide</a></li>
  <li><a href="http://emailrelay.sourceforge.net/" target="_blank" rel="noreferrer noopener">emailrelay's documentation</a></li>
</ul>]]></content><author><name></name></author><category term="Software" /><summary type="html"><![CDATA[残念ながら、私は機械翻訳の助けなしに日本語を書くことはできません。 そのため、このガイドは英語でのみご利用いただけます。 機械翻訳版を読むことはできますが、正確ではない場合があります。 謝罪いたします。]]></summary></entry><entry><title type="html">The Y2Ktop Adventure (pt. 1)</title><link href="http://kokoscript.com/2019/001.html" rel="alternate" type="text/html" title="The Y2Ktop Adventure (pt. 1)" /><published>2019-08-25T00:00:00-05:00</published><updated>2019-08-25T00:00:00-05:00</updated><id>http://kokoscript.com/2019/001</id><content type="html" xml:base="http://kokoscript.com/2019/001.html"><![CDATA[<p><span style="color: red;">Warning from the future:</span> The writing style I had back when I wrote this is <em>atrocious</em>. Not to mention the subject matter here is a (relatively speaking) boring, generic, Pentium 3 laptop with nothing noteworthy about it. Neat to me back in 2019, sure, but after 4 years I've come to realize it's absolutely not worth a half-hour read (and especially not with the way I wrote it). You should consider reading the <a href="/computers/charcoal.html">"My Computers" writeup I did for this laptop</a> instead, but if not: don't say I didn't warn you!</p>

<hr />

<p>This story starts back in late 2016 – more precisely, October 2016 – when my aunt and uncle gave me 2 (rather dated) laptops. One was your standard, shoddy HP “Invent” laptop from the mid-2000s (which I wasn’t too thrilled to receive, considering my past experience with one that failed to boot after several months of use), and the other was… something quite unlike the other laptops I had ever owned before (not counting the iBook G3, but that’s for another story). Indeed, this laptop was actually a bit of a relic; it came straight from the year 2000!</p>

<p>Naturally, thanks to my passion for retro technology, this machine would be the one that became a favorite of mine. And… not because the other laptop had a damaged motherboard. Nope. Definitely not because of that.</p>

<p><img src="/blogImg/comp/charcoal/glamour.jpg" alt="Gateway Solo 2150 in all of its glory" /></p>
<h5 id="the-laptop-in-question-a-gateway-solo-2150-pretty-obscure">The laptop in question, a Gateway Solo 2150. Pretty obscure.</h5>

<h3 id="the-start">The Start</h3>
<p>Some backstory on the laptop: It was originally my aunt’s laptop, which she used throughout college – mainly for note-taking (I think) and writing papers. Installed on this laptop at the time it was flung into my possession was Windows 98SE, a perfect fit for the dated processor and limited amount of RAM it came with. The laptop also happened to have a floppy disk left in the FDD, primarily filled with my aunt’s papers (which I decided to format before using). But, other than that… not much else came with the laptop, neither physically nor on the disk – not even a charger. The only other thing of note was an installation of some game that came with Taco Bell meals that I could not track a copy of <em>anywhere</em> online. Looks like it’s lost media now, since I formatted the HDD shortly after I got the laptop.</p>

<p>So… yeah. I formatted the HDD almost right away. In hindsight, this was a bad idea, as finding drivers for this laptop became a Pain In The Ass<em>™</em>. But, there was a method to my madness: I wanted to install Windows XP, which… ended up being a really bad idea. You see, at the heart of this laptop is a Pentium III clocked at 600MHz. Couple that with the initial 64MB of RAM, and you’ve got a terrible XP experience (XP XP?) on your hands. I was somehow able to install SP2 (and I think SP3, as well) before I decided that it was too painfully slow to do just about anything useful.</p>

<p><img src="/blogImg/comp/charcoal/xp.jpg" alt="Laptop running Windows XP" /></p>
<h5 id="windows-xp--slooooowwwww">Windows XP = slooooowwwww</h5>

<h3 id="cd-troubles">CD Troubles</h3>
<p>Windows XP was out of the question at this point, so I decided to go back to stock and created an install CD for Windows 98SE. This was when I noticed the first flaw of the laptop – The CD drive was a bit dated, and thus the drive would have trouble reading newer CD-Rs (like the ones I had on-hand). If I was lucky, the drive would pick up on the disc, and continue as normal. Because of that, I would need to eject and re-insert the disc several times in order to get it to work. But, when CDs <em>did</em> read, there was another issue that popped up regardless of whether or not the CD was new or old.</p>

<p>This issue ended up being the worst of them all, and would cause quite a few OS installations in the future to fail. The issue? Sometimes, the drive would just fail to read several sectors on the disc, and it would fail to read those same sectors no matter what. Even if the disc was spotless, it would still fail to read. Because I didn’t know this at the time, I thought that the 98SE CD I made was just bad. So, I tried making a new CD – and repeated this process a few times. After 98SE kept failing, I decided to try installing Windows 2000. Again, the same issue plagued that install.</p>

<p>Because I was slowly running out of blank CD-Rs at this point, I decided it would be best to just click “Ignore” on each failed file during the installation. This took a while to get through, but eventually I had W2K installed on the laptop. Luckily, it only failed to copy driver files that the laptop didn’t even need, anyway. (Like I said earlier, finding the correct drivers for this laptop was really difficult. I had to scour through really shady-looking driver websites that didn’t even tag their drivers that well, so it was always a gamble on whether or not the drivers would <em>actually</em> work once I copied them over.)</p>

<p>I played around with W2K on the laptop a little bit every once in a while for a few months. It was a bit slow, but it was still cool to use a laptop from that era. Later, I ended up putting the laptop underneath my dresser, and forgot about it for a little less than a year.</p>

<hr />

<h3 id="upgrade-time">Upgrade Time</h3>
<p>Late 2017. I realize I still have this stupidly-old laptop, and I get the urge to play with it, this time installing 98SE instead of continuing to use W2K – Of course, following the same “Ignore” procedure during the install process. It went well, and with the correct drivers installed, I was up and running again. This time I decided to do something a bit interesting: What if I connected an external monitor + a mouse, and tried to make this a desk-based setup? Sure enough, it worked out pretty well! …But unfortunately, this only lasted for a few months before I put the laptop back in storage once again.</p>

<p><img src="/blogImg/comp/charcoal/desksetup.jpg" alt="Laptop in a desktop configuration" /></p>
<h5 id="desk-setup">Desk setup.</h5>

<p>Some time in the latter half of 2018, I decided I wanted to start upgrading the laptop. This started with a WiFi card that would fit into a PCMCIA slot in the side of the laptop. Specifically, I chose the <a href="https://www.amazon.com/gp/product/B00007KDVK/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&amp;psc=1">Cisco-Linksys WPC54G</a>, mainly due to the fact that it is one of the few PC cards that support both WPA &amp; W2K – perfect for bringing it onto modern networks. But… notice how I didn’t say it supports 98SE. Even though the packaging states it has support for 98SE, and indeed, the driver disc came with a 98SE version, the software was extremely buggy on 98SE. More specifically, the interface was bugged, making it so that I couldn’t even click the controls needed to connect to a network. Ugh.</p>

<p>At this point, I went back to W2K so I could use the WiFi card. Unsurprisingly, the software wasn’t garbage there. Yay! I connected to my home network just fine, checked out some websites with the last supported version of SeaMonkey, and called it a day. It was only a matter of time before I set my sights on the next upgrade…</p>

<p>Ah, hard disks. What would we do without them? Probably store the entire OS in RAM and use flash drives as persistent storage, I guess. No, this is totally not hinting at a later part of this post. The hard disk in the laptop was still stock from 2000, meaning it was a bit of a clunker. Plus, it was pretty tiny – only 6GB of space, which is actually <em>less</em> than most of the flash drives I have on hand! So, naturally, it was next to go. Solid state storage is all the rage these days in modern technology, and it has even spread to the retro/vintage PC community, so I thought: What the heck, why not put a CF to IDE adapter + a CF card in this thing?!</p>

<p>Well, turns out there was a <em>“not”</em> to that <em>“why not“</em>. Once I got the CF card/adapter, popped it into the laptop, and installed W2K onto it, I got a particularly frustrating message I would begin to see much more often: “Operating system not found”. Great. I tried reinstalling W2K again, installing MS-DOS, installing <em>literally anything I could</em>, but no amount of OS installation and BIOS tweaking could help me. Hmmst. After doing a bit of research, however, I found my mistake: I had purchased a regular, standard “consumer” CF card rather than an “industrial” CF card. Why is this important? Well… one of the key differences between consumer and industrial CF cards is that consumer cards can only be used as removable media, whereas industrial cards are treated as fixed disks.</p>

<p>Welp, there it was. Apparently Koko doesn’t do her research. I tried looking for hours into ways to “trick” the firmware of the card into acting as a fixed disk, but to no avail. It was 3am at that point, so without spare funds for a new, industrial CF card, I gave up and went back to the aging 6GB HDD. (<em>Curiously, I just realized that W2K went through the initial install process onto the CF card just fine without even stopping me, despite it being treated as removable. I have no clue why this is.</em>)</p>

<h3 id="even-more-upgrades">Even More Upgrades...</h3>
<p>I tinkered with the laptop on-and-off for the next few months. But woah, hey, it’s early 2019 now! Current year!! I got a ton of birthday money around this time, so I decided to put a tiny bit of that money towards the laptop (and literally all of the rest of that money towards a used PS3 and PSP). The upgrades this time were pretty straightforward: A new-old stock <a href="https://www.amazon.com/gp/product/B004HKUR0M/">WD Blue 80GB HDD</a> (why I didn’t just buy an industrial CF, I have no clue), and a single 256MB stick of RAM (which was a gamble since an unknown revision of the laptop does not support 256MB sticks) to replace the 32MB stick already in the laptop. “<em>But wait, Koko!</em>“, you might be saying. “<em>Didn’t you say earlier that the laptop had 64MB of RAM, and not 32MB??</em>” Why yes, I did say that the laptop had 64MB of RAM… but I’ll get to the specifics of that.</p>

<p>Before I did anything, I decided to image the W2K installation from the old HDD so that I could skip waiting on the garbage CD drive, and just restore the image onto the new HDD. To do this, I used <a href="https://clonezilla.org/">Clonezilla</a>, which actually saves the day again later on in this post.</p>

<p><img src="/blogImg/comp/charcoal/clonezilla.jpg" alt="Imaging with Clonezilla" /></p>
<h5 id="a-disk-image--usb-11--snoozefest">A disk image + USB 1.1 = snoozefest</h5>

<p>So, after getting the new HDD installed and the new RAM seated, it was time to install W2K… wait, the laptop won’t power on. Was my laptop that one revision that didn’t support 256MB sticks? Oh no. Oh no. Oh- wait, now it’s booting fine. Phew. But wait, what’s this I see? 288MB of extended RAM? I thought I had only installed 256MB? Well… after deciding to remove the RAM stick to see what would happen, I learned that, apparently, the laptop <em>includes</em> 32MB of RAM built-in! The stick that was originally in the laptop was only 32MB, meaning the total extended memory at the time was 64MB. Interesting. Cool. More RAM is better.</p>

<p>The new HDD/RAM definitely helped. I was no longer waiting on W2K to load simple things such as the Control Panel or My Computer. So, I tinkered around with the laptop some more, even going as far as installing every single update available on Windows Update (yeah, for some reason W2K updates are <em>still</em> available). Now my laptop felt just a bit more modern and… uh… <em>usable</em>. But… I still decided to neglect it for a few more months. W2K just wasn’t too fun to use. Sure, it was a nostalgic joyride, but I just didn’t enjoy having to track down old versions of software just for W2K support.</p>

<hr />

<h3 id="linux-disaster">Linux Disaster</h3>
<p>So… that brings us to earlier last week. While moving some things around in my room in order to prepare for college move-in, I spotted the Y2Ktop. And, once again, I wanted to toy with it. But… this time was different. This time, I suddenly remembered back when the laptop only had 64MB of RAM, when almost every Linux distribution would fail to load from the live CD due to the anemic amount of memory available. I had around 300MB RAM available now, so… let’s see where we can go with a lightweight Linux distribution!</p>

<h4 id="linux-disaster-round-1-arch">Linux Disaster, Round 1: Arch</h4>
<p>My starting point was Arch Linux. It checked all the boxes: Bare-bones. Simple. Light. Command-line by default. 64-bit only– oh, right. Arch became 64-bit only some time in 2017, and I forgot about that fact because, surprise, I was only using Arch on 64-bit computers before. So, I never really cared until now, when I <em>actually</em> had a 32-bit machine on my hands. Luckily, other people must have had the same issue; because Arch is so lightweight, it’s perfect for running on old machines. Problem is, those same old machines typically use 32-bit CPUs. That’s where the <a href="https://archlinux32.org/">Arch Linux 32</a> project comes in; it’s Arch Linux, but with support for 32-bit CPUs and configured to use repositories that carry 32-bit packages, as well. It even supports i486 CPUs, too! So, I burnt the ISO to a CD (which, by the way, by this time I had found a stack of older CD-Rs that worked first-try on the laptop… the sector reading issue still persists, though), popped it into the laptop, booted the live CD, and…</p>

<p>…initramfs error. Crap. It didn’t like something about the laptop. So, I tried using the <a href="https://www.plop.at/en/bootmanagers.html">Plop boot disk</a> to boot the installer using a flash drive (did I mention that the laptop has a single USB 1.1 port?), but still no luck. This time, ISOLINUX didn’t even want to load. Alright then, time to find an older ISO of Arch. I picked out an ISO from early 2014, and sure enough, it worked! Er… slight problem, however.</p>

<p>For those who don’t know, Arch uses a little tool known as pacstrap in order to install the base system packages to the hard disk. That requires internet. Guess what doesn’t come with firmware for the WiFi card. Guess what also requires a restart in order to use the firmware for the WiFi card. See what I’m hinting at? Basically, I can’t even install the firmware I need, because I need to install the system first, but I need to connect to the internet to install, but I need to install the firmware to do that, and to install the firmware I need to first install Arch, but to install Arch I need internet….. I think you get the point. I was basically stuck.</p>

<p>Plan B: <a href="https://wiki.archlinux.org/index.php/Offline_installation">Copy the ISO contents to the disk, change some things.</a> Huh, maybe I wasn’t stuck, after all. I got the WiFi card firmware extracted and installed into the system, and I was now on the ‘net! Nice! I played around with elinks (a commandline browser) a bit, visiting my website and trying to log into Twitter, before I finally decided to start updating. Slight problem, however: I couldn’t just run <em>pacman -Syu</em>. That would be silly. I already knew that the default Arch repositories moved to 64-bit only packages as well, meaning I needed to edit the mirrorlist to point to repositories from the Arch Linux 32 project. So, I did that, went to do an upgrade (fingers crossed, 2014 to 2019 is pretty long in the Linux world, meaning packages could easily get broken), and… Oh, I need to import some PGP keys. Oh, import failed. This was probably because A) my system was so outdated that I couldn’t import keys, or B) im stupid</p>

<p>Ok, so that’s Arch out of the picture. Relying on an upgrade from 2014 to 2019 was just not how I wanted to go about things.</p>

<h4 id="linux-disaster-round-2-puppy-linux">Linux Disaster, Round 2: Puppy Linux</h4>
<p>Hey, remember how I mentioned keeping an OS in RAM and using flash drives as storage? Well, I was hinting at <a href="http://puppylinux.com/">this canine-themed Linux distribution</a>. Fun fact: Puppy Linux was my introduction to the world of Linux back in 2012, which I used on my childhood Pentium 4 machine to play Minecraft a few times. Ah… memories. Well, in looking at lists of lightweight Linux distros, I decided to go with what I knew would likely work. I picked out a version of Puppy with some Debian component/repository support, burnt it to a CD, and set to work playing around with the system.</p>

<p>After seeing that Puppy worked pretty well on the laptop, and realizing that I was getting up-to-date packages PLUS THE WIFI FIRMWARE BY DEFAULT, I decided it was time to install it! Oh fuck! The sector error!</p>

<p>The CD drive sucks, so why not try to boot from USB this time using Plop? Sure, it might not work at all given what happened with Arch, but it’s worth a shot. But, lo and behold, it booted from USB just fine! So, with the sector error out of the way, I was able to install Puppy to the laptop just fine. Finally, a modern Linux distro is on the laptop.</p>

<p><img src="/blogImg/comp/charcoal/pup.jpg" alt="Laptop running Puppy Linux" /></p>
<h5 id="buppy">buppy</h5>

<p>But… I didn’t really like Puppy Linux that much. For one, it felt like I was using a Frankenstein’s Monster-based Linux distribution. Like… it wasn’t Debian-based at all, but… there was a ton of Debian stuff added in. It was confusing. Additionally, I didn’t like how many packages and tools shipped with Puppy. Like, do I really <em>need</em> 3 different sets of control panels?! Overall, the distro was just a mess. It wasn’t bare-bones, it tried too hard to be friendly to the point where it was annoying, and it just didn’t feel light. So, Puppy was out of the picture.</p>

<p>“<em>Was I out of options?</em>” I began to think. “<em>Is a modern, bare Linux distribution too much to ask for?</em>“</p>

<p>No, it wasn’t too much.</p>

<h4 id="linux-disaster-round-3-debian">Linux Disaster, Round 3: Debian</h4>
<p>After doing some research, I somehow stumbled upon the <a href="https://www.debian.org/releases/stable/i386/ch03s04.en.html">system requirements</a> for base Debian 10, which the laptop met pretty well. I didn’t even know that you could do a base, desktop-less install of Debian up until that point, but apparently you can. So, I followed the usual process of download ISO &gt; burn to CD &gt; pop CD into laptop &gt; run installer &gt; install, but nothing can ever be that perfect, right? Indeed, the sector error came back, even after trying a second CD. Ok, boot from USB using Plop? I mean, that worked for Puppy, so it sh– Nope, that gets stuck before even loading the initrd. Sigh.</p>

<p>At this point, I <em>wanted</em> to just pull the plug on this project. But, then it hit me. I have that P4 machine. And it has a DVD drive. A DVD drive that can actually read discs. I came up with a foolproof plan that I KNEW would lead to a successful install:</p>

<ol>
  <li>Make a tiny partition on the P4 machine.</li>
  <li>Install base Debian onto that.</li>
  <li>Use Clonezilla to create an image of that, and save it to a flash drive.</li>
  <li>Use Clonezilla on the laptop to restore that image to the laptop’s hard drive.</li>
</ol>

<p>This process (luckily) went without any hiccups. But, there was another issue: because I wasn’t cloning the whole drive, I had to skip installing GRUB. So… even though I <em>had</em> Debian on the laptop by now, there was no way to boot into it. Psych! I actually had a GRUB boot CD lying around from the day before, and it was able to find Debian on the hard disk, and boot to it just fine. I was in!</p>

<p>So, after getting the WiFi firmware + GRUB properly installed, I had a bare-bones, modern Linux installation on the laptop. (And, it was only using ~32MB RAM at the command-line!)</p>

<p>The only other thing I did beyond the base install was an installation of xserver and i3, which I customized to look a tiny bit more sleek. I know I said I wanted a command-line only install, however I felt as if the laptop could handle a simple window manager just fine, and that it would help the laptop become a bit more useful in the process.</p>

<p><img src="/blogImg/comp/charcoal/debian.jpg" alt="Laptop running Debian, showing output of screenfetch" /></p>
<h5 id="obligatory-piping-of-screenfetch-to-lolcat-dont-know-why-only-209mb-of-ram-is-found">Obligatory piping of screenfetch to lolcat. Don’t know why only 209MB of RAM is found…</h5>

<hr />

<h3 id="retrospect--the-future">Retrospect + The Future</h3>
<p>That pretty much concludes the first part of this laptop’s journey. Looking back, I have a gut feeling that this whole thing might be a bit of a waste of time. The hardware can only be upgraded so much, and by the time I get the laptop as powerful as it can be (which is not even far from its current state), it will still be pretty damn slow. That’s not to say that the laptop hasn’t been fun to play with – despite all of the roadblocks, it’s still really neat to see what the laptop <em>can</em> and <em>can’t</em> handle, and even neater to see the laptop run a setup that doesn’t look too far off from my previous Arch+i3 installs. Speaking of Arch, I also realize that I could have done the same install process I did with Debian; but, it’s a bit too late to go back on that.</p>

<p>As for the future of the laptop… The upgrades won’t stop. Sure, it’s pretty maxed-out by now (namely, the RAM and CPU cannot be upgraded further, as well as embedded stuff such as the GPU), but there are a number of things I can still upgrade. Such as…</p>

<p><strong>The hard drive.</strong> I still have the CF to IDE adapter, so I could definitely get an industrial CF card at some point and copy the current install over to the card. This would (hopefully) make the laptop a lot snappier thanks to the solid state nature of the cards.</p>

<p><strong>The battery.</strong> I cannot believe I didn’t mention this earlier, but I think it’s best I talk about it down here. Because this laptop is extremely old and was likely never used for upwards of a decade (or more), the battery went completely flat, and the laptop would shut down immediately after the charger was removed…</p>

<p>Oh wait, I ALSO forgot to mention the charger situation. So, despite the laptop not coming with its charger, I was able to use the charger from a junk laptop of mine since they use the same barrel-style plug and share similar voltages. The only problem is that the charger is a bit loose when it comes to the port on the laptop, meaning it needs to be in just the right position in order to actually power the laptop. This means that a slight bump of the cable (or even gravity just doing its thing) will <em>immediately</em> power off the laptop. Not good.</p>

<p>Important tangent over, this is partially the reason I want to get the battery to work, because I have run into unexpected shutdowns so. many. times. The other part of the reason I want the battery to work is because, well, <em>it’s a laptop</em>. The main (but no longer prominent since it’s kinda obvious now) point of pretty much any laptop is their portability! I’d love to bring the laptop to classes sometimes, just to “wow” people because you never really see ancient tech still in use in the wild anymore. But… the fact that I need to be tethered to an outlet and I need the charger on me at all times is kinda limiting me from doing this.</p>

<p>Well, what’s my plan with the battery? My only option is to replace the cells in it. A while back I actually cracked the battery open to take a peek at what cells it uses, and how the battery circuit is designed. Lucky me, the cells are soldered in a parallel circuit, and the cells are actually labeled, too. The laptop battery is comprised of 6 Panasonic CGR18650HM cells, which I can easily find <a href="http://deconophone.free.fr/IMG/pdf/iontde.pdf">information</a> about online. What I can’t find online, however, is the cells themselves for sale. So… unfortunately, I need to study the cell’s specifications <em>extremely closely</em> and find a good substitute for them – hopefully they’ll work and I won’t have them all explode in my face :s</p>

<p><strong>PC cards.</strong> I still have one extra slot available for any expansions I may wish to add to the laptop. I’m thinking that an ethernet card may be a good fit, considering the laptop shipped without the option for one selected… But, since the WiFi card works super well, I might just find something else to put into the remaining slot, instead.</p>

<p><strong>CD drive.</strong> If I can ever find one that is meant for the laptop, I will definitely buy it. The current CD drive is just super unreliable.</p>

<p>Those are all of the upgrades to the laptop that I can come up with off the top of my head. There’s still quite a bit I can do, as you can see!</p>

<hr />

<p>Before the final good-bye, here are the (basic) current specs of the laptop at the time of writing this, for those curious:</p>

<ul>
  <li><strong>OSes:</strong> Debian 10 + Windows 2000 Professional</li>
  <li><strong>CPU:</strong> Coppermine Pentium III @ 601MHz</li>
  <li><strong>GPU:</strong> ATI Rage Mobility AGP 2x</li>
  <li><strong>RAM:</strong> 288MB</li>
  <li><strong>MODEL:</strong> Gateway Solo 2150</li>
</ul>

<p>Thanks for sticking with me though this crazy journey in modernizing vintage tech. Feel free to ask any questions/give some suggestions by <a href="/contact.html">contacting me</a>. Otherwise, you can wait until the next blog post! (Trust me, it won’t be as massive as this one!)</p>

<p>(TL;DR: cd drive sucks)</p>]]></content><author><name></name></author><category term="Computers" /><summary type="html"><![CDATA[Warning from the future: The writing style I had back when I wrote this is atrocious. Not to mention the subject matter here is a (relatively speaking) boring, generic, Pentium 3 laptop with nothing noteworthy about it. Neat to me back in 2019, sure, but after 4 years I've come to realize it's absolutely not worth a half-hour read (and especially not with the way I wrote it). You should consider reading the "My Computers" writeup I did for this laptop instead, but if not: don't say I didn't warn you!]]></summary></entry><entry><title type="html">ExaStar Development Journal #7 – ExaStar takes form</title><link href="http://kokoscript.com/2018/002.html" rel="alternate" type="text/html" title="ExaStar Development Journal #7 – ExaStar takes form" /><published>2018-05-12T00:00:00-05:00</published><updated>2018-05-12T00:00:00-05:00</updated><id>http://kokoscript.com/2018/002</id><content type="html" xml:base="http://kokoscript.com/2018/002.html"><![CDATA[<p>After so many journals where I’ve essentially repeated that I can “stop worrying so much about the game’s internals”, I can pretty much do just that now!</p>

<p><em>What’s new?</em></p>

<h3 id="battles-become-more-finalized">Battles become more finalized</h3>
<p>In the last journal, I sort of teased some information on how battles would work with the removal of the rhythm attack system. Well, here’s some information straight from the development notes (since I’m too lazy to rewrite everything)!</p>

<p><strong>Attacks</strong></p>
<ul>
  <li>As before, this is dependent on the weapon that is equipped on the character</li>
  <li>Attack strength ratio is still controlled by the charm that is equipped</li>
  <li><em>NEW:</em> Accuracy of a weapon adds to the charm’s high-pool ratio</li>
  <li><em>NEW:</em> Accuracy is no longer counted on a scale of 20 – now 100 instead</li>
</ul>

<p><strong>Cards</strong></p>
<ul>
  <li>Cards can be obtained by either using a turn to formulate one of the first 3 tiers, or using the 100% SP meter to make a card of the last 2</li>
  <li>Cards can affect basically every aspect of the battle – accuracy, health, timer, SP/EXP gained, etc.</li>
  <li>Members carry separate card decks</li>
  <li>Each member can initially carry up to 4 cards, but can increase this to 8 through level upgrades</li>
  <li>A card’s effects only apply to the current battle</li>
  <li>4 main tiers of cards:
    <ul>
      <li><strong>I</strong>. “Curse” cards which can only be discarded by using them (or using SP) (25%, 0% chance SP)</li>
      <li><strong>II</strong>. Neutral cards that have a benefit as well as a matching drawback (50%, 0% chance SP)</li>
      <li><strong>III</strong>. Cards that can help the player with no drawback (25%, 25% chance SP)</li>
      <li><strong>IV</strong>. Rare cards that give a huge benefit (0%, 75% chance SP)</li>
    </ul>
  </li>
  <li>The entire deck can be reshuffled by using the SP meter, however this will not yield any Tier 4 cards</li>
  <li>The deck can be completely cleared out using the SP meter, useful for getting rid of many Tier 1 cards at once</li>
  <li>Additionally, the SP meter can be used to swap out decks with another party member</li>
  <li>Card amplification: if the player has 2 of the same card, then the player can opt to merge the cards into one, doubled-up card</li>
</ul>

<p><strong>Battle timer</strong></p>
<ul>
  <li>Each of the player’s turns are timed, adding pressure to decisions</li>
  <li>Default time limit is 30 seconds</li>
  <li>If no choice is made before the timer is depleted, then the character will just pass for that turn</li>
  <li>The timer’s limit can be increased or decreased via cards for the specific battle</li>
</ul>

<p>It all seems a bit daunting and confusing from all of this text, but don’t worry – it <em>should</em> be pretty straightforward once it gets shown and explained inside the game.</p>

<p>I additionally started redesigning Grapefruit’s UI a tiny bit by removing the top dialog area and merging it with the new Deck area. The Deck area of the UI also holds new descriptions for each of the menus and actions of the battle system. It’s a bit rough right now, but take a look:</p>

<p><img src="/blogImg/002/cards.png" alt="Early card system implementation in ExaStar" /></p>

<p>As you can see, even the Card and Deck portions of Grapefruit are already implemented! <em>…sorta</em>.</p>

<h3 id="scene-design-becomes-much-easier">Scene design becomes much easier</h3>
<p>There were originally certain scene-related objects (such as scene transition boundaries, dialog regions, etc.) which could originally only be created by using the scene’s respective code. This proved to be a bit cumbersome, so I eventually created actors that would create these objects automatically using Stencyl’s in-scene actor behavior editor in order to make designing scenes a bit easier than before.</p>

<h3 id="did-someone-say-settings">Did someone say SETTINGS??</h3>
<p>There’s some more changes to how the game can be configured! The latest addition is a special lil’ file called <em>exaConfig.ini</em>, which lives in the root of the game’s <em>data</em> directory. Essentially, this file lets you change a ton of extra intricate settings that I normally would not have enough time to implement into the in-game settings screen, such as things like how the zoom shader should function, if characters should visibly talk or not, how the game should load assets, etc.</p>

<h3 id="simplified-storyline">Simplified storyline</h3>
<p>I can already imagine the disappointed looks this point will bring, but fear not: this is for the better. This mainly only affects the intro and prologue of the game, too, so no worries. Essentially, I had to strip away a lot of filler content/confusing storyline points from the beginning of the game in order to make it a bit easier to understand and get into. This also has the advantage of making the whole premise of the game a bit more explainable than the old one. I don’t exactly have any content from this storyline in the game just yet, aside from a reworked intro screen that will replace the “pre-prologue” part of the game.</p>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[After so many journals where I’ve essentially repeated that I can “stop worrying so much about the game’s internals”, I can pretty much do just that now!]]></summary></entry><entry><title type="html">ExaStar Development Journal #6 – The Rewriting (sorta)</title><link href="http://kokoscript.com/2018/003.html" rel="alternate" type="text/html" title="ExaStar Development Journal #6 – The Rewriting (sorta)" /><published>2018-02-14T00:00:00-06:00</published><updated>2018-02-14T00:00:00-06:00</updated><id>http://kokoscript.com/2018/003</id><content type="html" xml:base="http://kokoscript.com/2018/003.html"><![CDATA[<p>Wow, I’m not that late to posting this time!!</p>

<p>Ok, so this journal is going to be a bit long in comparison to the others despite the small amount of bulletpoints, <em>but</em> there have been some important changes to how the game works, and I felt as if that warranted a complete journal altogether.</p>

<p><em>What’s New?</em></p>

<h3 id="assets-assets-assets">Assets, Assets, Assets</h3>
<p>So, throughout most of ExaStar’s second development phase (relevant to the current, 2016 version of the project file, as described in Journal 1), I started shifting asset management away from Stencyl and into the game’s “data” directory. This started with EZImgInstance, which made it so that I could easily create images using files from the aforementioned data directory, and manipulate them almost as if they were actors/objects. This was a huge breakthrough, since before I had implemented this system, I needed to create a new actor type for literally <em>everything</em>, which made asset management a bit of a pain and added some extra heft to the game’s project file (which meant longer save and load times in the Stencyl toolset). Some time after I had that system in place, I decided to also apply the same to all of the music in the game. This was pretty important, as music actually makes up for a huge amount of the game’s size. This time, the implementation was pretty simple – I had an extension installed that could get sound data from the data directory, so all I had to do was plug it into the already existing sound calls.</p>

<p>Why did I do this you may ask? Well, Stencyl by default loads everything into memory when the game first opens (unless you have asset atlases set up beforehand, which I really did not want to mess with as a lot of the game’s assets are shared across multiple scenes, making it not worth the hassle). This is actually not much of a problem for me, personally; in fact, many games that are small enough to fit entirely in memory will do this! However, the issue with Stencyl’s asset loading implementation is the fact that there is essentially no loading screen. Instead, you get a blank, white screen (this is on Windows; you’ll a transparent screen on KDE, a black screen if you are on GNOME, or just an ever-bouncing icon in the dock on macOS) while the OS reports that it’s in a “non-responsive” state. Obviously, keeping end-users with slower disks in mind, I felt as if some people would likely get confused by this odd loading behavior, potentially leading to false reports of the game “crashing” on startup – when in reality, it’s just loading!!</p>

<p>So, to help combat this unhelpful loading screen, I did as I mentioned before, and essentially moved a lot of the images and music over to a separate directory that is not automatically loaded on startup. This worked pretty well, cutting the average time to load to around 1 second (on my SSD). Pretty cool, wouldn’t you say?</p>

<p>But then I noticed that, despite moving everything over to the <em>data</em> directory, the game was still having issues – namely, when running the game off of a flash drive (or even an energy-efficient HDD), there would be large freezes when loading music. The most common and most jarring area that this “freezing” occurred on was the title screen, which has to load music as soon as the scene loads. However, because of the music, the scene transition at the very beginning would get interrupted, and, instead of fading out, it would sometimes just cut straight to the title screen. Another area that had issues was the File Select, which has to load 2 music files as soon as it is entered (a “base” song as well as the same song but with added drums). The freezing caused the two music files to play slightly at an offset from each other (when they were supposed to play symmetrically), which was especially noticeable on slower drives. This was all not immediately noticeable since I mainly develop the game on systems that utilize SSDs, only rarely running the game off of an external drive.</p>

<p>So, I had to go back to the drawing board. Obviously, all of this freezing was due mainly to the fact that everything was no longer stored in memory, and instead streamed off the disk. I then set out to make my own asset management system, mainly covering these requirements:</p>

<ol>
  <li>There needed to be some sort of loading screen, most favorably with a progress indicator.</li>
  <li>I needed to easily reference assets from code in a format that was short and readable.</li>
  <li>Both images and sounds would need to work with this system: all images, music, and sfx, with the exclusion of actors, backgrounds, tilesets, and fonts (which are all mainly <em>supposed</em> to be managed in Stencyl itself).</li>
</ol>

<p>I started working away at this system aimlessly for a day or so before I put it to the side for winter break. During break, however, I did manage to write a tool that would index all of the applicable assets from the data folder and put their locations all into separate manifests. After break, I set out to model the asset loading system off of these manifests.</p>

<p>After around 1 hour of work (when I was in class, taking notes at the same time), I had a complete working prototype of the asset system up and ready to go! Now it was time to actually get it <em>integrated</em> into the game, instead of it just existing as a object that’s never used. After going through <em>all of the game’s source code</em>, replacing old music and sfx calls with calls to the asset system instead, and rewriting a large chunk of EZImageInstance to also integrate with the asset system, I was finally able to run the game for the first time in around 2 days!</p>

<p>After a few crashes in the first few scenes (which were due to small errors that I never caught in the integration process), I was finally able to test everything out, but… something was wrong. <em>Terribly, <strong>terribly</strong> wrong</em>. The game was probably in the most unstable state I had ever seen it. Scratch that – this was the <em>most instability I had ever seen in any one of my games PERIOD</em>. The random crashes still occurred, however this time it was due to errors I could not even trace. Most of my precious, beautiful interfaces I had spent hours perfecting either suddenly looked completely garbled or would just crash the game on creation. The best part of all, though, was that the asset loader <em>wasn’t even doing its job!</em> Take a look:</p>

<p><img src="/blogImg/003/saveboxFail.png" alt="Images failing to load in ExaStar's continue screen" /></p>
<h5 id="i-thought-that-everything-was-fine-up-until-this-point--where-did-all-of-the-outlines-go-why-is-there-only-one">I thought that everything was fine up until this point – where did all of the outlines go? Why is there only one?</h5>

<p><img src="/blogImg/003/settingsFail.png" alt="Images failing to load in ExaStar's settings screen" /></p>
<h5 id="looking-at-the-settings-screen-almost-all-of-the-buttons-went-missing-what-the">Looking at the settings screen, almost all of the buttons went missing. What the…?</h5>

<p><img src="/blogImg/003/uiFail.png" alt="UI image failing to load in ExaStar's save dialog" /></p>
<h5 id="things-got-even-worse-ingame-with-things-such-as-the-hud-itemboxes-saveboxes-and-various-other-uis-failing-to-appear">Things got even worse ingame, with things such as the HUD, itemboxes, saveboxes, and various other UIs failing to appear</h5>

<p>I spent an hour trying to track down each of these bugs (whilst pulling out <del>some</del> a lot of hair), and eventually got some of the random crashes to stop. However, no matter how much I tried, I could not fix nor figure out why certain images were never loaded. I eventually called it a day, and just went to bed completely frustrated and generally afraid that I would need to roll back all of that hard work.</p>

<p>The next day came, and while I was busy at a family gathering, I was able to quickly design a debug tool to view all of the assets that were in memory. To my surprise, all of the assets showed up properly. This just led to even more questions and frustration, so I decided to stop myself from trying to fix the game again until I got back home the next day.</p>

<p>When I did begin work the next night, I eventually came to the incomplete conclusion of “<em>Haxe’s management of passing variables by value vs reference is the cause of all of this.</em>” This was a pretty baseless conclusion to start off with, and the next hour of looking up Haxe documentation and forum posts wasn’t really helping that claim either. However, before I completely gave up hope, I decided to stick in a small “<em>.clone()</em>” to the return statement for images, despite thinking that passing the image by reference should have worked anyways.</p>

<p>Lo and behold, the game was immediately up in near-working order again. What started out as a large jump in concluding the reason for this bug actually ended up being completely correct. All that was left were a few modifications to EZImageInstance’s animation procedures and constructor, and I was good to go!</p>

<p>All that work paid off too; I finally had the ability to add a proper loading screen to the beginning of the game, got the ability to reference assets in a much simpler way, and was able to get both images and sounds working in the same exact system, too!</p>

<p>With the creation of the new asset system, as well as the updated (and much more versatile) EZImageInstance, I finally decided to mark a new milestone in ExaStar’s development, which I now call the “AssetLoader Phase”, or just <em>Phase 3</em>. The previous phases, in case you were wondering, were:</p>

<ul>
  <li><em>Phase 1</em> being the initial, visually-scripted versions of the game that used a resolution of 640×480 and slightly larger sprites, and essentially accounted for all builds of the game from Sep. 2016 – March 2017, and</li>
  <li><em>Phase 2</em> being the versions that began to use actual code instead of Stencyl’s visual scripting system, with downscaled sprites and important features such as the updated Party, Enemy, Map, Quest, and Inventory systems, as well as the Mapper. This phase accounted for all builds of the game from March 2017-December 2017.</li>
</ul>

<p>After finally getting through this nightmare, I was finally able to work on other things…</p>

<h3 id="fixed-party-data-saving">Fixed Party Data Saving!</h3>
<p>One of the things that I had issues with while attempting to save party, inventory, and quest data was that <em>I couldn’t just write the objects themselves to save files</em>; instead, this would just cause the game to dump its memory to the chosen save file, which would not even work due to there seemingly being a hard limit of around 512Kb on save files. So, I just opted to move the object’s most important data into arrays and saving that, which worked out fine. But, in my usual laziness, I never actually got around to fixing the saving of party data, which ended up being a 15 minute fix. With that working, the save system is basically in fully working order again.</p>

<h3 id="an-even-better-grapefruit">An even better Grapefruit</h3>
<p>For those who don’t know, Grapefruit is the codename for the game’s battle system. I don’t know if I mentioned this in a previous journal or not, but I decided to redo Grapefruit completely from the ground up after the conversion to Haxe, but never got around to adding onto it until recently. Essentially, I recently created a new model for how Grapefruit works; instead of it being one large scene behavior, it is now modular; there is a base scene behavior that loads everything, and multiple “modules” which control separate areas of the battle system. They can all communicate with each other, however the new, modular nature of Grapefruit makes stuff a lot less complex to work with, as modules can store their own variables that are relevant <em>only</em> to that module, and not others.</p>

<h3 id="gameplay-changes">Gameplay changes</h3>
<p>Speaking of Grapefruit, I’ve been brainstorming multiple ideas for the game’s battles, as I wanted to replace the old rhythm-based attack system that I planned to add before. After deciding that this attack system would be too tedious and a bit limiting in terms of what could be added, I looked for other ways to “spice up” battles a bit; my first idea was having a sort of platformer game that you’d need to complete in order to execute attacks, but I decided that this was too gimmicky. Another idea that I had was a sort of “minigame” for each attack, however I felt as if this was a bit too similar to Undertale’s battle system, so I quickly scratched that idea out. The conclusion? Skip the gimmicks! Instead, I decided to add onto the more strategical aspect of JRPGs and introduce a sort of card-based system. Essentially, cards serve more or less the same purpose that the rhythm-based attacks did. However, instead of having attacks rely on an external “game”, instead the cards will just add a bonus of some sort to an attack – much like the bonuses that one could get for exceeding the beat limit on the rhythm attacks. There’s a ton of complexity as to <em>what</em> I can do with the card system – So much so that I’ve been coming up with ideas non-stop, as opposed to the limited ideas with the rhythm attacks. I may lay this card system out in the next journal, as I have yet to actually write all of these ideas down somewhere (oops).</p>

<hr />

<p>That about wraps it up for this journal! Hope you enjoyed reading what was essentially a 2-hour long brain-dump =)</p>]]></content><author><name></name></author><category term="Gamedev" /><summary type="html"><![CDATA[Wow, I’m not that late to posting this time!!]]></summary></entry></feed>