[53] Status Update
Antorum
02/10/2026, 07:30pm
12 views
Howdy. This is just a quick status update on the project since I've been a bit quiet recently, but development is going well! The antorum.online website is back online in a limited state, and you can download the client and play. The test server is up periodically as I work on things.
Combat Improvements
Instead of being client driven, we now properly sync ticks with the server and try to match impact timings in a latency-aware way. We also have new combat sounds. You can attach an SFXContainerComponent to an NPC to give it custom hurt, death, and attack sounds. Weapons can also have their sound effects defined in their item definition, so each weapon sounds different when you swing it.
The CombatSfxResolver figures out which sounds to play for any given attack. It checks a priority chain: first, if the attacker has a weapon equipped, it pulls SFX ids from the weapon's item definition. If not, it checks for an SFXContainerComponent on the entity (this is how monsters get their custom sounds). And if neither of those exist, it falls back to default unarmed sounds. The resolver also handles finding the right socket to play the sound from (the muzzle socket for ranged weapons, the hand socket for melee, etc.)
CombatSfxResolver.cs
public CombatSfxData Resolve(NetworkEntityComponent entity)
{
// Priority 1: Check for equipped weapon sounds
EquipmentComponent equipment = entity.Equipment;
if (equipment != null)
{
var weaponSfx = ResolveFromEquipment(equipment);
if (weaponSfx != null) return weaponSfx;
}
// Priority 2: Check for monster/NPC SFX container
SFXContainerComponent sfxContainer = entity.GetComponent<SFXContainerComponent>();
if (sfxContainer != null && sfxContainer.State != null)
{
return ResolveFromSfxContainer(sfxContainer);
}
// Priority 3: Unarmed/default fallback
return CreateDefaultSfxData();
}
Dynamic Footstep Sounds
After years of hearing the same footstep sounds, I finally made it so the sound depends on the surface you are walking on. Not difficult, I don't know why it took so long. But it is very satisfying.
We keep a mapping of material/tile ID to AudioEvents. At runtime, we sample the surface the player is walking on, grab the material id, and resolve it to the right footstep sound. There's a default fallback for any unmapped surface. Simple, but makes a huge difference. Grass, stone, wood, and sand all feel distinct now. It does a lot for spatial awareness and just making the world feel more grounded.
DevOps Changes and Workflow Improvements
I had some issues with my server provider, and had no help from support, so I changed providers and rebuilt my server infrastructure. I also made some workflow improvements to make it easier to deploy and manage things from anywhere.
I created GitHub workflows to automate building and deployment of the client, editor, server docker images, and game content (addressables). The workflows also upload the latest client and editor executables to the website, so the most recent build is always available for download. antorum.online is back up, but still very much a work in progress. For now it is just a place to download the client and get some basic information about the game.
Entity Inheritance
Entities can now extend one another, making similar entities easier to maintain and reducing duplication. So we have a base npc-chonkrat, and then npc-chonkrat-runt, npc-chonkrat-papa, etc. Entity definitions are TOML files. A derived entity specifies extends = "npc-chonkrat" and only overrides the components it needs to change. Everything else is inherited from the base. The server merges component tables at load time with deep merging for nested properties, and validates that there are no circular inheritance chains. This matters a lot long-term: adding a new variant is just a small file with a few overrides instead of a full copy-paste, which means less duplication and fewer bugs when the base definition changes. Here's the base chonkrat and its two variants:
npc-chonkrat.toml
id = 98
[networked]
[info]
name = "Chonkrat"
description = "An abnormally large and menacing variant of rat."
model_id = 54
[transform]
[animator]
animation_controller_id = 3
[npc]
wander_radius = 20.0
sayings = ["eep"]
combat_sayings = ["SCREE!"]
[interactable]
interactions = ["attack", "walk-to", "examine"]
[movement]
speed = 1.25
[health]
[combat]
[stats]
max_hp = 12
attack = 2
armor = 1
[rest]
requires_rest_zone = false
[drop_table]
drops = [
["any",
"1, 100.0",
"330, 90.0",
"96, 90.0",
"414, 30.0"
],
]
npc-chonkrat-runt.toml
id = 81
extends = "npc-chonkrat"
[info]
name = "Chonkrat Runt"
description = "Slightly less menacing than its larger counterpart."
[transform]
scale = 0.75
[stats]
max_hp = 10
attack = 1
armor = 0
npc-chonkrat-papa.toml
id = 21
extends = "npc-chonkrat"
[info]
name = "Chonkrat Papa"
description = "Don't mess with his ratlings."
[transform]
scale = 1.5
[stats]
max_hp = 20
attack = 4
armor = 4
Dialog Editor
I vibe coded a dialog editor to help me work on more complex conversations. It's built with Dioxus, largely with the help of ChatGPT and Claude. Dioxus is great for building quick UIs and it is nice to stay in the Rust ecosystem, since I have been extracting server code into a shared module which tooling can use. The editor still needs a lot of work though. Node edges and connections are especially difficult to get right. Very basic feature set right now, but it is already helpful for visualizing conversation flow.
As for the vibe coding workflow itself, it is very nice and becoming very important, but of course that is bittersweet. Working with AI tools exhausts me more than anything. It is great for quickly iterating and learning something new, but in the days of agentic tools I think we are just going to find ourselves prompting them and running "teams" of them. I don't know how this is all going to turn out.
I guess there is a reason I've been building an MMORPG for years as a sort of escapism.
Thanks for reading. More news soon!
- Declan (@dooskington)