<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>triangle on despatches</title><link>https://icle.es/tags/triangle/</link><description>Recent content in triangle on despatches</description><generator>Hugo</generator><language>en</language><atom:link href="https://icle.es/tags/triangle/index.xml" rel="self" type="application/rss+xml"/><item><title>Escape to Menu</title><link>https://icle.es/2025/07/15/escape-to-menu/</link><pubDate>Tue, 15 Jul 2025 17:12:46 +0100</pubDate><guid>https://icle.es/2025/07/15/escape-to-menu/</guid><description>&lt;p>One of the key bits of functionality I want in a &lt;a href="https://icle.es/sprout.md">sprout build&lt;/a> is
the pause menu.&lt;/p>
&lt;p>After a bit of working it out, I ended up with this plan:&lt;/p>
&lt;p>
 &lt;img src="./sketch.png" alt="Pause menu sketch">

&lt;/p>
&lt;p>Until now, hitting Escape would just exit the game immediately. Not ideal.
triangle needed a proper menu: something that pauses the game, gives players a
chance to resume or quit intentionally, and maybe even shows a bit of useful
info.&lt;/p></description><content:encoded><![CDATA[<p>One of the key bits of functionality I want in a <a href="https://icle.es/sprout.md">sprout build</a> is
the pause menu.</p>
<p>After a bit of working it out, I ended up with this plan:</p>
<p>
  <img src="./sketch.png" alt="Pause menu sketch">

</p>
<p>Until now, hitting Escape would just exit the game immediately. Not ideal.
triangle needed a proper menu: something that pauses the game, gives players a
chance to resume or quit intentionally, and maybe even shows a bit of useful
info.</p>
<p>So that’s what I’ve been building. The menu now:</p>
<ul>
<li>Pops up when Escape is pressed (as long as no panels are open).</li>
<li>Pauses the game,</li>
<li>Shows a few buttons: Resume, Quit, Contact, and Config Path</li>
</ul>
<h2 id="ordering">Ordering</h2>
<p>At first, I was a bit confused as to why I was not able to see the menu. I was
under the (mistaken) impression that dvui would always draw last.</p>
<p>Once I switched things around to always draw dvui at the end, I could see the
menu.</p>
<h2 id="pauseresume">Pause/Resume</h2>
<p>Since we use the frame time to determine how much time has passed, pausing was
simply a case of setting that time elapsed to zero.</p>
<p>In fact, it skips the update call altogether. One thing I noticed while I was
doing that was that the camera movement code happened in <code>render</code>. While there
is a particular logic to that, it&rsquo;ll need to be moved to <code>update</code> (later), at
which point, render will also not require the elapsed time.</p>
<p>Resume was simply a case of letting &ldquo;time flow&rdquo; again.</p>
<h2 id="logging">Logging</h2>
<p>I thought logging might be useful — but writing to file or supporting runtime
filtering requires a custom log handler. While it&rsquo;s probably doable, I didn’t
want to dive into it just yet. For now, I’ve left it out entirely. Maybe a
separate debug build is the better option anyway.</p>
<h2 id="contact">Contact</h2>
<p>I set up a form at <a href="https://tally.so">tally.so</a> and ChatGPT helped me write a
tiny bit of code that opens a browser to that form.</p>
<p>I&rsquo;ve not tested this on Mac/Windows and would appreciate any feedback (when a
<a href="https://icle.es/sprout.md">sprout</a> build is available.)</p>
<h2 id="quit">Quit</h2>
<p>We now have a quit button that will quit the game. The game will also currently
quit if you hit <code>q</code>. I&rsquo;ll unmap this later.</p>
<h2 id="config-path">Config path</h2>
<p>The config location is also displayed. The path can be selected and copied - so
that the player can navigate there easier to be able to override controls etc.</p>
<h2 id="final">Final</h2>
<p>
  <img src="./final.png" alt="Final menu">

</p>
<p>While perhaps not as pretty as the sketch (credit is more to tldraw than me), it
is functional.</p>
<p>Next steps are to get a dialog working with some basic information, including a
changelog and possibly a roadmap.</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://icle.es/youtube/triangle/menu.md">YouTube Video</a></li>
<li>Let&rsquo;s code devlogs
<ul>
<li><a href="https://icle.es/youtube/shri-codes/triangle/menus-start.md">#1.1 - Getting Started</a></li>
<li><a href="https://icle.es/youtube/shri-codes/triangle/menus-theming.md">#1.2 - Theming &amp; More Buttons </a></li>
<li><a href="https://icle.es/youtube/shri-codes/triangle/menus-finalising.md">#1.3 - Wiring &amp; Finalising</a></li>
</ul>
</li>
<li>Prev: <a href="https://icle.es/2025-06-08-config.md">Under the Hood of Triangle</a></li>
</ul>
]]></content:encoded></item><item><title>The End</title><link>https://icle.es/2025/07/02/the-end/</link><pubDate>Wed, 02 Jul 2025 12:17:38 +0100</pubDate><guid>https://icle.es/2025/07/02/the-end/</guid><description>&lt;p>It&amp;rsquo;s funny how much can change in a moment. There was once a ship that was
happy. It had sharp edges and a soft soul. It loved feeling the cool sea breeze,
the mist as it flew close to the ocean. It loved the heat of the desert, and it
loved to watch the setting sun.&lt;/p>
&lt;p>It loved to watch the birds, to race galloping creatures below, often pretending
to lose.&lt;/p></description><content:encoded><![CDATA[<p>It&rsquo;s funny how much can change in a moment. There was once a ship that was
happy. It had sharp edges and a soft soul. It loved feeling the cool sea breeze,
the mist as it flew close to the ocean. It loved the heat of the desert, and it
loved to watch the setting sun.</p>
<p>It loved to watch the birds, to race galloping creatures below, often pretending
to lose.</p>
<p>One morning, not unlike many others, it was resting atop a mountain watching the
sun rise, basking in the warmth of the sun and the twittering of the birds.</p>
<p>It&rsquo;s funny how much can change in a moment.</p>
<p>It was now drifting in silence surrounded by what looked like asteroids. The
warmth of the sun felt like a distant memory. How many moments had passed in
between? It did not know.</p>
<p>Beyond the asteroids, far in the distance, it could make out vague shapes.
Amongst them, one it almost recognised - a segment of a shattered sphere. The
ship recognised the ocean, now spilling into the black of space. It had once
felt the mist from that ocean, many years ago. It would not skim that ocean
again.</p>
<p>It slowly became aware of the structure - gargantuan, black, looming in the
dark. It hung weightlessly above what was once the ship&rsquo;s home, barely visible,
quietly consuming the remains. Methodically, mechanically, it tore through the
wreckage, scattering crumbs into the void - rocks, dust, fragments of what once
was.</p>
<p>The ship found itself drifting in silence surrounded by what looked like
asteroids.</p>
<p>There was no asteroid belt here before.</p>
]]></content:encoded></item><item><title>Under the Hood of triangle</title><link>https://icle.es/2025/06/27/under-the-hood-of-triangle/</link><pubDate>Fri, 27 Jun 2025 08:01:28 +0100</pubDate><guid>https://icle.es/2025/06/27/under-the-hood-of-triangle/</guid><description>&lt;p>In &lt;a href="https://youtu.be/8nA-a5Z1IDc">this Let&amp;rsquo;s Code video&lt;/a> on
&lt;a href="http://www.youtube.com/@ShriCodesHere">&lt;strong>shri codes&lt;/strong>&lt;/a>, I walk through some of
the early systems in place for &lt;a href="https://icle.es/tags/triangle/">&lt;em>Triangle&lt;/em>&lt;/a> - my space
ARPG built in &lt;strong>Zig&lt;/strong> with &lt;strong>raylib&lt;/strong>.&lt;/p>
&lt;p>For the broader story behind the project - what &lt;em>Triangle&lt;/em> is, where it&amp;rsquo;s going,
and why I’m making it - check out the
&lt;a href="https://icle.es/endeavours/triangle.md">triangle endeavour&lt;/a> or the
&lt;a href="https://icle.es/tags/triangle/">devlog archive&lt;/a>. This post is about the &lt;em>how&lt;/em>.&lt;/p>
&lt;h3 id="-core-systems-so-far">🧩 Core Systems So Far&lt;/h3>
&lt;p>At this stage, Triangle supports:&lt;/p>
&lt;ul>
&lt;li>Basic player movement and shooting (Asteroids-style).&lt;/li>
&lt;li>Procedurally generated asteroid fields per &lt;em>sector&lt;/em>.&lt;/li>
&lt;li>Material drops (iron ore) from destroyed asteroids.&lt;/li>
&lt;li>Automatic recipe unlocking when new items are picked up.&lt;/li>
&lt;li>Smelting system that converts ore to ingots.&lt;/li>
&lt;li>Unlocking and queuing up &lt;em>constructors&lt;/em> using the crafting menu.&lt;/li>
&lt;li>A basic UI for crafting and inventory.&lt;/li>
&lt;li>Configurable keyboard mappings using TOML.&lt;/li>
&lt;/ul>
&lt;p>That’s the visible layer. Below the surface, the codebase has carved out some
space for more ambitious systems.&lt;/p></description><content:encoded><![CDATA[<p>In <a href="https://youtu.be/8nA-a5Z1IDc">this Let&rsquo;s Code video</a> on
<a href="http://www.youtube.com/@ShriCodesHere"><strong>shri codes</strong></a>, I walk through some of
the early systems in place for <a href="https://icle.es/tags/triangle/"><em>Triangle</em></a> - my space
ARPG built in <strong>Zig</strong> with <strong>raylib</strong>.</p>
<p>For the broader story behind the project - what <em>Triangle</em> is, where it&rsquo;s going,
and why I’m making it - check out the
<a href="https://icle.es/endeavours/triangle.md">triangle endeavour</a> or the
<a href="https://icle.es/tags/triangle/">devlog archive</a>. This post is about the <em>how</em>.</p>
<h3 id="-core-systems-so-far">🧩 Core Systems So Far</h3>
<p>At this stage, Triangle supports:</p>
<ul>
<li>Basic player movement and shooting (Asteroids-style).</li>
<li>Procedurally generated asteroid fields per <em>sector</em>.</li>
<li>Material drops (iron ore) from destroyed asteroids.</li>
<li>Automatic recipe unlocking when new items are picked up.</li>
<li>Smelting system that converts ore to ingots.</li>
<li>Unlocking and queuing up <em>constructors</em> using the crafting menu.</li>
<li>A basic UI for crafting and inventory.</li>
<li>Configurable keyboard mappings using TOML.</li>
</ul>
<p>That’s the visible layer. Below the surface, the codebase has carved out some
space for more ambitious systems.</p>
<h3 id="-code-structure-overview">🗂️ Code Structure Overview</h3>
<p>Here’s a quick tour of the key directories and their purpose:</p>
<h4 id="blit"><code>blit/</code></h4>
<p>Handles all the 2D rendering abstractions - bounding boxes, shapes (including
triangle fans 😉 and circles), and canvas drawing. Wraps <code>raylib</code> for better
namespacing and type control.</p>
<h4 id="phys"><code>phys/</code></h4>
<p>Physics system. <code>body.zig</code> implements basic movement, collisions, and inertia.
Still minimal, but usable.</p>
<h4 id="combat"><code>combat/</code></h4>
<p>Still early - only <code>bullet</code> is implemented, and is used to destroy asteroids.
Placeholder for upcoming systems like health, damage, and targeting.</p>
<h4 id="items"><code>items/</code></h4>
<p>This module forms the backbone of the crafting system. Items are tied to
recipes, and recipes are triggered via pickups or crafting actions. There&rsquo;s a
lot more to unpack here - likely its own devlog soon.</p>
<h4 id="notifier"><code>notifier/</code></h4>
<p>Manages in-game UI feedback. When you pick something up or unlock a new recipe,
this is what shows it. Notifiers are scoped - one for inventory, one for
crafting - and show up on either side of the screen.</p>
<h4 id="ship"><code>ship/</code></h4>
<p>This module contains the ship related logic, including movement, and the logic
for constructing smelters and other buildables. Eventually this will handle
assembly chains.</p>
<h4 id="asteroid"><code>asteroid</code></h4>
<p>These files should be refactored into its own module, and should contain the
asteroid itself, as well as sector, a chunk of space - asteroids, entities,
bullets, etc. This is the closest thing to a “level” in Triangle. It’s
procedurally generated, and you can eventually travel from one sector to the
next.</p>
<h4 id="diag"><code>diag/</code></h4>
<p>Code to support diagnostics &amp; logging</p>
<h4 id="game"><code>game/</code></h4>
<p>This module should get a bit of refactoring as well, moving some of the files
from the root into its own module</p>
<ul>
<li><code>config</code>: Takes care of user control bindings and input overrides via a TOML
config file.</li>
<li><code>notifier</code>: supports collecting notifications across multiple channels.</li>
<li><code>save</code>: Just scaffolding for now - no save/load logic yet, but it’s a
placeholder to group serializable components together.</li>
<li><code>context</code>: container struct to support dependency injection.</li>
</ul>
<h4 id="ui"><code>ui/</code></h4>
<p>Holds in-game panels like crafting and inventory. Needs cleanup and
consolidation.</p>
<h3 id="-whats-missing">🧪 What’s Missing</h3>
<p>A lot of the systems are stubbed out, but not yet wired in. For instance:</p>
<ul>
<li><code>power</code> is empty (future energy system)</li>
<li>The <em>canvas</em> currently wraps <code>raylib</code> without adding much - that will likely
change as UI complexity grows.</li>
</ul>
<h3 id="-threads-ill-pull-next">🧶 Threads I’ll Pull Next</h3>
<p>These are the next things I’ll either build or record:</p>
<ul>
<li>Improving the coding structure. I&rsquo;d like to improve organisation and am
considering extracting a re-usable library out of it, mainly so I can tinker
with other smaller game projects.</li>
<li>A <strong>rudimentary menu system</strong></li>
<li>A <strong>build+deploy system</strong> to generate early test builds</li>
<li>Possibly a <strong>contact/feedback</strong> form directly in-game or on the site</li>
</ul>
<p>I’ll be releasing this in what I’m calling a <a href="https://icle.es/sprout.md"><strong>&ldquo;Sprout&rdquo; build</strong></a> -
a playable seed, a form of extremely early access. Feedback and collaboration is
really important to me and I want to get this out there as soon as there is the
tiniest spark of &ldquo;life.&rdquo;</p>
<p>If you’d like to follow the journey, you can
<a href="https://youtu.be/8nA-a5Z1IDc">watch the video devlog</a></p>
]]></content:encoded></item><item><title>Surviving to Create</title><link>https://icle.es/2025/06/26/surviving-to-create/</link><pubDate>Thu, 26 Jun 2025 11:06:47 +0100</pubDate><guid>https://icle.es/2025/06/26/surviving-to-create/</guid><description>&lt;p>&lt;a href="https://drone-ah.com/2025/04/26/a-lonely-triangle/">&lt;em>triangle&lt;/em>&lt;/a> is about
depression, recovery, and resilience - things I’ve lived through and am still
figuring out. It&amp;rsquo;s in the early stages, and far from making any money.&lt;/p>
&lt;h2 id="the-real-stuff-behind-making-a-game">The Real Stuff Behind Making a Game&lt;/h2>
&lt;p>Making games is hard! Making games independently is harder. When you&amp;rsquo;re doing it
solo, it&amp;rsquo;s harder still.&lt;br>
It’s juggling health, money, and energy - not to mention motivation.&lt;/p>
&lt;p>I’ve spent over 20 years in tech, running a company, building stuff like
megabus.com’s ticketing system.&lt;br>
After burning out and dealing with some tough health issues, I&amp;rsquo;m struggling with
even the idea of picking up freelance work or hustling like I used to. I have
about 3 months of money left, so I need to be smart.&lt;/p></description><content:encoded><![CDATA[<p><a href="https://drone-ah.com/2025/04/26/a-lonely-triangle/"><em>triangle</em></a> is about
depression, recovery, and resilience - things I’ve lived through and am still
figuring out. It&rsquo;s in the early stages, and far from making any money.</p>
<h2 id="the-real-stuff-behind-making-a-game">The Real Stuff Behind Making a Game</h2>
<p>Making games is hard! Making games independently is harder. When you&rsquo;re doing it
solo, it&rsquo;s harder still.<br>
It’s juggling health, money, and energy - not to mention motivation.</p>
<p>I’ve spent over 20 years in tech, running a company, building stuff like
megabus.com’s ticketing system.<br>
After burning out and dealing with some tough health issues, I&rsquo;m struggling with
even the idea of picking up freelance work or hustling like I used to. I have
about 3 months of money left, so I need to be smart.</p>
<p>On the positive side, I’ve got the experience - in dev and solo work - to see
this through,<br>
I just need to figure out a sustainable financial path.</p>
<h2 id="what-im-doing-now">What I’m Doing Now</h2>
<p>Here’s my current rough plan to keep <em>triangle</em> moving without burning out:</p>
<h3 id="1-building-community">1. Building Community</h3>
<p>I&rsquo;ve been posting devlogs and videos, but haven&rsquo;t really focused on building a
following or community. I loathe the &ldquo;like and subscribe&rdquo; crowd, so I don&rsquo;t want
to be doing that. I want to focus on adding value.</p>
<p>It&rsquo;ll take me longer, and that&rsquo;s ok - I have to figure out the finances, but
that&rsquo;s a separate problem.</p>
<p>I want to keep doing what I&rsquo;ve been doing. Devlogs, videos, sharing and
collaborating. I&rsquo;ll have more time for a bit as well since the main project I
was working on has now been canceled.</p>
<p>I also want to make some improvements to this site to link things up a bit
better and make it a bit easier to find things. That&rsquo;ll have to happen on a
&ldquo;little bit here and there&rdquo; kind of basis though.</p>
<h3 id="2-going-for-grants">2. Going for Grants</h3>
<p>I’m looking at funding from places like Creative Scotland that support mental
health and creative projects.<br>
<em>triangle</em> isn’t just a game - I&rsquo;m trying to express something real, and I’m
trying to communicate that clearly.</p>
<h3 id="3-maybe-a-kickstarter">3. Maybe a Kickstarter</h3>
<p>Further down the line, I might run a Kickstarter -<br>
but only if I can find the right piece of work that adds real value for everyone
involved: myself, the game, and potential contributors.</p>
<h3 id="4-patreon--ko-fi---but-no-paywalls">4. Patreon / Ko-fi - But No Paywalls</h3>
<p>Once I have enough of a following / community (5k - 10k subs), I could set up
Patreon (and/or maybe Ko-fi), but no paywalls - I don&rsquo;t like them. I want to
invite people who want to support and join in, and participate.<br>
Everything stays public - devlogs, updates, builds.<br>
Supporters get perks like name credits and priority access.</p>
<h3 id="4-loans-only-if-i-have-to">4. Loans Only If I Have To</h3>
<p>If I really need it, I’ll consider a small loan to get through a crunch -<br>
but only if it helps hit a real milestone, not just to stretch things out.</p>
<h2 id="other-stuff-im-doing">Other Stuff I’m Doing</h2>
<p>I’ve also started a coding video channel,
<a href="https://www.youtube.com/@ShriCodesHere"><em>Shri Codes</em></a>.<br>
It’s a way to share bits of the process behind building <em>triangle</em>, maybe other
excursions and the things I’m learning along the way.</p>
<p>This blog is where I write devlogs, ideas, and rambling thoughts -<br>
it’s turning into a bit of a central hub that connects everything else I’m
doing. I have a bunch of other things that I&rsquo;ve done in the past (and am working
on now) that I&rsquo;d like to put on here as well. Of course, that takes up time too,
and I&rsquo;m not sure how much real value that will add.</p>
<h2 id="if-you-want-to-follow-along">If You Want to Follow Along</h2>
<p>You&rsquo;re welcome to follow along on whatever platform works for you - no pressure,
no strings.<br>
I love collaboration and would really value your thoughts, whenever you feel
ready to join in.</p>
<p>If you have advice, experience, or just want to say hi, please do. It means a
lot. I&rsquo;ll take every bit of encouragement and support I can get.</p>
]]></content:encoded></item><item><title>Managing Configuration</title><link>https://icle.es/2025/06/08/managing-configuration/</link><pubDate>Sun, 08 Jun 2025 21:47:05 +0000</pubDate><guid>https://icle.es/2025/06/08/managing-configuration/</guid><description>&lt;p>Before making the game available for playtesting, I wanted the player to be able
to configure the game to some degree.&lt;/p>
&lt;p>As a starting point, my keyboard layout is &lt;code>colemak&lt;/code>, and I doubt that the
controls I use would suit the majority of players.&lt;/p>
&lt;p>I am putting off a UI based config management option down the road (did I
mention that I do not enjoy GUI work?). As such I&amp;rsquo;ve been pondering alternative
configuration options.&lt;/p></description><content:encoded><![CDATA[<p>Before making the game available for playtesting, I wanted the player to be able
to configure the game to some degree.</p>
<p>As a starting point, my keyboard layout is <code>colemak</code>, and I doubt that the
controls I use would suit the majority of players.</p>
<p>I am putting off a UI based config management option down the road (did I
mention that I do not enjoy GUI work?). As such I&rsquo;ve been pondering alternative
configuration options.</p>
<h2 id="platform-independence">Platform independence</h2>
<p>Before I even get to that, the first problem I need to solve is a way to
determine the location for the config files independent of the platform.</p>
<p>Fortunately, <a href="https://github.com/ziglibs/known-folders">known-folders</a> came to
the rescue and provided an easy to use framework that can be used to determine
the various relevant locations for multiple platforms.</p>
```zig
const known_folders = @import("known-folders");
const maybe_config = try known_folders.getPath(allocator, .roaming_configuration);
if (maybe_config) |config| {
    defer allocator.free(config);
    std.debug.print("roaming config path: {s}\n", .{config});
}
```
<h2 id="locations">Locations</h2>
<p>There are three real locations of relevance for triangle</p>
<ul>
<li>The binary / package</li>
<li>Config, technically, split into two
<ul>
<li>user (or in Windows parlance, the remote dir, and can be shared across
computers)</li>
<li>system (in windows parlance, local, and is specific to that system)</li>
</ul>
</li>
<li>Save Data</li>
</ul>
<h2 id="user--game-config-files">User &amp; Game Config Files</h2>
<p>With that sorted out, the next bit is to identify the relevant config files. I
expect that triangle will continue to use these, and will eventually just get a
UI config option as well.</p>
<h3 id="user-config">User Config</h3>
<p>There are two main bits of user configuration</p>
<ul>
<li>Preference like controls</li>
<li>System details like resolution</li>
</ul>
<p>I am currently unsure when it&rsquo;ll support system config.</p>
<h3 id="game-config">Game Config</h3>
<p>There are two bits of config that the game will store. One set of config is to
remember game choices the user has made.</p>
<h4 id="remember-player-actions">Remember Player Actions</h4>
<p>For example, it will be useful to show the user details of changes to the game
since they last played. To do this, we need to track the last set of changes
that the user saw.</p>
<p>The game will show a notice on startup about its extremely early access status,
and provide an option for the user to hide that in the future. We need to save
that somewhere too.</p>
<h4 id="telemetry">Telemetry</h4>
<p>The second bit of config is metrics. While a lot of games will simply send
telemetry information directly to the developer, player privacy is really
important to me. I recognise that I will get far less data because of this, and
that there will be a bit of survivorship bias with the data - but I feel that
privacy is more important.</p>
<p>The way I want telemetry to work is that it will all be saved in a human
readable telemetry file in the config file location.</p>
<p>The data is stored only locally, and is never automatically sent. The player is
welcome to use this data for themselves if they wish and also share at their
discretion. The information will be stored in a human readable format that
should be as easy to understand as possible - no data dumps.</p>
<p>The location will also store logs (if enabled).</p>
<h2 id="setting-config">Setting Config</h2>
<p>In terms of allowing the player to manage config, there are a couple of
challenges:</p>
<ul>
<li>Providing enough documentation that it is easy to do</li>
<li>Allowing for updates, particularly to the addition of new keys</li>
</ul>
<p>To tackle this, I am going to provide an annotated template file with all the
config options. The user can create a separate file based on this with <strong>only</strong>
the config they wish to override.</p>
<p>It will be tricky to change how particular parameters are configured. E.g. If a
single value key needs to switch to an array. I&rsquo;d be loathe to sprinkle the code
with checks for legacy keys/formats. We&rsquo;ll play it by ear.</p>
<p>I considered updating the config file automatically, but this would discard any
comments the user had added. While I could offer an option to merge changes in,
it’s not straightforward enough to implement just yet.</p>
<h2 id="format">Format</h2>
<p>I&rsquo;ve been considering <code>toml</code> and <code>yaml</code> for this, with
<a href="https://github.com/sam701/zig-toml/">zig-toml</a> and
<a href="https://github.com/kubkon/zig-yaml">zig-yaml</a> respectively.</p>
<p><code>zig-yaml</code> seems to be more active (more stars, forks, issues and pr&rsquo;s and
currently also the more recent commit).</p>
<p>I am also more familiar with and prefer yaml.</p>
<p>However, it does not currently support default values. I would like the user to
have to specify only the config they&rsquo;d like to override. <code>zig-yaml</code> currently
expects all the keys to be defined if you want to parse it into a struct.</p>
<p><a href="https://github.com/kubkon/zig-yaml/issues/85">#85</a> should bring it in, but I
<a href="https://github.com/kubkon/zig-yaml/issues/92">could not get it to work</a></p>
<p>So, I tried out <code>zig-toml</code> and the test worked the first time.</p>
<p><a href="https://github.com/drone-ah/wordsonsand/tree/main/code/zig/src/toml_with_defaults.zig">src/toml_with_defaults.zig</a></p>
```zig
const std = @import("std");

const Controls = struct {
    forward: []const u8 = "w",
    craft: []const u8 = "q",
    inventory: []const u8 = "e",
};

const User = struct {
    controls: Controls = .{},
};

test "load partial toml config" {
    const toml = @import("toml");
    const allocator = std.testing.allocator;
    var parser = toml.Parser(User).init(allocator);
    defer parser.deinit();

    const source =
        \\[controls]
        \\craft = "s"
    ;
    var result = try parser.parseString(source);
    defer result.deinit();

    const config = result.value;
    const default = User{};
    try std.testing.expectEqualStrings(default.controls.forward, config.controls.forward);
    try std.testing.expectEqualStrings("s", config.controls.craft);
}
```
<h1 id="using-the-config">Using the config</h1>
<p>The final part is to <em>use</em> the config.</p>
<h2 id="loading-config">Loading config</h2>
<p>All config is loaded at startup and attached to a <code>Config</code> struct, which is in
turn part of a <code>Context</code> struct that is passed around.</p>
<p><a href="https://github.com/drone-ah/wordsonsand/tree/main/code/zig/src/load_save_config.zig">src/load_save_config.zig</a></p>
```zig
user: User,

game_path: []const u8,
game: Game,

pub fn init(allocator: std.mem.Allocator) ConfigError!Self {
    const maybe_config = known_folders.getPath(allocator, .roaming_configuration) catch {
        return ConfigError.UnableToDetermineConfigLocation;
    };
    if (maybe_config) |config| {
        defer allocator.free(config);

        // user config path
        const full_path = std.fmt.allocPrint(allocator, "{s}/triangle/user.toml", .{config}) catch {
            std.debug.panic("oom", .{});
        };
        defer allocator.free(full_path);

        // game config path
        const game_path = std.fmt.allocPrint(allocator, "{s}/triangle/game.toml", .{config}) catch {
            std.debug.panic("oom", .{});
        };

        return .{
            .user = loadConfig(allocator, User, full_path),

            .game_path = game_path,
            .game = loadConfig(allocator, Game, game_path),
        };
    }

    return ConfigError.UnableToDetermineConfigLocation;
}

fn loadConfig(allocator: std.mem.Allocator, ConfigType: type, path: []const u8) ConfigType {
    var parser = toml.Parser(ConfigType).init(allocator);
    defer parser.deinit();

    var result = parser.parseFile(path) catch {
        log.warn("unable to read config file: {s}", .{path});
        return .{};
    };
    defer result.deinit();

    return result.value;
}
```
<h2 id="user-config-controls">User config (controls)</h2>
<p>This one involves a little translation as we need to know the <code>rl.KeyboardKey</code>
for each mapping to be able to detect it.</p>
<p>I use a <code>std.StringArrayHashMapUnmanaged(rl.KeyboardKey)</code> to map the string to
each key</p>
<p><a href="https://github.com/drone-ah/wordsonsand/tree/main/code/zig/src/load_save_config.zig">src/load_save_config.zig</a></p>
```zig
const Input = struct {
    keymap: KeyMaps,

    pub fn init(allocator: std.mem.Allocator) Self {
        var keymap = KeyMaps{};
        for (default_keybindings) |entry| {
            keymap.put(allocator, entry.name, entry.key) catch {
                std.debug.panic("oom", .{});
            };
        }

        return .{
            .keymap = keymap,
        };
    }

    const KeyMap = struct {
        name: []const u8,
        key: rl.KeyboardKey,
    };

    const default_keybindings = [_]KeyMap{
        .{ .name = "a", .key = .a },
        .{ .name = "b", .key = .b },
        .{ .name = "c", .key = .c },
    };
}
```
<h2 id="game-config-1">Game Config</h2>
<p>As a starting point, we&rsquo;ll probably only have one config entry here - the last
time the news was marked as read by the player.</p>
<p>We&rsquo;ll store this as an <code>i64</code> and loading it is exactly the same as above.</p>
<p>The main difference with the <code>Game</code> config class is that on writing any value,
it will also save it to disk.</p>
```zig
pub fn markNewsAsRead(self: *Self) void {
    self.game.news_read = std.time.timestamp();
    saveConfig(self.allocator, self.game, self.game_path);
}

fn saveConfig(allocator: std.mem.Allocator, Config: anytype, full_path: []const u8) void {
    const path = std.fs.path.dirname(full_path) orelse ".";
    std.fs.cwd().makePath(path) catch |err| { // creates parent dirs if needed
        log.warn("unable to save: {any}", .{err});
        return;
    };

    var file = std.fs.cwd().createFile(full_path, .{
        .read = false,
        .truncate = true,
    }) catch |err| {
        log.warn("unable to save: {any}", .{err});
        return;
    };
    defer file.close();

    var writer = file.writer().any();
    toml.serialize(allocator, Config, &writer) catch |err| {
        log.warn("unable to write to config file: {any}", .{err});
    };
}
```
<p><del>I ran into a bug where
<a href="https://github.com/sam701/zig-toml/issues/32">the api for serialization was broken</a>.
There is (currently)
<a href="https://github.com/sam701/zig-toml/pull/33">a pending pr #33 to resolve it</a></del></p>
<p>One of the challenges of using emerging language and ecosystem is that you&rsquo;re
more likely to run into bugs. One of the great joys of working with such
ecosystem is the greater opportunity to contribute and get involved!</p>
<h1 id="links">Links</h1>
<ul>
<li><a href="https://youtu.be/OVswrFoFNjM">YouTube Devlog</a></li>
<li>Prev: <a href="https://icle.es/2025-05-20-crafting-machines.md">Crafting Machines</a></li>
</ul>
]]></content:encoded></item><item><title>Crafting, Mods, and Machines</title><link>https://icle.es/2025/05/20/crafting-machines/</link><pubDate>Tue, 20 May 2025 14:27:00 +0000</pubDate><guid>https://icle.es/2025/05/20/crafting-machines/</guid><description>&lt;p>In this post, I want to explore the crafting system in triangle, especially how
mods, tiers, and machine interactions could create depth without overwhelming
complexity.&lt;/p>
&lt;p>Crafting in triangle should be a mix of random number generation (rng) and
deterministic. Crafted items will have a random set of mods. However, like in
&lt;a href="https://lastepoch.com/">Last Epoch&lt;/a>, you can extract the mods of the items.
These can then be placed into another item.&lt;/p>
&lt;p>It feels like mod crafting would be an interesting way to approach crafting in
general. Unlike in the Last Epoch, the extracted mods aren&amp;rsquo;t just combined back
together.&lt;/p></description><content:encoded><![CDATA[<p>In this post, I want to explore the crafting system in triangle, especially how
mods, tiers, and machine interactions could create depth without overwhelming
complexity.</p>
<p>Crafting in triangle should be a mix of random number generation (rng) and
deterministic. Crafted items will have a random set of mods. However, like in
<a href="https://lastepoch.com/">Last Epoch</a>, you can extract the mods of the items.
These can then be placed into another item.</p>
<p>It feels like mod crafting would be an interesting way to approach crafting in
general. Unlike in the Last Epoch, the extracted mods aren&rsquo;t just combined back
together.</p>
<p>What if each mod retains its values? What if you could combine two mods together
to upgrade or reroll them?</p>
<p>When you insert a mod into an item, what if it destroyed the mod that you were
replacing?</p>
<h2 id="tiers">Tiers</h2>
<p>All materials, items, and mods have tiers. Factories will only process items of
their tier or lower. A Tier 1 smelter cannot process a Tier 2 material and a
Tier 1 constructor cannot craft a Tier 2 item.</p>
<p>I am currently mulling over the idea of having nine tiers.</p>
<h2 id="items-aka-buildings-machines">Items (a.k.a Buildings? Machines?)</h2>
<p>I&rsquo;m toying with the idea of the tier determining how many mod slots it could
have. A Tier 2 item would have two mod slots and a Tier 5 item would have 5
slots.</p>
<p>This provides a natural power curve, even if the mods themselves do not have
tiers.</p>
<p>All items will also have up to one implicit mod. This mod will be item type
specific. e.g., for smelters, it will be <code>smelting rate</code></p>
<p>From a concept perspective, there are probably buildings, or some kind of
structure that can be slotted on to the ship.</p>
<p>Perhaps buildings are a better name for them than items.</p>
<h3 id="slots--hardpoints">Slots / Hardpoints</h3>
<p>I see two kinds of slots on the ship</p>
<ul>
<li><em>Interior slot</em>: Machines like the smelters will go in here</li>
<li><em>Exterior Slot</em>: Weapons, Armour, Hull Extensions etc. could go on to these.</li>
</ul>
<h2 id="mods">Mods</h2>
<p>Currently mods also support tiers, but I am wondering whether the power creep
could become exponential, combined with the additional mod slots. There may be
good ways to mitigate it though, which will only become apparent once some of
these mechanics have been implemented.</p>
<p>Another reason to skip tiers on mods is if that means that the player ends up
with far too many mods in their inventory.</p>
<p>There are a couple of key areas in which I am thinking about deviating from
other games (like the Diablo series, PoE 1/2, Last Epoch etc.)</p>
<h3 id="local-only">Local only</h3>
<p>Each mod applies only to the item it is on. A firing rate increase on your
smelters won&rsquo;t do anything - since they don&rsquo;t fire. A smelting speed increase on
your weapon will likewise have no effect. It would make sense to extract or
replace the ineffective mods.</p>
<p>If you have two weapons attached, and one of them has a mod that increases
firing rate, that will impact only that weapon.</p>
<h3 id="no-prefix--suffix-mods">No Prefix / Suffix mods</h3>
<p>There is no classification of mods as prefix/suffix or limits of each type. You
can have full attack mods on if you like. Considering the previous restriction,
it makes more sense to stack attack on weapons and defense on armour.</p>
<h2 id="machines">Machines</h2>
<p>There are a few factory based machines I have in mind so far.</p>
<p>All these machines will have a rate at which they will complete their work. I
have been pondering whether higher tier machines will complete lower tier work
faster.</p>
<h2 id="smelters">Smelters</h2>
<p>The standard refinery. It will convert ore and scrap (obtained when enemy ships
are destroyed, and from scrapping items) into refined materials.</p>
<h2 id="constructor">Constructor</h2>
<p>Converts refined materials into items of Tier 1. For Tier 2 onwards, I am
considering taking items from the previous tier plus a refined material of the
new tier. E.g. to craft a Tier 2 factory, it could take 2 x Factory Mk. I + 30
Tier 2 materials (whatever that turns out to be)</p>
<p>This could keep the recipes easy to understand and reason about. It also
provides one use for poorly rolled items.</p>
<h2 id="disassemblers">Disassemblers</h2>
<p>These function similar to the Foundry in Last Epoch, at least with regards to
extracting all the mods from the item.</p>
<p>I haven&rsquo;t decided how deterministic it will be. Will it extract all items or
could some be damaged in the process and maybe turned to scrap?</p>
<h2 id="scrappers">Scrappers</h2>
<p>These are a bit like the trash bin. If crafting requires lower tier items, these
might not be necessary. TBD!</p>
<h2 id="foundry">Foundry</h2>
<p>This would be the machine that could do mod crafting. I am still a little foggy
about how or if this machine could work.</p>
<p>Should mod crafting involve risk? Like combining two mods to maybe create a
higher tier one - or failing and getting scrap?</p>
<p>What about rerolling a mod by sacrificing another mod?</p>
<p>All ideas accepted :)</p>
<h2 id="augmentor">Augmentor</h2>
<p>The Augmentor could be the final step in the pipeline - letting you insert or
fine-tune mods once you’ve refined or crafted them.</p>
<p>This machine will work instantly so that it doesn&rsquo;t tie up the items currently
in use. It would allow you to modify items up to its own tier.</p>
<h2 id="triangle">triangle</h2>
<p>With machines slotted directly onto the ship, triangle becomes a living,
drifting factory - salvaging, upgrading, refining, evolving, fighting, and
surviving!</p>
<h2 id="other-posts">Other posts</h2>
<ul>
<li><a href="https://youtu.be/livphL9lOxo">Companion vlog for this post</a></li>
<li><a href="https://icle.es/2025-05-13-materials.md">Prev: Materials &amp; Pickups </a></li>
<li>Next: Coming Soon</li>
</ul>
]]></content:encoded></item><item><title>Materials</title><link>https://icle.es/2025/05/13/materials/</link><pubDate>Tue, 13 May 2025 12:07:00 +0000</pubDate><guid>https://icle.es/2025/05/13/materials/</guid><description>&lt;p>In triangle, everything should feel earned - so all items are crafted, not
found. I am also a big fan of factory / automation games. How much of a factory
sim can we squeeze into an ARPG (or on to a triangle for that matter)? Let&amp;rsquo;s
find out!&lt;/p>
&lt;h2 id="drops">Drops&lt;/h2>
&lt;p>As a starting point, when asteroids are destroyed, they will drop materials.
I&amp;rsquo;ve also been pondering whether materials could drop when they split. It makes
logical sense, but I want to get a better sense of pacing before diving into
that question.&lt;/p></description><content:encoded><![CDATA[<p>In triangle, everything should feel earned - so all items are crafted, not
found. I am also a big fan of factory / automation games. How much of a factory
sim can we squeeze into an ARPG (or on to a triangle for that matter)? Let&rsquo;s
find out!</p>
<h2 id="drops">Drops</h2>
<p>As a starting point, when asteroids are destroyed, they will drop materials.
I&rsquo;ve also been pondering whether materials could drop when they split. It makes
logical sense, but I want to get a better sense of pacing before diving into
that question.</p>
<p>Currently, only iron drops. I started off by finding a sprite sheet with
reasonable looking sprites and then started integrating them in before I decided
to just stick with shapes — so iron drops are little blue circles.</p>
```zig
var shape = Shape.initCircle(7);
shape.move(pos);
shape.colour = .blue;
```
<p>PS: you haven&rsquo;t seen the <code>Shape</code> code, but you get the gist.</p>
<p>These drops are managed by a <code>MaterialField</code> that keeps track of all the dropped
materials. They are given a low linear velocity, and no rotational velocity
(it&rsquo;s a circle, so you wouldn&rsquo;t see it anyway).</p>
<p>They do not interact with collisions to avoid them being bumped off the screen
or you having to chase them down.</p>
<p>At some point, they could also be optimised to only update if they are on
screen. Assuming the player will pick up most of them, this may not be
necessary.</p>
<h2 id="pickup">Pickup</h2>
<p>I also &ldquo;installed an attractor&rdquo; on the ship such that if it gets close enough to
a material, it&rsquo;ll pull it in. When it&rsquo;s close enough, it&rsquo;ll be &ldquo;picked up.&rdquo;</p>
```zig
pub fn pickup(self: *Ship, material: *Material.Drop) bool {
    const magnet_sq = self.magnet_range * self.magnet_range;
    const pickup_sq = self.pickup_range * self.pickup_range;

    const dist = self.pos().sub(material.body.pos());
    const dist_sq = dist.magSquared();

    if (dist_sq <= pickup_sq) {
        self.save.inventory.addMatDrop(material);
        return true;
    }

    if (dist_sq <= magnet_sq) {
        // pull it towards us
        material.body.applyForce(dist.scale(self.magnet_force).sub(material.body.lvel));
    }

    return false;
}
```
<p>It is a physics interaction, so it is possible to slingshot the material and
have it fly off the screen (it&rsquo;s happened to me). It feels like a sun
interaction and you are in control of it, so I am inclined to leave that in.</p>
<p>Once it&rsquo;s close enough, it&rsquo;s picked up, and added to an inventory.</p>
<p>The attractor also feels like an upgrade that can be crafted later.</p>
<h2 id="smelting">Smelting</h2>
<h3 id="crafting-panel">Crafting Panel</h3>
<p>For smelting, I started by sketching out a straightforward UI. I&rsquo;ll admit up
front that I don&rsquo;t enjoy UI work, but in earnest I started on it, and I got as
far as displaying the panel itself and I got tremendously bored.</p>
<p>
  <figure>
    <img src="/assets/2025/05/craft-ui-sketch.png" alt="Crafting Panel Sketch" class="figcaption-img">
    <figcaption>Crafting Panel Sketch</figcaption>
  </figure>

</p>
<p>I decided to make material refinement prioritisation automatic, at least for the
time being. It would always prioritise the most valuable material that is in the
inventory.</p>
<p>Actually, that&rsquo;ll remove some factory micromanagement without removing the
feeling of agency from the player.</p>
<p>It&rsquo;ll mainly be a problem if the player has run out of lower tier materials and
they have a large amount of higher tier items in the inventory. I figure let&rsquo;s
wait until we have some playtesting before we worry about it. The system
currently only drops one material and it can be refined automatically.</p>
<h3 id="refining">Refining</h3>
<p>All materials and items will have a tier, starting with iron at 1. To be able to
smelt a material, you&rsquo;ll need a smelter at a minimum of that tier. For example
to craft iron, you need a minimum tier 1 smelter (which is fine since 1 is the
minimum tier anyway). If you wanted to smelt a tier 3 material, you&rsquo;ll need a
minimum of tier 3 smelter.</p>
<p>The ship starts with a Tier 1 smelter and will always have a tier 1 smelter.
This smelter cannot be modified, removed, or destroyed. The same is true of some
other factories, which will be covered in the next post.</p>
<p>The first iteration of refining took the total capacity of all smelters at each
tier, and would convert a portion of the materials from <code>ore</code> to <code>ingot</code>. It
used floating points to track progress. If it would take four seconds to convert
one ore to ingot, over one second, it&rsquo;d transfer 0.25 from ore to ingot.</p>
<p>The display only showed integers, so there was a bit of <code>floor</code> and <code>ceil</code> to be
able to show consistent numbers.</p>
<p>I didn&rsquo;t like this way of doing things. It felt icky — hacky — but it was simple
enough and it works, for now.</p>
<h2 id="other-posts">Other posts</h2>
<ul>
<li><a href="https://youtu.be/8ct9aWNj3Zk">Companion vlog for this post</a></li>
<li><a href="https://icle.es/2025-05-10-asteroid-field.md">Prev: Procedural Asteroid Fields</a></li>
<li>Next:
<a href="https://icle.es/2025-05-20-crafting-machines.md">Refineries, Constructors and other other factory types in a world without conveyor belts.</a></li>
</ul>
]]></content:encoded></item><item><title>Procedural Asteroid Field Generation</title><link>https://icle.es/2025/05/10/asteroid-field/</link><pubDate>Sat, 10 May 2025 10:07:08 +0000</pubDate><guid>https://icle.es/2025/05/10/asteroid-field/</guid><description>&lt;p>In this post, I am going to cover procedural asteroid field generation. At a
high level, I wanted:&lt;/p>
&lt;ul>
&lt;li>An asteroid field that feels infinite&lt;/li>
&lt;li>Natural-looking distribution and density&lt;/li>
&lt;li>A safe starting area for the player&lt;/li>
&lt;/ul>
&lt;h2 id="spawning-asteroids">Spawning Asteroids&lt;/h2>
&lt;p>I explored strategies for spawning multiple asteroids on screen without overlaps
and avoiding the player’s starting zone. Initially, I considered a brute-force
collision check for each spawn candidate, but quickly realized it wouldn’t scale
well with many asteroids.&lt;/p>
&lt;p>I decided to split the screen into a grid. If the maximum radius of an asteroid
is &lt;code>r&lt;/code>, then each grid would be &lt;code>2r x 2r&lt;/code>, and would be able to accommodate
asteroids at their full size. The asteroid size is determined randomly, with a
minimum size. It also offsets the center of the x by a random amount, up to a
maximum of &lt;code>r&lt;/code>.&lt;/p></description><content:encoded><![CDATA[<p>In this post, I am going to cover procedural asteroid field generation. At a
high level, I wanted:</p>
<ul>
<li>An asteroid field that feels infinite</li>
<li>Natural-looking distribution and density</li>
<li>A safe starting area for the player</li>
</ul>
<h2 id="spawning-asteroids">Spawning Asteroids</h2>
<p>I explored strategies for spawning multiple asteroids on screen without overlaps
and avoiding the player’s starting zone. Initially, I considered a brute-force
collision check for each spawn candidate, but quickly realized it wouldn’t scale
well with many asteroids.</p>
<p>I decided to split the screen into a grid. If the maximum radius of an asteroid
is <code>r</code>, then each grid would be <code>2r x 2r</code>, and would be able to accommodate
asteroids at their full size. The asteroid size is determined randomly, with a
minimum size. It also offsets the center of the x by a random amount, up to a
maximum of <code>r</code>.</p>
<p>
  <figure>
    <img src="/assets/2025/05/asteroid-map.png" alt="The grid visualised" class="figcaption-img">
    <figcaption>The asteroid grid</figcaption>
  </figure>

</p>
<p>In theory, this means that if two adjacent cells spawn the full size asteroid
and offset in the &ldquo;wrong&rdquo; direction, they could start off overlapping. I&rsquo;ve not
seen this happen yet, likely because the full size asteroids are not that
common.</p>
<p>The grid has an additional offset in the x axis for each row. With this offset,
even if every asteroid was placed at the x center of each grid, they would not
look uniform.</p>
<p>Finally, for each grid cell, there is some further randomisation that determines
whether the asteroid will get spawned at all.</p>
<p>In hindsight - there might be more randomisation than we need. It works well
though.</p>
<p>The asteroids are also given a low starting velocity, both linearly and
rotationally.</p>
<p>Even if asteroids do overlap on spawn, the physics will correct it through the
collision detection.</p>
<h2 id="spawning-the-ship">Spawning the Ship</h2>
<p>The code for this part is a little icky right now. It checks the y position of
the ship and avoids spawning asteroids in any grid cell that overlaps the
starting location.</p>
<p>Of course, this should only matter for the asteroids spawned on the starting
screen. The rest of the asteroids don&rsquo;t need this check. It works just fine
though - for now :)</p>
<h2 id="making-it-infinite">Making it &ldquo;Infinite&rdquo;</h2>
<p>I pondered the best way to make it feel infinite without risk of slowdowns and
issues. A quadtree was an obvious answer. However, it felt too complicated since
I&rsquo;d never written one before, and I was worried about how I would move each
asteroid between each section as it moved. (Spoiler alert: I should have just
used quadtrees, and intend to move to it later.)</p>
<p>In the meantime, I wanted to build something which felt simpler, which was
basically to create chunks of asteroids, with each chunk being a little larger
than the size of the screen.</p>
<p>The system could then, based on the current y position, track just the current,
previous, and next chunks, and process only these asteroids.</p>
<p>When I got it working, it was pretty ok. The main problem I had now was the
asteroids leaving the screen and never coming back.</p>
<p>After I&rsquo;d implemented this, I ended up watching
<a href="https://www.youtube.com/watch?v=OJxEcs0w_kE">Coding Challenge #98: Quadtree</a>,
and boy did I feel silly about being scared about quadtrees. They were promptly
demystified and felt like a much simpler solution than what I have here - so
I&rsquo;ll add that to the list to change.</p>
<h2 id="asteroids-drifting-offscreen">Asteroids Drifting Offscreen</h2>
<p>One of the problems was that the asteroids would drift offscreen. Because I
wasn&rsquo;t updating all the asteroids, they&rsquo;d never come back on to the screen. In
fact, even if I updated <em>all</em> the asteroids, the screen would eventually clear.
I believe this was because there is a lot of offscreen space (mainly on the left
and right of the screen) that the asteroids could fly off to.</p>
<p>I tried various ways of wrapping the asteroids around after they hit a few
hundred pixels past the edge. I didn&rsquo;t want it to look like a wraparound, and
pretending like the screen was about twice the width helped.</p>
<p>However, the problem was now with the asteroids slinking out the bottom of the
screen (and the top too), and there wasn&rsquo;t an easy way to tackle those.</p>
<p>In the end, I ended up
<a href="https://www.youtube.com/watch?v=OAcXnzRNiCY">using an attractor</a>, which I
placed on the center of each chunk.</p>
<p>This worked surprisingly well that I didn&rsquo;t even need the horizontal wraparound.</p>
<p>I had also ended up making the <code>Chunk</code> struct a little heavy, and I ended up
refactoring it. While refactoring, I overoptimized it to only update asteroids
on the screen. This &ldquo;fix&rdquo; meant that asteroids off-screen aren&rsquo;t attracted to
the center of the chunk anymore, and the screen ends up clearing again.
Something to fix later - maybe when I implement the quadtree.</p>
<h2 id="debug-panel">Debug Panel</h2>
<p>There were various points when I was trying to track down issues that I reverted
to my habit of printing stuff out - but that was a nightmare because it was
printing the same thing in each frame.</p>
<p>Eventually, I got into the habit of displaying stuff on screen and drawing
different colours etc. I then wanted a way to see things easier and toggle some
of the debug features. To do this, I needed a GUI.</p>
<p>I looked at <a href="https://github.com/raysan5/raygui">raygui</a>, which looked good but
probably too basic for me. I don&rsquo;t enjoy GUI work and I figured something a
little more fleshed out would help.</p>
<p>I had already looked at <a href="https://github.com/david-vanderson/dvui">dvui</a> before
and it looked like it could be a good candidate.</p>
<p>Integrating it into <em>triangle</em> was pretty straightforward - at least once I
figured out how to add it to <code>build.zig</code>.</p>
<h2 id="next-steps">Next Steps</h2>
<p>Right now, the field feels big enough (around 15 chunks), but not truly
infinite. I&rsquo;m thinking of implementing a circular buffer - say, 8 or 10 chunks -
where old ones are recycled as the player moves forward. If a player backtracks,
most asteroids will have drifted off anyway, and replacements will blend in just
fine.</p>
<p>I’ll probably tackle that - and maybe finally add a quadtree - soon.</p>
<p><strong>Next up:</strong>
<a href="https://icle.es/2025-05-20-crafting-machines.md">crafting systems and how I’m handling material drops and recipes.</a></p>
<h2 id="other-posts">Other posts</h2>
<ul>
<li><a href="https://youtu.be/RXcBDC8Ki1w">Companion vlog for this post</a></li>
<li><a href="https://icle.es/2025-05-08-basic-gameplay.md">Basic Gameplay</a></li>
<li><a href="https://icle.es/2025-05-13-materials.md">Next: Materials &amp; Pickups</a></li>
</ul>]]></content:encoded></item><item><title>Basic Gameplay</title><link>https://icle.es/2025/05/08/basic-gameplay/</link><pubDate>Thu, 08 May 2025 10:07:08 +0000</pubDate><guid>https://icle.es/2025/05/08/basic-gameplay/</guid><description>&lt;p>The first goal was to get it working as far as the coding challenge itself.&lt;/p>
&lt;h2 id="the-ship">The Ship&lt;/h2>
&lt;p>Spawning the ship itself was relatively straightforward. I only needed three
points and &lt;code>drawTriangle&lt;/code> from &lt;code>raylib&lt;/code>.&lt;/p>
&lt;p>In the coding challenge, the ship was rotated with the keyboard, but I wanted
the ship to point to the mouse so that aiming was straightforward. Rotating to
the mouse was trickier — it involved &lt;code>atan2&lt;/code> and ChatGPT got me started.&lt;/p>
&lt;p>The coding challenge video did not worry about time lapsed between each frame,
but I wanted triangle to be framerate independent. That involved a bit of
jiggery pokery to get working, including determining how much the ship can move
within a time frame.&lt;/p>
&lt;p>Moving the ship was a bit easier, but based on the many videos of the Coding
Train that implemented force, velocity and dampening, it was pretty
straightforward. Dampening took a bit of trial and error. ChatGPT sent me down
the garden path initially with an over-complicated formula, but I was able to
simplify it later.&lt;/p>
&lt;p>Integrating the physics for linear momentum with the one for rotational momentum
was also quite nice — it meant that the ship could overshoot the aim when
rotating and will correct back.&lt;/p></description><content:encoded><![CDATA[<p>The first goal was to get it working as far as the coding challenge itself.</p>
<h2 id="the-ship">The Ship</h2>
<p>Spawning the ship itself was relatively straightforward. I only needed three
points and <code>drawTriangle</code> from <code>raylib</code>.</p>
<p>In the coding challenge, the ship was rotated with the keyboard, but I wanted
the ship to point to the mouse so that aiming was straightforward. Rotating to
the mouse was trickier — it involved <code>atan2</code> and ChatGPT got me started.</p>
<p>The coding challenge video did not worry about time lapsed between each frame,
but I wanted triangle to be framerate independent. That involved a bit of
jiggery pokery to get working, including determining how much the ship can move
within a time frame.</p>
<p>Moving the ship was a bit easier, but based on the many videos of the Coding
Train that implemented force, velocity and dampening, it was pretty
straightforward. Dampening took a bit of trial and error. ChatGPT sent me down
the garden path initially with an over-complicated formula, but I was able to
simplify it later.</p>
<p>Integrating the physics for linear momentum with the one for rotational momentum
was also quite nice — it meant that the ship could overshoot the aim when
rotating and will correct back.</p>
<h2 id="the-asteroids">The Asteroids</h2>
<p>The coding challenge worked with a fixed number of on screen asteroids and they
wrapped around. I needed to expand this to:</p>
<ul>
<li>A potentially infinite vertical scroller.</li>
<li>Collisions between the asteroids (currently the same as from the challenge,
only checks full circle collision)</li>
<li>Figure out how to handle asteroids moving out of the screen</li>
</ul>
<p>The above will be covered in a bit more depth in the
<a href="https://icle.es/2025-05-10-asteroid-field.md">next devlog</a>.</p>
<h2 id="the-camera">The Camera</h2>
<p>I also needed a follow camera. Unlike the original asteroids, the ship can move
up/down and the camera should follow it.</p>
<p>The current code looks something like this:</p>
```zig
/// ship_y: ship's current y position
/// ship_vy: ship's y velocity
/// dt: time elapsed since last update
/// margin: the zone out of which camera is moved
/// speed: maximum camera movement speed
fn updateCameraY(self: *Self, ship_y: f32, ship_vy: f32, dt: f32, margin: f32, speed: f32) void {
    const cam_y = self.camera.target.y;
    const inner_margin = margin * 0.8;

    // Predict future ship position
    // TODO: at high speeds, when the clamp goes off, the camera does a little shuffle
    // It would be nice to fix that
    const screen_height: f32 = @floatFromInt(self.screen.height);
    const predicted_y = ship_y + std.math.clamp(ship_vy * 30.0, -screen_height, screen_height);

    var target_y = self.camera.target.y;
    var target_clr: rl.Color = .red;

    const tmargin = cam_y - inner_margin;
    const bmargin = cam_y + inner_margin;
    if (predicted_y < tmargin) {
        target_y = predicted_y + margin;
        target_clr = .blue;
    } else if (predicted_y > bmargin) {
        target_y = predicted_y - margin;
        target_clr = .green;
    }

    if (target_y > screen_height / 2) target_y = screen_height / 2;
    const t = std.math.clamp(dt * speed, 0.0, 1.0);
    self.camera.target.y = lerp(cam_y, target_y, t);
}
```
<p>It predicts the position of the ship, based on its y velocity. If that&rsquo;s outside
the middle zone, it&rsquo;ll move the camera at up to the maximum speed. There is some
clamping to prevent things flying off at high speeds. It also prevents the
camera from overtaking the ship.</p>
<p>This code is mostly hacked together with some help from ChatGPT. It&rsquo;ll be
cleaned up later, once I have a better indication of possible ship speeds, and
how/if we&rsquo;ll zoom in and out as well.</p>
<p>Another important facet to consider with the camera was what to do with the
edges. Out of the four edges, only one is infinite.</p>
<p>The current solution is that the camera stops tracking when it gets to the
&ldquo;bottom.&rdquo; It also does not move to the left or right. The ship, on the other
hand is free to move out of the range of the camera. Currently, nothing changes
except that the camera does not follow.</p>
<p>Since the thrust works in the direction of the mouse, it&rsquo;s pretty easy to bring
the ship back on to the screen. This feels like the world is big and still out
there, we just don&rsquo;t track what happens out there.</p>
<p>I considered wrapping around on the left and right, but that felt more like the
ship was trapped in that zone. I want the feeling of
<a href="https://icle.es/2025-04-26-a-lonely-triangle.md#story">being trapped in vengeance</a> to be more
subtle ;)</p>
<h2 id="combat">Combat</h2>
<p>Combat is pretty much a mirror image of what happens in the coding challenge,
though I didn&rsquo;t have some of the helper functions. I learned some math :)</p>
<p>Initially, the update loop only handled asteroid collisions. I added bullets as
a separate field in the update loop. It then checks each bullet with every
asteroid in the active chunks (covered in
<a href="https://icle.es/2025-05-10-asteroid-field.md">devlog #2</a>).</p>
<p>If there is collision, the asteroid is split into two, moved apart a bit, and
given opposite linear momentum. The bullet also takes &ldquo;damage&rdquo; at this point,
and can be removed. The damage system is designed to support penetration, which
is currently not active.</p>
<p>If the spawned asteroids would be too small, nothing is spawned. Later on, this
would be the trigger to spawn a material drop.</p>
<h2 id="zig--raylib">Zig / Raylib</h2>
<p>Learning <a href="https://ziglang.org">zig</a> and <a href="https://www.raylib.com/">raylib</a> was a
big part of this. Fortunately, both were a lot of fun to learn and work with.</p>
<p>One thing I found annoying was that the vector functionality in raylib was
scattered around as individual functions instead of on the vector struct. While
this was understandable, with raylib being written in C, I found it a bit
frustrating.</p>
<p>I ended up writing my own Vector struct in zig and the functions that I used as
methods on that struct. It was an opportunity for me to learn some vector math
as well.</p>
<p>I also encapsulated raylib inside a <code>Canvas</code> struct. I probably still have some
<code>rl.</code> calls other places in the code, but the idea is that a canvas is passed
into any bits of code that needs it. The main help is that it&rsquo;ll convert our
version of <code>Vector2</code> to the one that raylib wants.</p>
<p>I am also thinking about how I want input handling to work. I would like to
encapsulate that into a separate struct. Right now, I mainly access raylib
directly.</p>
<h2 id="manual-testing">Manual testing</h2>
<p>While I am a big proponent and fan of Test Driven Development, I was happy
enough with manual testing for triangle. I found joy in seeing it work each time
I manage to get something working.</p>
<p>I do end up writing tests later, particularly when I got to bits of code that
was harder to test manually.</p>
<h2 id="feel">Feel</h2>
<p>triangle already feels fun and light. There are some clunky elements to be
ironed out, but so far, it feels good :)</p>
<h2 id="other-posts">Other posts</h2>
<ul>
<li><a href="https://youtu.be/F2ITT2-uKso">Companion vlog for this post</a></li>
<li><a href="https://icle.es/2025-04-26-a-lonely-triangle.md">Prev: A lone triangle vs the universe</a></li>
<li><a href="https://icle.es/2025-05-10-asteroid-field.md">Next: Procedural Asteroid Field</a></li>
</ul>]]></content:encoded></item><item><title>A lone triangle vs the universe</title><link>https://icle.es/2025/04/26/a-lonely-triangle/</link><pubDate>Sat, 26 Apr 2025 10:07:08 +0000</pubDate><guid>https://icle.es/2025/04/26/a-lonely-triangle/</guid><description>&lt;p>I&amp;rsquo;ve been unwell for a few months which has left me with a great deal of fatigue
and limited ability to be productive.&lt;/p>
&lt;p>One of the activities to pass time while healing was playing a lot of ARPG&amp;rsquo;s
recently, particularly &lt;a href="https://pathofexile2.com/home">PoE 2&lt;/a> and the
&lt;a href="https://lastepoch.com/">Last Epoch&lt;/a>. I&amp;rsquo;d even had a few ideas about how I might
do things a little differently. Since my brain capacity was pretty low, these
games with their repetitive nature and easy dopamine hits were really enjoyable.&lt;/p>
&lt;p>I found the gameplay a bit fragmented though, and having to follow guides was
annoying. I had been mulling over the idea of making something which flows, and
supports your decision making. It&amp;rsquo;s not about giving the player answers, but
about providing the player the most relevant information in an easy to digest
format. They still get to make the choices. I struggled to identify what
mattered - guides were easier.&lt;/p>
&lt;p>I did wonder if there was an easy way to just say - &amp;ldquo;use this build from this
guide&amp;rdquo; so that it would save me from all the tedious clicking :/&lt;/p>
&lt;p>All this left me wondering if I could build something that captured the fun
while keeping you in the flow. I even looked at how complicated it would be to
make an isometric game. Too complicated for me was the answer.&lt;/p>
&lt;p>Factory games, one of my other favourite genres felt a little too mentally
taxing. Otherwise, I would have dived into
&lt;a href="https://factorio.com/buy-space-age">Space Age&lt;/a>.&lt;/p>
&lt;p>I have also been bingeing on youtube videos and discovered a fantastic channel -
the &lt;a href="https://www.youtube.com/@TheCodingTrain">Coding Train&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;ve been almost religiously watching the
&lt;a href="https://www.youtube.com/watch?v=17WoOqgXsRM&amp;amp;list=PLRqwX-V7Uu6ZiZxtDDRCi6uhfTH4FilpH">Coding Challenges&lt;/a>
playlist and I have been loving it. I love how he makes 2d graphics programming
seems so easy and approachable. I used to be so intimidated by all the math.&lt;/p>
&lt;p>There was a slow confidence building in me that maybe - just maybe I could build
a game.&lt;/p></description><content:encoded><![CDATA[<p>I&rsquo;ve been unwell for a few months which has left me with a great deal of fatigue
and limited ability to be productive.</p>
<p>One of the activities to pass time while healing was playing a lot of ARPG&rsquo;s
recently, particularly <a href="https://pathofexile2.com/home">PoE 2</a> and the
<a href="https://lastepoch.com/">Last Epoch</a>. I&rsquo;d even had a few ideas about how I might
do things a little differently. Since my brain capacity was pretty low, these
games with their repetitive nature and easy dopamine hits were really enjoyable.</p>
<p>I found the gameplay a bit fragmented though, and having to follow guides was
annoying. I had been mulling over the idea of making something which flows, and
supports your decision making. It&rsquo;s not about giving the player answers, but
about providing the player the most relevant information in an easy to digest
format. They still get to make the choices. I struggled to identify what
mattered - guides were easier.</p>
<p>I did wonder if there was an easy way to just say - &ldquo;use this build from this
guide&rdquo; so that it would save me from all the tedious clicking :/</p>
<p>All this left me wondering if I could build something that captured the fun
while keeping you in the flow. I even looked at how complicated it would be to
make an isometric game. Too complicated for me was the answer.</p>
<p>Factory games, one of my other favourite genres felt a little too mentally
taxing. Otherwise, I would have dived into
<a href="https://factorio.com/buy-space-age">Space Age</a>.</p>
<p>I have also been bingeing on youtube videos and discovered a fantastic channel -
the <a href="https://www.youtube.com/@TheCodingTrain">Coding Train</a>.</p>
<p>I&rsquo;ve been almost religiously watching the
<a href="https://www.youtube.com/watch?v=17WoOqgXsRM&amp;list=PLRqwX-V7Uu6ZiZxtDDRCi6uhfTH4FilpH">Coding Challenges</a>
playlist and I have been loving it. I love how he makes 2d graphics programming
seems so easy and approachable. I used to be so intimidated by all the math.</p>
<p>There was a slow confidence building in me that maybe - just maybe I could build
a game.</p>
<h2 id="coding-challenge-46">Coding Challenge 46</h2>
<p>Last night, I was watching the one about remaking
<a href="https://www.youtube.com/watch?v=hacZU523FyM">Asteroids</a>. He made it look so
easy and fun. How he puts those asteroids together felt so clever - neat! I
could do that. Actually, I could make that really interesting.</p>
<p>For a little while now, I&rsquo;ve been wanting to try out one of the challenges, tied
in with playing with <a href="https://ziglang.org/">zig</a>, which has also been on my
radar. I&rsquo;ve largely worked with higher level languages like Java (and golang
more recently). I&rsquo;ve always been fascinated by C, and C++, but never quite got
into it. Zig represented a more fun and modern version of C that I could enjoy.
I also really liked how explicit and simple zig feels.</p>
<p>It feels like a perfect opportunity to feed two birds with one scone. I could
try and replicate what he did in that video in zig.</p>
<p>A little bit of research on graphics/game libraries in zig revealed
<a href="https://www.raylib.com/">raylib</a> to be the best candidate for my needs. While a
C library, it has zig bindings, there is plenty of documentation, albeit not
necessarily in zig - but that was ok.</p>
<p>I could see it in my mind - a field of asteroids, <em>pew pew pew</em>, asteroids break
apart. Destroyed asteroids would drop resources. Oh you could have factories on
the ship and build your armoury - weapons, armour, scanners - everything. That
would be an interesting twist - you don&rsquo;t pick up loot - you build it.</p>
<p>Each item would still have randomised mods, but you could extract them like in
Last Epoch and use them in other ones - I have a lot of ideas.</p>
<h2 id="story">Story</h2>
<p>In terms of the story, I&rsquo;ve been pondering the destructive nature of capitalism
a lot lately and that felt like a good metaphor to bring in here - it just
connected.</p>
<p>The planet where the triangle belongs has been destroyed by a corporation - not
necessarily maliciously, but in a bid to extract it of all valuable resource.</p>
<p>I have this image in my mind of something called a &ldquo;Mill&rdquo; that the planet is put
through. The remnants create an asteroid field in which you find yourself.</p>
<p>The game essentially starts off with seeking vengeance. Victory - at least over
the corporation - is not possible. You can get stronger, much stronger, and you
can get to a place where you can beat the waves with relative ease - but the
waves never end!</p>
<p>I have been pondering a softer ending, a choice that the player can make, when
they&rsquo;ve had enough - but I&rsquo;m reluctant to give that away.</p>
<p>On pondering this. I realised this could also be a quiet metaphor for
depression. Having been through it in my life, I could feel a connection. What
if <strong>the triangle is indestructible</strong>?</p>
<p>Actually, that would improve the flow - no game over screen, and no respawn. All
the machinery and installations on the ship could be destroyed, but the core of
the ship - that always remains. You can always keep fighting.</p>
<h2 id="gameplay">Gameplay</h2>
<h3 id="scavenge">Scavenge</h3>
<p>Starting off in an asteroid field, you&rsquo;re able to scavenge materials. The ship
starts off with a smelter, a factory, and some basic power. You can refine
materials and construct buildings to get stronger.</p>
<p>Each sector will get more difficult. I thought about being able to travel
vertically and horizontally, but in the end, I decided to make it a vertical
scroller. A two dimensional map might be more interesting, but it also brings
annoying choices, particularly in terms of wondering whether you should have
gone another way.</p>
<p>By putting the game on some form of rails, you have a smaller context to think
about - and you can focus on fun. I am reminded of
<a href="https://en.wikipedia.org/wiki/Raptor:_Call_of_the_Shadows">Raptor: Call of the Shadows</a>,
a game that I loved in the 90&rsquo;s.</p>
<p>Raptor had shops, but I want to do a super simplified factory aspect. Every
upgrade should feel earned — constructed, not found. I want to feel that
progress is made entirely by the actions of the player.</p>
<p>There will still be randomisation, but it will be limited to the mods that are
spawned on the items that are constructed. The player gets to decide what items
to build.</p>
<p>I want the crafting to be as deterministic as possible, but while also keeping
it interesting.</p>
<h3 id="figuring-it-out">Figuring it out</h3>
<p>There are no (traditional) tutorials or help sections. This decision will put a
lot of pressure on getting the UI/UX intuitive and easy to use. However, it will
also help maintain flow and keep every bit of progress feeling earned.</p>
<p>Items will have descriptions and details so that the user can make choices, but
it is about experimentation and exploration. I would also like a component,
something like a computer that can be installed. The idea is that this can do
analysis of items and mods and give you details of clear and easy to understand
benefits and disadvantages. Not just what attributes change, but how it will
impact gameplay. I can&rsquo;t be perfect, but it must be accurate, and be able to
provide enough information to be able to decide whether it&rsquo;s worth a try.</p>
<p>The intention is that this computer can act a bit like the guide that you would
otherwise google.</p>
<p>Each sector increases difficulty, but is infinite in itself. After traveling for
a bit, you will be offered an exit. If you keep going, exits will be offered at
regular intervals - but you can just keep going.</p>
<p>You are dropped into a universe and every step is up to you to figure it out.</p>
<h2 id="aesthetic">Aesthetic</h2>
<p>I can hear the synthwave tracks that play in the background already (it could be
because I&rsquo;ve been listening to a lot of synthwave recently). It feels right and
thematic to the original Asteroids.</p>
<p>The graphics, at least initially is going to be simple and shape based, again
thematic. I&rsquo;ve been pondering whether and how the graphics would get updated as
you get through the sectors. The start of it is going to be black and white with
little hints of colour. As you progress, you&rsquo;ll get more and more colour and the
vibrancy increases, sometimes in leaps and bounds. It could be a metaphor for
the journey of recovery.</p>
<p>The sound effects should also have a strong 80s vibe. I want it to feel like
you&rsquo;ve been transported to the 80s but if it happened in 2025.</p>
<h2 id="triangle">triangle</h2>
<p>As a name, at least for the time being, I&rsquo;ve settled on <strong>triangle</strong>. It is a
passion project - I have a vision and a strong idea of how I want to feel
playing it.</p>
<p>At its heart, triangle is about (re)building - a game where every piece of
progress feels personal.</p>
<p>I would like to share it with the world and am looking for feedback, thoughts
ideas and supporters.</p>
<p>If the idea or any of the concepts resonate with you, I’d love to hear from you.</p>
<h2 id="other-posts">Other Posts</h2>
<ul>
<li><a href="https://icle.es/2025-05-08-basic-gameplay.md">Next: Basic Gameplay</a></li>
</ul>
<h2 id="updates">Updates</h2>
<p>2025-05-31: Added <a href="https://youtu.be/8pBPQbJtIJk">devlog #0 on youtube</a><br>
2025-05-21: Added an <a href="https://droneah.itch.io/triangle">itch.io page</a></p>]]></content:encoded></item></channel></rss>