<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Ben Leskey's Blog: software</title>
		<description>software posts from Ben Leskey</description>
		<link>https://benleskey.com/blog/tags/software</link>
		<language>en-us</language>
		<copyright>2025 Ben Leskey</copyright>
		<lastBuildDate>Sun, 14 Sep 2025 18:47:01 +0000</lastBuildDate>
		<pubDate>Sun, 14 Sep 2025 18:47:01 +0000</pubDate>
		<atom:link href="https://benleskey.com/blog/tags/software.feed.xml" rel="self" type="application/rss+xml" />

		<item>
	<title>Fixing Intellj IDEA lag spikes on OpenSUSE KDE Plasma 6</title>
	<description>&lt;div&gt;&lt;p&gt;
    I ran into &lt;a href=&quot;https://youtrack.jetbrains.com/issue/JBR-6830&quot;&gt;JBR-6830&lt;/a&gt;. When using Intellij IDEA on KDE Plasma 6 (OpenSUSE Tumbleweed), I was getting random very noticeable lag spikes.
    The solution was to add &lt;/p&gt;&lt;pre&gt;&lt;code&gt;-Dwatch.desktop.geometry=false&lt;/code&gt;&lt;/pre&gt; to the Intellij IDEA custom VM options.

    No more lag spikes!
&lt;/div&gt;</description>
	<category>debugging</category><category>software</category>
	<link>https://benleskey.com/blog/intellj_kde_lag_spike_workaround</link>
	<guid isPermaLink="true">https://benleskey.com/blog/intellj_kde_lag_spike_workaround</guid>
	<pubDate>Thu, 17 Apr 2025 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Fixing HP Victus 15 internal microphone drivers by compiling patched auto-updating kernel with openSUSE Tumbleweed Open Build Service</title>
	<description>&lt;div&gt;&lt;p&gt;
	I&#x27;ve recently moved to a new laptop. OpenSUSE tumbleweed is still my OS of choice, and my recent experience cemented its position as my favorite Linux distro. My new device is an HP Victus 15, specs below:
&lt;/p&gt;

&lt;pre&gt;
Operating System: openSUSE Tumbleweed 20241222
KDE Plasma Version: 6.2.4
KDE Frameworks Version: 6.9.0
Qt Version: 6.8.1
Kernel Version: 6.12.6-5.gfb072de-default (64-bit)
Graphics Platform: X11
Processors: 12 &amp;#215; AMD Ryzen 5 7535HS with Radeon Graphics
Memory: 14.8 GiB of RAM
Graphics Processor: AMD Radeon 660M
Manufacturer: HP
Product Name: Victus by HP Gaming Laptop 15-fb2xxx
&lt;/pre&gt;

&lt;p&gt;
	The only hardware issue encountered on this device was that the internal microphone was not recognized. For example, using &lt;code&gt;arecord -l&lt;/code&gt; to list my sound devices, I only saw the unplugged microphone device, even though my devices were shown in &lt;code&gt;inxi -A&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;
$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 2: Generic_1 [HD-Audio Generic], device 0: ALC245 Analog [ALC245 Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
&lt;/pre&gt;

&lt;pre&gt;
$ inxi -A
Audio:
  Device-1: Advanced Micro Devices [AMD/ATI] Navi 21/23 HDMI/DP Audio
    driver: snd_hda_intel
  Device-2: Advanced Micro Devices [AMD/ATI] Rembrandt Radeon High
    Definition Audio driver: snd_hda_intel
  Device-3: Advanced Micro Devices [AMD] ACP/ACP3X/ACP6x Audio Coprocessor
    driver: snd_pci_acp6x
  Device-4: Advanced Micro Devices [AMD] Family 17h/19h/1ah HD Audio
    driver: snd_hda_intel
  API: ALSA v: k6.12.6-5.gfb072de-default status: kernel-api
  Server-1: PipeWire v: 1.2.7 status: active
&lt;/pre&gt;

After much research I found the fix. I needed to add a linux kernel quirk patch for my specific model in the ACP6X sound driver for the internal microphone. The patch to the kernel itself is extremely simple, just adding my hardware to the quirk list so that even though Linux does not autodetect the correct driver, it will load it anyway:


&lt;pre&gt;
&lt;code language=&quot;diff&quot;&gt;
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index ecf57a6..c95198c 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -458,6 +458,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
 			DMI_MATCH(DMI_PRODUCT_NAME, &quot;OMEN by HP Gaming Laptop 16z-n000&quot;),
 		}
 	},
+	{
+		.driver_data = &amp;amp;acp6x_card,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, &quot;HP&quot;),
+			DMI_MATCH(DMI_PRODUCT_NAME, &quot;Victus by HP Gaming Laptop 15-fb2xxx&quot;),
+		}
+	},
 	{
 		.driver_data = &amp;amp;acp6x_card,
 		.matches = {
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
	So far, so good. But here&#x27;s where OpenSUSE shines: using the Open Build Service, I was able to create a new set of kernel packages with my patch that &lt;i&gt;automatically keep up&lt;/i&gt; with the latest tumbleweed kernel. &lt;a href=&quot;https://www.reddit.com/r/openSUSE/comments/72wz1l/comment/dnm87nj/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button&quot;&gt;This reddit comment&lt;/a&gt; was very helpful, reproduced below:
&lt;/p&gt;

&lt;blockquote&gt;
	&lt;ol&gt;
		&lt;li&gt;Go to https://build.opensuse.org/package/show/openSUSE:Factory/kernel-source&lt;/li&gt;

		&lt;li&gt;sign in&lt;/li&gt;

		&lt;li&gt;click branch&lt;/li&gt;

		&lt;li&gt;optional step - use the command line tool osc to download, edit and test the build locally.&lt;/li&gt;

		&lt;li&gt;overwrite the config.tar.bz2 with your .config tar bzipped&lt;/li&gt;

		&lt;li&gt;Make package links to kernel-source and call them kernel-default, kernel-devel and kernel-syms&lt;/li&gt;

		&lt;li&gt;&lt;strike&gt;Draw the rest of the owl&lt;/strike&gt;&lt;/li&gt;

		&lt;li&gt;Wait for it to build successfully&lt;/li&gt;

		&lt;li&gt;Add the repo to your TW installation (links are on the webpage) and switch the kernel packages over&lt;/li&gt;
    &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;
	I more or less followed these steps, with some details below:
&lt;/p&gt;

&lt;p&gt;
	First, I installed the kernel-source package so I could test and install my fix locally, using the following:
&lt;/p&gt;

&lt;pre&gt;
&lt;code language=&quot;bash&quot;&gt;
$ cd linux-build-directory

$ make -C /usr/src/linux O=$PWD oldconfig # Create a kernel config based on my running kernel config
[...]

# In the .config file, I updated CONFIG_LOCALVERSION to differentiate my compiled kernel,
# and removed the reference to the OpenSUSE keys in CONFIG_MODULE_SIG_KEYS

# I also added a modprobe configuration file:
$ cat /etc/modprobe.d/10-unsupported-modules.conf
allow_unsupported_modules 1

$ make -j12 # It builds.

$ sudo make modules_install -j12 # Adds the kernel modules to /usr/lib/modules

$ sudo make install -j12 # Installs the kernel to /boot and updates the bootloader
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;At this point I created the patch diff of my changes using the original file and my new file:&lt;/p&gt;

&lt;pre&gt;
&lt;code language=&quot;bash&quot;&gt;
$ git diff /tmp/old-acp6x-mach.c ./sound/soc/amd/yc/acp6x-mach.c &amp;gt; /tmp/my-new-patch.patch
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
	After this, I rebooted into my custom kernel to verify the fix. To remove the custom compiled kernel from the bootloader list, I just removed the files and updated the bootloader:

&lt;/p&gt;&lt;pre&gt;
&lt;code language=&quot;bash&quot;&gt;
$ cd /boot
$ mkdir /home/user/old-linux
$ find | grep my-custom-kernel | sudo xargs -O{} -n1 mv {} /home/user/old-linux # Replace my-custom-kernel with what you put in CONFIG_LOCALVERSION
$ sudo update-bootloader # Get the removed custom kernel off the bootloader menu
&lt;/code&gt;
&lt;/pre&gt;


&lt;p&gt;
	I then:
	&lt;/p&gt;&lt;ul&gt;
		&lt;li&gt;
			&quot;Branched&quot; a new package from the openSUSE tumbleweed kernel-source package. This created my own kernel-source package that I could modify as needed. Importantly, it is linked to the upstream openSUSE kernel, so it will stay up to date while applying my patches.
		&lt;/li&gt;
		&lt;li&gt;
			Checked out my new package locally, using osc: &lt;code&gt;$ osc -A https://api.opensuse.org checkout home:linewriter1024:branches:Kernel:stable/kernel-source&lt;/code&gt;
		&lt;/li&gt;
		&lt;li&gt;
			Linked repos as mentioned in the reddit comment + some additional.
&lt;pre&gt;
&lt;code language=&quot;bash&quot;&gt;
$ osc linkpac home:linewriter1024:branches:Kernel:stable/kernel-source home:linewriter1024:branches:Kernel:stable/kernel-default
$ osc linkpac home:linewriter1024:branches:Kernel:stable/kernel-source home:linewriter1024:branches:Kernel:stable/kernel-devel
$ osc linkpac home:linewriter1024:branches:Kernel:stable/kernel-source home:linewriter1024:branches:Kernel:stable/kernel-docs
$ osc linkpac home:linewriter1024:branches:Kernel:stable/kernel-source home:linewriter1024:branches:Kernel:stable/kernel-syms
$ osc linkpac home:linewriter1024:branches:Kernel:stable/kernel-source home:linewriter1024:branches:Kernel:stable/kernel-macros
&lt;/code&gt;
&lt;/pre&gt;
		&lt;/li&gt;
		&lt;li&gt;
			Added my patch to patches.addon.tar.bz2 inside my checked out directory, which has an empty directory &lt;code&gt;patches.addon&lt;/code&gt;, into which directory I added my patch file &lt;code&gt;6.12.6-9999-local.patch&lt;/code&gt; and a text file named &lt;code&gt;series&lt;/code&gt; with one line: &lt;code&gt;6.12.6-9999-local.patch&lt;/code&gt;, the name of the patch file.
		&lt;/li&gt;
		&lt;li&gt;
			Ran &lt;code&gt;osc build&lt;/code&gt; and verified that my patch was being used:
&lt;pre&gt;
&lt;code language=&quot;bash&quot;&gt;
$ cat /var/tmp/build-root/standard-x86_64/.build.log | grep addon
[   10s] + /usr/lib/rpm/rpmuncompress -x /home/abuild/rpmbuild/SOURCES/config.addon.tar.bz2
[   10s] + /usr/lib/rpm/rpmuncompress -x /home/abuild/rpmbuild/SOURCES/patches.addon.tar.bz2
[   22s] + patch -s -F0 -E -p1 --no-backup-if-mismatch -i /home/abuild/rpmbuild/BUILD/kernel-source-6.12.6/patches.addon/6.12.6-9999-local.patch
&lt;/code&gt;
&lt;/pre&gt;
		&lt;/li&gt;
		&lt;li&gt;
			Ran &lt;code&gt;osc commit&lt;/code&gt; and uploaded my changes. The open build service built my new kernel and after it was finished I added the repository to my package repositories and ran &lt;code&gt;sudo zypper dup --from=https://download.opensuse.org/repositories/home:/linewriter1024:/branches:/Kernel:/stable/standard/ --allow-vendor-change&lt;/code&gt; to upgrade my kernel to my new patched version.
		&lt;/li&gt;
		&lt;li&gt;
			Rebooted and profited!
		&lt;/li&gt;
	&lt;/ul&gt;


&lt;p&gt;
	This OpenSUSE infrastructure is incredibly useful, and I am very pleased with my choice of distro and the (relative!) ease of fixing this internal microphone issue without sacrificing update flow. Next step, getting this patch into the upstream Linux kernel!
&lt;/p&gt;
&lt;/div&gt;</description>
	<category>debugging</category><category>software</category>
	<link>https://benleskey.com/blog/opensuse_mic</link>
	<guid isPermaLink="true">https://benleskey.com/blog/opensuse_mic</guid>
	<pubDate>Wed, 25 Dec 2024 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Switching to openSUSE Tumbleweed</title>
	<description>&lt;div&gt;&lt;ol class=&quot;toc&quot;&gt;&lt;/ol&gt;

After over ten years of Ubuntu with the i3 window manager and a host of custom scripts as my desktop OS, I&#x27;ve switched to using &lt;a href=&quot;https://get.opensuse.org/tumbleweed/&quot;&gt;openSUSE Tumbleweed&lt;/a&gt; with KDE Plasma.

&lt;h2 id=&quot;hardware&quot;&gt;The Hardware&lt;/h2&gt;

&lt;p&gt;I took two identical Dell Inspiron 5577 laptops and cobbled them together into something slightly better. I chose one unit as the base for the new machine and the other as a donor unit since it had a random power off issue when using the graphics card. I swapped out the slow-as-sin hard drive in my chosen unit with a new SSD, then took the 8 GB RAM stick from the donor unit and stuck it into the chosen unit for a comfortable 16 GB RAM. I also had to steal the keyboard from the donor unit as the chosen unit&#x27;s keyboard had a lot of dead keys from some damage. After the dust settled, I was left with a nice recycled build, as reported by KDE system information:&lt;/p&gt;

&lt;pre&gt;
Operating System: openSUSE Tumbleweed 20240803
KDE Plasma Version: 6.1.3
KDE Frameworks Version: 6.4.0
Qt Version: 6.7.2
Kernel Version: 6.10.2-1-default (64-bit)
Graphics Platform: X11
Processors: 4 &amp;#215; Intel&amp;#174; Core&amp;#8482; i5-7300HQ CPU @ 2.50GHz
Memory: 15.4 GiB of RAM
Graphics Processor: NVIDIA GeForce GTX 1050/PCIe/SSE2
Manufacturer: Dell Inc.
Product Name: Inspiron 5577
System Version: 1.1.3
&lt;/pre&gt;

&lt;h2 id=&quot;software&quot;&gt;KDE + Software&lt;/h2&gt;

&lt;p&gt;KDE is an excellent desktop environment. Coming from i3, I find it very user-friendly, and entirely as powerful if more mouse-focused. The integration with the wide array of programs is refreshing.&lt;/p&gt;

&lt;p&gt;The best part of openSUSE Tumbleweed is, of course, the software selection. New versions of everything, and a host of package repositories, all with frequent updates.&lt;/p&gt;

&lt;h2 id=&quot;support&quot;&gt;Decent hardware support&lt;/h2&gt;

&lt;p&gt;All the hardware is supported, including the NVIDIA graphics card. The only issue is (a classic Linux problem) with suspend and hibernate - the graphics card sometimes does not power back on. I don&#x27;t need either state, so I just disabled them by masking their targets: &lt;code&gt;sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Setting up NVIDIA&lt;/h3&gt;

&lt;p&gt;The yast software installer automatically prompted me to install the NVIDIA drivers, and after they were installed I made them default with &lt;code&gt;sudo prime-select boot nvidia&lt;/code&gt;. Fairly easy!&lt;/p&gt;

&lt;h2 id=&quot;nvidia&quot;&gt;NVIDIA + Secure Boot&lt;/h2&gt;

&lt;p&gt;
	The biggest problem with the NVIDIA drivers is updating them. With UEFI secure boot enabled, each time the drivers were upgraded I had to enroll their keys at boot time. If I missed the 10 second window (and you only get one chance, even after rebooting), the graphical environment couldn&#x27;t come up and I had to recover manually by running &lt;code&gt;sudo mokutil --import /usr/share/nvidia-pubkeys/whatever-nvidia-pubkey.der&lt;/code&gt; from the recovery environment. You can also disable kernel module verification by running &lt;code&gt;sudo mokutil --disable-validation&lt;/code&gt;. This will ask you to set up a small password and then disable the verification at next boot time (assuming you can remember the small password you set up).
&lt;/p&gt;

&lt;h2 id=&quot;codecs&quot;&gt;Codecs&lt;/h2&gt;

&lt;p&gt;By default, openSUSE does not provide some codecs, e.g. for some internet video formats or for Discord live video streaming. The fix is simple but not prompted at all: &lt;code&gt;sudo zypper install opi &amp;amp;&amp;amp; sudo opi codecs&lt;/code&gt; will add the correct repositories and install the needed codecs.&lt;/p&gt;
&lt;/div&gt;</description>
	<category>software</category>
	<link>https://benleskey.com/blog/opensuse</link>
	<guid isPermaLink="true">https://benleskey.com/blog/opensuse</guid>
	<pubDate>Tue, 06 Aug 2024 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Arcana Technoclysm update: Magical UPS, whistle crafting, and bionic installation</title>
	<description>&lt;div&gt;&lt;p&gt;I&#x27;ve updated my mod &lt;a href=&quot;https://benleskey.com/blog/../aka/cdda-arcana-technoclysm&quot;&gt;Arcana Technoclysm&lt;/a&gt; for &lt;a href=&quot;https://cataclysmdda.org&quot;&gt;Cataclysm: DDA&lt;/a&gt; with some more new features.&lt;/p&gt;

&lt;p&gt;First the changes. The vehicle whistles can now be crafted from each other, so if you find the boat whistle and really wanted the motorcycle whistle you can still craft it. The recipes are written in the Cleansing Flame book and the arcana lab journal. Also, there are some more Technoclysm item spawns in the strange warehouse, so poke around! Technoclysm items can also spawn rarely with other Arcana items in labs or magic locations.&lt;/p&gt;

&lt;p&gt;The first new feature is an eternal UPS (power supply) which uses magical energy to constantly provide power to UPS-enabled tools. It has a recharge rate of about 2 kJ per minute and is fairly small, and can be either crafted or found.&lt;/p&gt;

&lt;p&gt;The second, and more important, new feature is a magical means of installing bionics. &lt;i&gt;Bionic talismans&lt;/i&gt; can be crafted from any power storage or power generation CBM. Using the bionic talisman will temporarily summon a magical avatar of the bionic, who will install any bionic for you before disappearing. This provides a means of installing bionics outside of the Exodii using another otherworldly force who may be more or less friendly.&lt;/p&gt;
&lt;/div&gt;</description>
	<category>software</category><category>games</category><category>cdda</category>
	<link>https://benleskey.com/blog/cdda_arcana_technoclysm_2</link>
	<guid isPermaLink="true">https://benleskey.com/blog/cdda_arcana_technoclysm_2</guid>
	<pubDate>Fri, 17 May 2024 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Arcana Technoclysm: A small mod for Cataclysm: DDA</title>
	<description>&lt;div&gt;&lt;p&gt;&lt;a href=&quot;https://cataclysmdda.org&quot;&gt;Cataclysm: DDA&lt;/a&gt;&#x27;s premier magic mod is &lt;a href=&quot;http://www.cddawiki.chezzo.com/cdda_wiki/index.php?title=Magiclysm&quot;&gt;&lt;/a&gt;Magiclysm, which not only adds an entire magic system and slew of useful spells, items, and locations but also alters the lore of the game to imply that magic has existed throughout history. This lore breaks significantly from real world history, so I prefer mods that keep magic as something esoteric and hidden, such as the newly added mod &lt;i&gt;Mind Over Matter&lt;/i&gt; or the mature &lt;a href=&quot;https://github.com/chaosvolt/cdda-arcana-mod&quot;&gt;Arcana&lt;/a&gt; mod.&lt;/p&gt;

&lt;p&gt;However, there is one particular spell from Magiclysm that I love that is not available in any other mod: the &lt;i&gt;Summon Motorcycle&lt;/i&gt; spell. This is amazingly useful and fun to play with, so I decided to port the idea over to Magiclysm in my own mod. This new mod, &lt;a href=&quot;https://benleskey.com/blog/../aka/cdda-arcana-technoclysm&quot;&gt;Arcana Technoclysm&lt;/a&gt;, currently adds three temporary summonable vehicles and a new location to find the magic whistles needed to summon these vehicles. It&#x27;s a simple mod but fills in the summonable vehicle gap in the more lore-friendly magic mods. The vehicles currently available are an amphibious boat with wheels, a motorcycle, and a horseless carriage with plenty of cargo space. Each of the vehicles is a tradeoff between maneuverability, compatible terrain, and cargo space, so hopefully players will have incentive to find strange warehouses and collect all the magic whistles.&lt;/p&gt;

&lt;img src=&quot;https://benleskey.com/blog/../images/technoclysm_items.png&quot; alt=&quot;A screenshot of the three vehicle summoning whistles from the Arcana Technoclysm mod&quot;&gt;
&lt;img src=&quot;https://benleskey.com/blog/../images/technoclysm_vehicles.png&quot; alt=&quot;A screenshot of the three summonable vehicles from the Arcana Technoclysm mod&quot;&gt;
&lt;img src=&quot;https://benleskey.com/blog/../images/technoclysm_warehouse.png&quot; alt=&quot;A screenshot of the new location where you can find vehicle summoning whistles&quot;&gt;
&lt;/div&gt;</description>
	<category>software</category><category>games</category><category>cdda</category>
	<link>https://benleskey.com/blog/cdda_arcana_technoclysm</link>
	<guid isPermaLink="true">https://benleskey.com/blog/cdda_arcana_technoclysm</guid>
	<pubDate>Fri, 20 Oct 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>World map for a Wesnoth campaign: The Paradise Trap</title>
	<description>&lt;div&gt;&lt;img src=&quot;https://raw.githubusercontent.com/wesnoth/wesnoth/51b58ad218fc285717c7d5889fdfeda59f017ca6/data/core/images/maps/wesnoth.webp&quot; style=&quot;max-width: 160px&quot; class=&quot;right&quot; alt=&quot;the wesnoth world map&quot;&gt;

&lt;p&gt;
	I&#x27;m working on a &lt;a href=&quot;https://wesnoth.org&quot;&gt;Battle for Wesnoth&lt;/a&gt; campaign, &lt;a href=&quot;https://benleskey.com/blog/../aka/wexlfu-tpt&quot;&gt;The Paradise Trap&lt;/a&gt;, set in the western sea off the coast of the Great Continent in the world of Wesnoth. The world of Wesnoth has a canonical map, but working with a nice-looking map is beyond the scope of my project for now. Fortunately for me, Wesnoth&#x27;s game map editor creates nice enough maps and has an automatable screenshot export function built in.
&lt;/p&gt;

&lt;img src=&quot;https://benleskey.com/blog/../images/tpt_world_map_raw_0.3.png&quot; alt=&quot;the paradise trap world map before labels are added&quot;&gt;

&lt;p&gt;
	I built an approximation of the world map in the in-game map editor. This maps out the terrain of both the canonical continent and my own world building&amp;#8212;Paradise. The scale is certainly off, but it doesn&#x27;t matter; I&#x27;m not concerned about accuracy for this map which will only be used at the start of scenarios to roughly plot out the movement of the characters.
&lt;/p&gt;

&lt;img src=&quot;https://benleskey.com/blog/../images/tpt_world_map_0.3.png&quot; alt=&quot;the paradise trap world map&quot;&gt;

&lt;p&gt;
	Finally, I use ImageMagick to add labels to the map. The labels are drawn using a built-in Wesnoth font, so the style at least somewhat matches the game. This final image is then used at the beginning of each scenario, and can be used for spatial reference as I write the story.
&lt;/p&gt;

&lt;p&gt;
	This is all automated through a script that exports the wesnoth map of the world to an image and then uses a long &lt;code&gt;convert&lt;/code&gt; invocation to add all the labels. The script is included below for reference.
&lt;/p&gt;

&lt;h2&gt;Map generation script&lt;/h2&gt;
&lt;pre&gt;
&lt;code language=&quot;bash&quot;&gt;
#!/bin/bash
if ! [[ ./images/worldmap.png -ot ./worldmap/worldmap.cfg ]] &amp;amp;&amp;amp; ! [[ ./images/worldmap.png -ot ./worldmap/build.sh ]] ; then
	echo &quot;Worldmap is up to date!&quot;
	if [[ -n &quot;$wx&quot; ]]; then
		return 0
	else
		exit 0
	fi
fi

wesnoth --screenshot ./worldmap/worldmap.cfg ./images/worldmap.png &amp;gt; /dev/null

commandline=&quot;&quot;
font=&quot;$(wesnoth --data-path)/fonts/OldaniaADFStd-Regular.otf&quot;

label() {
	x=$1
	y=$2
	text=&quot;$3&quot;
	scale=${4:-1}
	scalei=&quot;$(echo &quot;$scale * 48&quot; | bc | cut -d. -f1)&quot;
	len=&quot;$(echo &quot;$text&quot; | wc -c)&quot;

	commandline=&quot;$commandline -pointsize \&quot;$scalei\&quot; -font \&quot;$font\&quot; -fill white -gravity NorthWest -draw \&quot;text $((x - (scalei * len) / 6)),$((y - $scalei)) \\\&quot;$text\\\&quot;\&quot;&quot;
}

label 3510 1040 &quot;Elensefar&quot; 1.5
label 3732 1338 &quot;Halstead&quot;
label 3680 1616 &quot;Aldril&quot;
label 4000 1190 &quot;Carcyn&quot;
label 3303 2304 &quot;Blackwater Port&quot;
label 3024 2037 &quot;Isle of Alduin&quot;
label 2445 1134 &quot;The Three Sisters&quot;
label 2390 1575 &quot;The Red Isle&quot;

label 1606 1317 &quot;Ialwas&quot;

label 3357 441 &quot;Glamdrol&quot;
label 3837 282 &quot;Rumyr&quot;

label 5031 1866 &quot;Dan Tonk&quot; 1.5
label 5523 2349 &quot;Weldyn&quot; 1.5
label 5349 1722 &quot;Tath&quot;
label 4875 2844 &quot;Fort Tahn&quot;

label 4815 1362 &quot;Gryphon Mountain&quot;
label 5640 1086 &quot;Swamp of Dread&quot; 2
label 4380 213 &quot;Lake Vrug&quot; 2
label 3395 1890 &quot;Bay of Pearls&quot;

label 3960 760 &quot;Wesmere Forest&quot; 2
label 3774 2670 &quot;Aethenwood&quot;

label 423 1719 &quot;Paradise&quot; 3
label 4344 1911 &quot;Wesnoth&quot; 3

eval &quot;convert $commandline ./images/worldmap.png ./images/worldmap.png&quot;
&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;</description>
	<category>software</category><category>games</category><category>wesnoth</category>
	<link>https://benleskey.com/blog/tpt_world_map</link>
	<guid isPermaLink="true">https://benleskey.com/blog/tpt_world_map</guid>
	<pubDate>Thu, 28 Sep 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Make horses milkable</title>
	<description>&lt;span&gt;See &lt;a href=&quot;https://github.com/CleverRaven/Cataclysm-DDA/pull/66639&quot;&gt;https://github.com/CleverRaven/Cataclysm-DDA/pull/66639&lt;/a&gt;
&lt;/span&gt;</description>
	<category>cdda</category><category>software</category>
	<link>https://github.com/CleverRaven/Cataclysm-DDA/pull/66639</link>
	<guid isPermaLink="true">https://benleskey.com/blog/make_horses_milkable</guid>
	<pubDate>Mon, 03 Jul 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>The Dillo Test</title>
	<description>&lt;div&gt;&lt;i&gt;(Post updated on 2025-01-05 to correct the link to Dillo.)&lt;/i&gt;

&lt;p&gt;You may not know what Dillo is.&lt;/p&gt;
&lt;img src=&quot;https://benleskey.com/blog/../images/dillo_test.png&quot; alt=&quot;benleskey.com in dillo&quot;&gt;

&lt;p&gt;That&#x27;s &lt;a href=&quot;https://dillo-browser.github.io&quot;&gt;Dillo&lt;/a&gt;. It&#x27;s a plain graphical web browser with minimal feature set beyond being able to draw text and, sometimes, images. Why would anyone want that? Well, there&#x27;s not much practical reason now&amp;#8212;every modern browser supports a truckload of features beyond simple HTML&amp;#8212;but it does indicate a simplicity and graceful degradation that is aesthetically desirable. This website, for instance, is entirely usable in Dillo. Javascript doesn&#x27;t work, but everything has a fallback. If your site works in Dillo, then it is &lt;i&gt;simple enough&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;It&#x27;s not even difficult to make this site work in Dillo. In fact, it avoids any kind of bloat. This site can&#x27;t rely on Javascript, or on complicated styling, or on anything but simple text to get its message across. Functionality beyond that is, as is traditional, just window dressing on top of a simple HTML structure. Common? No. But if your site works in Dillo it will probably satisfy the hacker hipsters.&lt;/p&gt;

&lt;p&gt;You can, of course, take it further and make the &lt;a href=&quot;https://lynx.invisible-island.net/&quot;&gt;Lynx&lt;/a&gt; test &lt;b&gt;;)&lt;/b&gt;. You&#x27;d better hope you&#x27;ve got alt text on your &lt;i&gt;img&lt;/i&gt; elements!&lt;/p&gt;

&lt;img src=&quot;https://benleskey.com/blog/../images/dillo_test_lynx.png&quot; alt=&quot;benleskey.com in lynx&quot;&gt;
&lt;/div&gt;</description>
	<category>dev</category><category>software</category><category>website</category>
	<link>https://benleskey.com/blog/dillo_test</link>
	<guid isPermaLink="true">https://benleskey.com/blog/dillo_test</guid>
	<pubDate>Sun, 05 Jan 2025 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Mapping tool 0.6.2 release &amp; thanks to all testers</title>
	<description>&lt;div&gt;&lt;p&gt;Hello everyone!&lt;/p&gt;
&lt;p&gt;Thank you so much for supporting the development of the mapping tool with your feedback, bug reports, and suggestions!&lt;/p&gt;
&lt;p&gt;Version 0.6.2 of the mapping tool is out, with several important improvements including unlimited scale and image exporting. This is the last version of the mapping tool to be developed as an MVNU project, as I have graduated! In the future, the mapping tool will be developed as an open source project.&lt;/p&gt;
&lt;p&gt;You can download it here: &lt;a href=&quot;https://github.com/mapper1024/mapper1024/blob/master/DOWNLOAD.md#download-options&quot;&gt;https://github.com/mapper1024/mapper1024/blob/master/DOWNLOAD.md#download-options&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can use the online demo here: &lt;a href=&quot;https://mapper1024.github.io/demo&quot;&gt;https://mapper1024.github.io/demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mapper1024.github.io/screenshots/sample_map_0.6.2.png&quot;&gt;&lt;img src=&quot;https://mapper1024.github.io/screenshots/sample_map_0.6.2.thumb.png&quot; alt=&quot;A screenshot of the sample map in the mapping tool&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;image-exporting&quot;&gt;Image Exporting&lt;/h2&gt;
&lt;p&gt;You can now save parts of the map as images to be used as illustrations for a book, for a website, or for whatever you want.&lt;/p&gt;
&lt;h2 id=&quot;unlimited-scale&quot;&gt;Unlimited Scale&lt;/h2&gt;
&lt;p&gt;The scale of your maps is now unlimited, with a few handy controls to set exactly what scale you want to edit your map at at any given moment: you can edit on the scale of meters to design a city, or on the scale of hundreds of kilometers to design a continent. Whatever you like, all in the same map.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What&amp;#8217;s Next&lt;/h2&gt;
&lt;p&gt;Just because I graduated doesn&amp;#8217;t mean that the mapping tool is done. I&amp;#8217;m going to keep working on it! Features like altitude controls, better performance, alternate graphical styles, and more are all in the pipeline. I&amp;#8217;ll also be releasing the tool as an open source project so that anyone interested can contribute ideas, code, and art.&lt;/p&gt;
&lt;h2 id=&quot;stay-in-touch&quot;&gt;Stay in Touch&lt;/h2&gt;
&lt;p&gt;Feel free to e-mail me at &lt;a href=&quot;mailto:ben@benleskey.com&quot;&gt;ben@benleskey.com&lt;/a&gt; for anything! You can also post feature requests or bug reports using the &lt;a href=&quot;https://github.com/mapper1024/mapper1024/issues&quot;&gt;mapping tool issue tracker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;d like to keep up with development of the mapping tool and try out new releases, all mapping tool news is posted &lt;a href=&quot;https://benleskey.com/aka/mappingtoolnews&quot;&gt;on my website&lt;/a&gt;. An &lt;a href=&quot;https://benleskey.com/blog/tags/mappingtool.feed.xml&quot;&gt;rss feed for news&lt;/a&gt; is available, as well as an &lt;a href=&quot;https://github.com/mapper1024/mapper1024/releases.atom&quot;&gt;atom feed for releases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you again for your support, and I hope you continue to enjoy the mapping tool as it grows and evolves!&lt;/p&gt;
&lt;/div&gt;</description>
	<category>mappingtool</category><category>release</category><category>software</category>
	<link>https://benleskey.com/blog/mapper0_6_2</link>
	<guid isPermaLink="true">https://benleskey.com/blog/mapper0_6_2</guid>
	<pubDate>Mon, 08 May 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Prototype post-apocalyptic roadtrip simulator for the Minetest engine</title>
	<description>&lt;div&gt;&lt;ol class=&quot;toc&quot;&gt;&lt;/ol&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Github repository: &lt;a href=&quot;https://github.com/tigris-mt/roadtrip&quot;&gt;https://github.com/tigris-mt/roadtrip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	I&#x27;ve begun work on a game project, smaller in scope and more relaxing than the &lt;a href=&quot;https://benleskey.com/blog/textengine_intro&quot;&gt;text engine project&lt;/a&gt;. This new project uses the &lt;a href=&quot;https://minetest.net&quot;&gt;Minetest&lt;/a&gt; engine for voxel graphics, entities, and physics. This game, Roadtrip, is designed along the lines of &lt;a href=&quot;https://store.steampowered.com/app/1017180/The_Long_Drive/&quot;&gt;The Long Drive&lt;/a&gt; as a post-apocalyptic roadtrip simulator and also takes inspiration from &lt;a href=&quot;https://cataclysmdda.org&quot;&gt;Cataclysm: Dark Days Ahead&lt;/a&gt;. It&#x27;s written in Lua.
&lt;/p&gt;
&lt;a href=&quot;https://benleskey.com/blog/../images/roadtrip.png&quot;&gt;&lt;img class=&quot;left&quot; src=&quot;https://benleskey.com/blog/../thumbs/roadtrip.25.png&quot; alt=&quot;roadtrip game prototype&quot;&gt;&lt;/a&gt;

&lt;h2 id=&quot;minetest&quot;&gt;&lt;a href=&quot;https://minetest.net&quot;&gt;Minetest&lt;/a&gt; &amp;amp; Voxels&lt;/h2&gt;

&lt;p&gt;The Minetest engine provides a solid base for this type of game: procedural generation, low-poly graphics, built-in multiplayer, etc. I have quite a bit of experience with Minetest and Lua, so it&#x27;s a good choice for game engine since I don&#x27;t want to put too much development effort into this game. The world will consist of &quot;voxels&quot;, blocks like in Minecraft that form the terrain, along with moveable &quot;entities&quot; such as the vehicles and the player.&lt;/p&gt;

&lt;h2 id=&quot;design&quot;&gt;Design&lt;/h2&gt;

&lt;p&gt;The basic design of the game is the player driving down a road in a procedurally generated environment. The player must survive (food, water, gasoline/electricity, shelter, etc.) as long as possible while enjoying the view and trip.&lt;/p&gt;

&lt;p&gt;Like The Long Drive, Roadtrip will have a long highway extending indefinitely into the distance with a randomly generated environment surrounding it, maybe including side roads or broader road networks. The prototype environment will be a desert, but it will be easy to add new biomes.&lt;/p&gt;

&lt;p&gt;In the prototype, the road runs from the south toward the north, with turns and corners defined by an equation that returns the X (east-west) coordinate of the road based on the Z (north-south) coordinate. The current equation is defined as &lt;/p&gt;&lt;pre&gt;&lt;code language=&quot;lua&quot;&gt;local z_factor = 60 + 60 * seed_random()
local z_factor_2 = 160 + 160 * seed_random()
local x_factor = 60 + 60 * seed_random()

local function road_x(z)
	return math.floor(math.sin(z / z_factor) * (math.sin(z / z_factor_2) ^ 4) * x_factor + 0.5)
end&lt;/code&gt;&lt;/pre&gt; where &lt;em&gt;seed_random()&lt;/em&gt; is a random generator returning a random number between 0 and 1 according to the world seed. All other properties of the world, like the width of the road at any point, will be defined by similar procedural generation to ensure continuity as each chunk of the map is generated separately.

&lt;h3 id=&quot;vehicles&quot;&gt;Vehicles&lt;/h3&gt;

&lt;p&gt;Vehicles are the focus of Roadtrip, so they will be very customizable. Vehicles will be individual components internally, such as the chassis, the wheels, the doors, etc., which can then be installed together and the models attached to display an entire car, truck, or other vehicle.&lt;/p&gt;

&lt;h3 id=&quot;survival&quot;&gt;Survival&lt;/h3&gt;

&lt;p&gt;Survival elements including hunger, thirst, weather, and fuel will drive further exploration and resource gathering. There may be enemies as well, or some other active threat.&lt;/p&gt;

&lt;h3 id=&quot;exploration&quot;&gt;Exploration&lt;/h3&gt;

&lt;p&gt;Resources will be available in structures off the road, in abandoned cars, or from natural resources like lakes or trees. This will encourage exploration off road, both with and without a vehicle. Terrain generation can be improved to have hills, valleys, caves, etc. to spice up the landscape.&lt;/p&gt;

&lt;h2 id=&quot;challenges&quot;&gt;Development Challenges&lt;/h2&gt;

&lt;p&gt;The Minetest engine is limited in a number of ways that will make development more challenging. I do think my familiarity with the engine along the engine&#x27;s simplicity will make it worth it, however.&lt;/p&gt;

&lt;h3 id=&quot;rotation&quot;&gt;Rotation&lt;/h3&gt;

&lt;p&gt;Most noticably, if the player is attached to an object and the object rotates, the player&#x27;s viewport does not rotate with the object. My workaround, rotating both at the same time, suffers from network lag causing noticable drift that the player must manually correct. I may need to contribute a feature to the engine to support this for more realistic driving.&lt;/p&gt;

&lt;h3 id=&quot;collision&quot;&gt;Collision&lt;/h3&gt;

&lt;p&gt;For performance reasons, an object&#x27;s collisionbox cannot extend much beyond ~3 nodes in any direction before it starts to break. This means that entities like vehicles can&#x27;t be too big or their physics won&#x27;t work. Again, I may need to improve the engine here, or just live with innacurate collision on large vehicles.&lt;/p&gt;

&lt;h3 id=&quot;hud&quot;&gt;Hud &amp;amp; Controls&lt;/h3&gt;

&lt;p&gt;Implementing the HUD and controls is a bit of a challenge as the Minetest engine is geared toward a Minecraft-like game. Nevertheless, I should be able to represent the dashboard on the HUD nearly as easily as the player&#x27;s own stats. Controls are also a little harder, with a limited number of keybinds available to the game. I can use movement keys for driving, and I may be able to use hotbar items for actions like shifting gears or quick access of weapons or tools.&lt;/p&gt;
&lt;/div&gt;</description>
	<category>roadtrip</category><category>minetest</category><category>games</category><category>software</category>
	<link>https://benleskey.com/blog/roadtrip_intro</link>
	<guid isPermaLink="true">https://benleskey.com/blog/roadtrip_intro</guid>
	<pubDate>Sun, 23 Apr 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>First place prize for my mapping tool presentation at MVNU&#x27;s sURC</title>
	<description>&lt;div&gt;&lt;a href=&quot;https://benleskey.com/blog/../images/mapper_gifs_gif.gif&quot;&gt;&lt;img class=&quot;right&quot; src=&quot;https://benleskey.com/blog/../images/mapper_gifs_gif_small.gif&quot; alt=&quot;A map being drawn in the mapping tool&quot;&gt;&lt;/a&gt;

&lt;p&gt;Update 2023-04-25: &lt;a href=&quot;https://www.mvnu.edu/news/mvnu-students-shine-during-symposium-for-undergraduate-research-and-creative-works&quot;&gt;MVNU sURC press release featuring my mapping tool&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I presented my &lt;a href=&quot;https://benleskey.com/blog/../aka/mappingtool&quot;&gt;Mapping Tool&lt;/a&gt; for fifteen minutes at &lt;a href=&quot;https://www.mvnu.edu/undergraduate/academics/honorsprogram/surc&quot;&gt;MVNU&#x27;s &lt;abbr title=&quot;Symposium for Undergraduate Research and Creative Work&quot;&gt;sURC&lt;/abbr&gt;&lt;/a&gt; on April 5th, taking home the first place prize of the Symposium. My friends and faculty showed up en masse at my presentation, making for a lively Q&amp;amp;A and a really fun time.&lt;/p&gt;

&lt;p&gt;sURC itself was interesting. There were around twenty presenters in total. The Honors students presented their senior projects and other students presented their summer research projects in a conference format. In my room we had a wide variety of presentations: an analysis of continuous glucose monitors in diabetic patients, a presentation of a concussion-detecting device for athletes, an examination of religion and spirituality in occupational therapy, and my own software project.&lt;/p&gt;

&lt;p&gt;This sURC presentation was my presentation for the Honors program portion of my project, so in it I focused on how user feedback shaped the development of the mapping tool with an overview of project features and the development process. Later I&#x27;ll have a twenty-five minute presentation on the details of programming and engineering the project.&lt;/p&gt;

&lt;p&gt;For more information on the mapping tool, including download links and a quick-start guide, see &lt;a href=&quot;https://benleskey.com/blog/../aka/mappingtool&quot;&gt;the mapping tool website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(&lt;a href=&quot;https://benleskey.com/blog/mapper_gifs&quot;&gt;More about how I made the GIFs&lt;/a&gt; used in the presentation.)&lt;/p&gt;
&lt;/div&gt;</description>
	<category>academics</category><category>mappingtool</category><category>software</category>
	<link>https://benleskey.com/blog/mvnu_surc</link>
	<guid isPermaLink="true">https://benleskey.com/blog/mvnu_surc</guid>
	<pubDate>Tue, 25 Apr 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Toward the ultimate text adventure game engine</title>
	<description>&lt;div&gt;&lt;ol class=&quot;toc&quot;&gt;&lt;/ol&gt;

&lt;ul&gt;&lt;li&gt;Github repository: &lt;a href=&quot;https://github.com/linewriter1024/textengine&quot;&gt;https://github.com/linewriter1024/textengine&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h2 id=&quot;text-adventures&quot;&gt;Text adventure&lt;/h2&gt;

&lt;h3 id=&quot;why-text&quot;&gt;Why text?&lt;/h3&gt;

&lt;p&gt;
	Text-based games have an extremely good &lt;em&gt;complexity/development time&lt;/em&gt; ratio. There are no graphics to worry about, no programmer art beyond writing (what luck, I happen to enjoy writing), no controls beyond text processing, no frames per second, et cetera, et cetera. Fully text based games can be even more complex than turn-based 2D roguelike games like &lt;a href=&quot;https://cataclysmdda.org/&quot;&gt;Cataclysm: DDA&lt;/a&gt;. There&#x27;s just less extra to worry about&amp;#8212;the development focus has one of the most basic interfaces imaginable. Additionally, fully text-based games are perfect for screen readers due to their simple interface (see &lt;a href=&quot;https://writing-games.com/building-a-better-mud/&quot;&gt;this article&lt;/a&gt; about writing a screen-reader friendly text-based game).
&lt;/p&gt;

&lt;p&gt;
	The specific style of text-based game I&#x27;m talking about is the text adventure game. The computer tells you about your environment, your circumstances, and your character, and you choose what your character does or says. It&#x27;s simple, straightforward, and very reminiscent of the conversational style of a tabletop RPG.
&lt;/p&gt;

&lt;p&gt;The problem is, a human game master will always be better than a computer here, right? A human game master can simulate any situation, can mediate any conflict, can act any character. (I exaggerate, but not very much.) A computer must be programmed to handle every specific situation in the game&#x27;s world.&lt;/p&gt;

&lt;h3 id=&quot;ai&quot;&gt;AI?&lt;/h3&gt;
&lt;p&gt;
	You can make &lt;a href=&quot;https://chat.openai.com/chat&quot;&gt;ChatGPT&lt;/a&gt; play a roleplaying game with you. ChatGPT can be the game master, simulate the environment, accept the actions of your character, whatever you might want. A couple of pitfalls prevent full immersion, however. ChatGPT forgets details and must be reminded, and it can&#x27;t stay on track with a game system, being a more freeform storyteller. I want something better.
&lt;/p&gt;

&lt;h3 id=&quot;muds&quot;&gt;MUDs?&lt;/h3&gt;

&lt;p&gt;
	The system I have in mind is similar to the &lt;a href=&quot;https://en.wikipedia.org/w/index.php?title=MUD&amp;amp;oldid=1140275602&quot;&gt;MUD&lt;/a&gt; &lt;a href=&quot;https://medium.com/@williamson.f93/multi-user-dungeons-muds-what-are-they-and-how-to-play-af3ec0f29f4a&quot;&gt;gameplay&lt;/a&gt; &lt;a href=&quot;https://writing-games.com/what-is-a-mud-multi-user-dungeon/&quot;&gt;style&lt;/a&gt;, which is fully text-based as you pilot your character through a multiplayer world. MUDs are, however, limited in a number of ways: they are generally room based, their multiplayer nature requires real-time interaction, their systems are quite gamey, and the &quot;dungeon master&quot; is primarily concerned with combat mechanics.
&lt;/p&gt;

&lt;h3 id=&quot;something-new&quot;&gt;Something new&lt;/h3&gt;

&lt;p&gt;I&#x27;ve started work on a new system at &lt;a href=&quot;https://github.com/linewriter1024/textengine&quot;&gt;https://github.com/linewriter1024/textengine&lt;/a&gt;. This &lt;em&gt;text engine&lt;/em&gt; is an effort toward building a flexible text-based game engine for simulating any scenario or world.&lt;/p&gt;

&lt;h2 id=&quot;goals&quot;&gt;Goals&lt;/h2&gt;

&lt;ul&gt;
	&lt;li&gt;To simulate combat and action mechanics&lt;/li&gt;
	&lt;li&gt;To handle social interactions&lt;/li&gt;
	&lt;li&gt;To keep track of world events: political affiliations, kingdom economics, famines, shipping routes&lt;/li&gt;
	&lt;li&gt;To support singleplayer play or play with a small party&lt;/li&gt;
	&lt;li&gt;To support any genre of game: high fantasy action, low fantasy economic, sci-fi space battle, zombie apocalypse, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are very ambitious goals, but the text engine format lends itself well to managing all this complexity without the added requirements of graphical display and control.&lt;/p&gt;

&lt;p&gt;This will be a long-term project, so development may be quite slow. There is no time pressure, however, so I can take it at my pace.&lt;/p&gt;

&lt;h3 id=&quot;go&quot;&gt;Learning Go&lt;/h3&gt;

&lt;p&gt;This project is also a way for me to learn the Go language, which has some unique syntax and ideas that I think will be perfect for a large text-based game engine like this.&lt;/p&gt;

&lt;h2 id=&quot;design&quot;&gt;Design&lt;/h2&gt;

&lt;p&gt;The basic structure is thus: everything in the world, whether a city, a man, a kingdom, a sword, or a lake, is an entity in relationship with other entities, capable of whatever actions and interactions make sense for it.&lt;/p&gt;

&lt;p&gt;Let&#x27;s example a sample scenario to illustrate the idea: a covered &lt;b&gt;wagon&lt;/b&gt; of four &lt;b&gt;merchants&lt;/b&gt; traveling down a &lt;b&gt;road&lt;/b&gt; through a &lt;b&gt;forest&lt;/b&gt;, about to be robbed by two &lt;b&gt;bandits&lt;/b&gt; with &lt;b&gt;crossbows&lt;/b&gt;. You are the lone &lt;b&gt;guard&lt;/b&gt; for the &lt;b&gt;wagon&lt;/b&gt;, wielding only a &lt;b&gt;sword&lt;/b&gt; riding in the &lt;b&gt;front&lt;/b&gt; with the &lt;b&gt;driver&lt;/b&gt; while the other three &lt;b&gt;merchants&lt;/b&gt; are sitting in the &lt;b&gt;back&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;Every bold word in that previous paragrah would be an entity (or a few entities). As an example, the wagon is an entity with two entities making it up: the front and the back. In the event that you wanted to hide underneath the wagon, an entity for the underneath would be generated as well. The front of the wagon is an entity that &lt;em&gt;contains&lt;/em&gt; you and the driver. This &lt;em&gt;contains&lt;/em&gt; relationship would be mirrored by &lt;em&gt;in&lt;/em&gt; relationships that you and the driver have with the wagon. The wagon itself is &lt;em&gt;on&lt;/em&gt; the road, while the road &lt;em&gt;contains&lt;/em&gt; the wagon. The road itself is within the forest, and so forth.&lt;/p&gt;

&lt;p&gt;You can see the complex interactions that can happen here. Each of the merchants and bandits has an AI corresponding with their generated personalities and skills, so some of the merchants might draw weapons while others might seek cover in the wagon from the bandits&#x27; crowssbows.&lt;/p&gt;

&lt;p&gt;Dialogue would be represented by ideas, such as &quot;The king expresses his gratitude with an undercurrent of disdain.&quot; or &quot;The bandit threatens you with no trace of reason in his eyes.&quot; You can choose to respond however you see fit, by threatening, cajoling, warmly thanking, etc. to influence character&#x27;s perceptions, attitudes, and ultimately actions.&lt;/p&gt;

&lt;p&gt;Attitudes and actions would bubble upward as well to influence cities and kingdoms. If the player was not currently in a city it could be simulated on a macro level rather than each individual character in the city, so that the city&#x27;s influence and politics would change and existing characters in the city would have their life stories altered without the need for a granular simulation.&lt;/p&gt;

&lt;p&gt;I still need to work through the details of the system, and see what implementation issues I run into, but this should provide a base for further development in a new and exciting direction.&lt;/p&gt;

&lt;h2 id=&quot;github&quot;&gt;Github repository&lt;/h2&gt;
&lt;p&gt;I&#x27;ve started work at &lt;a href=&quot;https://github.com/linewriter1024/textengine&quot;&gt;https://github.com/linewriter1024/textengine&lt;/a&gt;. The prototype is not yet a functioning game, but there is a command processing system and the basis of a generic game engine capable of singleplayer and multiplayer text-based gameplay.&lt;/p&gt;
&lt;/div&gt;</description>
	<category>software</category><category>games</category><category>textengine</category><category>discovery</category>
	<link>https://benleskey.com/blog/textengine_intro</link>
	<guid isPermaLink="true">https://benleskey.com/blog/textengine_intro</guid>
	<pubDate>Fri, 21 Apr 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>Mapping tool quick start guide</title>
	<description>&lt;span&gt;See &lt;a href=&quot;https://github.com/mapper1024/mapper1024/wiki/Quick-Start&quot;&gt;https://github.com/mapper1024/mapper1024/wiki/Quick-Start&lt;/a&gt;
&lt;/span&gt;</description>
	<category>mappingtool</category><category>software</category>
	<link>https://github.com/mapper1024/mapper1024/wiki/Quick-Start</link>
	<guid isPermaLink="true">https://benleskey.com/blog/mapper_quick_start</guid>
	<pubDate>Thu, 20 Apr 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>OBS Studio -&gt; ffmpeg -&gt; ImageMagick for an optimized gif</title>
	<description>&lt;div&gt;&lt;p class=&quot;update&quot;&gt;Update: The gifs worked! I &lt;a href=&quot;https://benleskey.com/blog/mvnu_surc&quot;&gt;successfully presented&lt;/a&gt; at sURC, and won the top prize.&lt;/p&gt;

&lt;p&gt;I&#x27;ve been working on my presentation to present my &lt;a href=&quot;https://benleskey.com/blog/../aka/mappingtool&quot;&gt;mapping tool&lt;/a&gt; senior Honors project at &lt;a href=&quot;https://www.mvnu.edu/undergraduate/academics/honorsprogram/surc&quot;&gt;MVNU&#x27;s &lt;abbr title=&quot;Symposium for Undergraduate Research and Creative Work&quot;&gt;sURC&lt;/abbr&gt;&lt;/a&gt;. For part of this presentation I wanted an animated gif illustrating the basic drawing functionality of the tool.&lt;/p&gt;

&lt;p&gt;I started with &lt;a href=&quot;https://obsproject.com/&quot;&gt;OBS studio&lt;/a&gt; to record the actual video of me drawing a simple map. Then I imported the video into &lt;a href=&quot;https://kdenlive.org/&quot;&gt;Kdenlive&lt;/a&gt; for editing. OBS recorded the video as 1920x1080, but the actual mapping tool window that I was recording was only 1916x1008 so when I imported it into Kdenlive there was a black bar along the bottom. To solve this, I set the resolution of the Kdenlive project to 1278x672 (the same scale but a smaller resolution to keep the GIF size small) and applied a transform effect to position the original clip within the proper resolution. I set the clip speed to 300%, so that in under 30 seconds people could see an example of an entire (if small) map being created.&lt;/p&gt;

&lt;p&gt;With the Kdenlive project ready, I rendered it to an MP4 file for conversion to GIF.&lt;/p&gt;
&lt;p&gt;I then converted the MP4 file using &lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i in.mp4 -vf &quot;scale=-1:-1,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse&quot; -r 10 out.gif&lt;/code&gt;&lt;/pre&gt; 10 fps struck a good balance between choppiness and size; I didn&#x27;t need it to be smooth, just not so jumpy as to be annoying. I had ffmpeg generate a GIF palette itself with the palettegen and paletteuse combo.
&lt;p&gt;Then I optimized the gif, lossily, using ImageMagick&#x27;s mogrify command &lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mogrify -layers &#x27;optimize&#x27; -fuzz 7% out.gif&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The final size of the optimized GIF was 3.1MiB, down from a 21MiB unoptimized GIF, an 8.1MiB MP4 file, and an 11MiB original obs output file.

&lt;/p&gt;&lt;p&gt;
Here&#x27;s the final GIF:
&lt;img src=&quot;https://benleskey.com/blog/../images/mapper_gifs_gif.gif&quot; alt=&quot;A map being drawn in the mapping tool&quot;&gt;
&lt;/p&gt;
&lt;/div&gt;</description>
	<category>discovery</category><category>utilities</category><category>software</category>
	<link>https://benleskey.com/blog/mapper_gifs</link>
	<guid isPermaLink="true">https://benleskey.com/blog/mapper_gifs</guid>
	<pubDate>Fri, 07 Apr 2023 12:00:00 +0000</pubDate>
</item>
<item>
	<title>I took the ETS major field test for computer science</title>
	<description>&lt;div&gt;&lt;p&gt;
	Every senior student at &lt;a href=&quot;https://mvnu.edu&quot;&gt;MVNU&lt;/a&gt; takes a standardized assessment. The assessment lets MVNU administration compare how well their students are doing against other institutions, and how well each program is teaching the fundamentals of the program&#x27;s field. In my case the assessment was the &lt;a href=&quot;https://www.ets.org/content/dam/ets-org/pdfs/mft/comp-sci-test-description.pdf&quot;&gt;ETS major field test for computer science&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
	While I knew the test was coming up, I wasn&#x27;t given any information about what would be on the test, and since I knew it wouldn&#x27;t affect my graduation or GPA I wasn&#x27;t particularly concerned about studying for it. The day before the testing day I decided to look into exactly what I would be taking. I found a &lt;a href=&quot;https://www.ets.org/pdfs/mft/comp-sci-sample-questions.pdf&quot;&gt;sample questions document&lt;/a&gt; (&lt;a href=&quot;https://web.archive.org/web/20230316163356/https://www.ets.org/pdfs/mft/comp-sci-sample-questions.pdf&quot;&gt;archived&lt;/a&gt;) by searching on google. These sample questions were representative of the actual test&amp;#8212;as one&#x27;d expect&amp;#8212;and there was nothing I hadn&#x27;t seen in my courses at MVNU. The bulk of the questions seemed to be more &quot;Computer Science&quot; than &quot;Computer Programming&quot; and were covered by two courses in particular that I&#x27;d taken: &lt;em&gt;Data Structures &amp;amp; Algorithm Design&lt;/em&gt; and &lt;em&gt;Survey and Organization of Programming Languages&lt;/em&gt;. I didn&#x27;t study much beyond checking out the sample questions.
&lt;/p&gt;

&lt;p&gt;
	On the day of the actual test classes were cancelled for everyone to let seniors take their major field assessments, juniors take their general education assessments (only every other junior class has to take these, I didn&#x27;t have to last year), and everyone except graduating seniors to meet with their advisors and schedule classes for the next year. Sadly, I didn&#x27;t get to enjoy much of the cancelled classes as I had to wake up for the major field test at 9 AM, and I still had my meeting with my &lt;a href=&quot;https://benleskey.com/blog/../aka/mappingtool&quot;&gt;Honors project&lt;/a&gt; advisor to keep wrapping things up. The test was proctored&amp;#8212;as required by ETS&amp;#8212;and administered in the main computer lab. The test itself was not very stressful; the sample questions were good preparation and representation of the actual assessment.
&lt;/p&gt;

&lt;a href=&quot;https://benleskey.com/blog/../images/cs_mft_score.png&quot;&gt;&lt;img src=&quot;https://benleskey.com/blog/../thumbs/cs_mft_score.50.png&quot; alt=&quot;Test results&quot; class=&quot;left&quot;&gt;&lt;/a&gt;

&lt;p&gt;
	My score was displayed and e-mailed to me immediately after I completed the test. I scored 181 on a scale from 120&amp;#8211;200, placing me in the 98th percentile. This is pretty good, and I&#x27;m happy with the score, and how it reflects on my learning at MVNU. I could have done a little better with more studying, but most of the questions could be solved with logic, and that&#x27;s not something learned by cramming before a test&amp;#8212;only years of practice can build that kind of understanding and that&#x27;s what brought my score so high.
&lt;/p&gt;

&lt;p&gt;
	I couldn&#x27;t find much other discussion online about the ETS major field test, just a couple &lt;a href=&quot;https://scratchrobotics.com/2018/07/08/how-i-prepare-for-ets-major-field-test-computer-science/&quot;&gt;blog&lt;/a&gt; &lt;a href=&quot;https://scratchrobotics.com/2019/02/26/prep-mft-cs-story-part-2/&quot;&gt;posts&lt;/a&gt; from &lt;a href=&quot;https://scratchrobotics.com/&quot;&gt;a student&#x27;s blog&lt;/a&gt;.
&lt;/p&gt;

&lt;h2&gt;Quirks of the results page&lt;/h2&gt;

&lt;a href=&quot;https://benleskey.com/blog/../images/cs_mft_error.png&quot;&gt;&lt;img src=&quot;https://benleskey.com/blog/../thumbs/cs_mft_error.50.png&quot; alt=&quot;Invalid key pressed popup&quot; class=&quot;right&quot;&gt;&lt;/a&gt;

&lt;p&gt;
	The results page pops up the individual results for the test that I took, but interestingly if I press an &quot;illegal key&quot; like the Windows key, it alerts me and leaves the page. Why? I don&#x27;t know. Maybe it thinks I&#x27;m still taking the test.
&lt;/p&gt;
&lt;/div&gt;</description>
	<category>deepdive</category><category>software</category><category>academics</category>
	<link>https://benleskey.com/blog/cs_mft</link>
	<guid isPermaLink="true">https://benleskey.com/blog/cs_mft</guid>
	<pubDate>Thu, 16 Mar 2023 12:00:00 +0000</pubDate>
</item>_
	</channel>
</rss>
