Antorum Online

[66] Planetfall

2026-10-19, 6:00 PM
5 views

I've been reworking the tutorial (again). I'm trying to find a good balance between story and gameplay so the player learns how to play but also gets a decent introduction to the world. I think it is in a good place now. We have a basic orientation sequence and then send players to a new zone, Planetfall Isle. Building that opening meant finally giving quests some real structure, so this post covers both.

Planetfall Isle

New characters still start with orientation at Intake Facility 45, but the trip to the mainland doesn't go to plan. The transfer shuttle is shot down over Planetfall Isle, leaving you at the wreck with Officer Hawthorne and Pilot Pete.

The opening takes you from salvaging supplies and clearing vermin around the crash site, up the ridge to the Scout Camp, and on to the Comms Outpost. There are 20 quests from 12 NPCs across the three areas, with some combat, gathering, cooking, smelting, and making your own armor.

A player facing a quest giver beside the crashed shuttle on Planetfall Isle.

A quest giver beside the shuttle wreck on Planetfall Isle.

Quest Objectives

Until now, quests in Antorum were pretty bare bones. A quest was a list of numbered stages with some journal text for each one, and everything else was Lua. A dialog script would set your stage, an entity script would check it before letting you do something, and so on. I covered that setup way back in the Lua Scripting post, and it hadn't changed much since.

That's flexible, but it turned every ordinary MMO quest step into its own little scripting project. A "kill 10 rats" quest would have needed a death hook on the rat, a counter stashed in a quest variable, and a check in the dialog at the end. "Bring me 2 cooked venison" meant the dialog script counting items in your inventory. The player never saw any of that, either. The quest log only showed journal text, so you had to keep count in your head.

Quest stages can now declare objectives, as plain data. Here's the early game Ratcatcher quest:

[[stages]]
stage = 0
completion = "automatic"
next_stage = 10

[[stages.objectives]]
id = 0
type = "kill"
target = 81 # Chonkrat Runt
required = 10

[[stages]]
stage = 10

[[stages.objectives]]
id = 0
type = "talk-to"
target = 187 # Pilot Pete

Stage 0 wants 10 dead Chonkrat Runts. Its completion is automatic, so as soon as you hit 10 it advances to stage 10 on its own, which just wants you to go talk to Pete. Stage 10 is scripted, which is the default. The quest waits there until something else moves it along, in this case Pete's dialog paying you and closing the quest out. Quests that don't declare any objectives keep working exactly like they did before.

There are eight kinds of objective so far:

  • kill: kill a certain kind of creature
  • possess-item: have some amount of an item in your inventory
  • craft-item: craft or cook an item
  • talk-to: talk to an NPC
  • interact: use something in the world
  • consume-item: eat or drink something
  • enter-zone: reach a named zone on the map
  • enter-chunk: reach a specific map chunk

Objectives can also be optional, meaning they track progress but don't hold up the stage.

The objective text is generated from the objective's kind and whatever it points at, so that kill objective shows up as Chonkrat Runt slain: 3/10, and the talk-to shows up as Talk to Pilot Pete. Names stay singular and the count carries the quantity, so I don't have to write (or pluralize) the same boilerplate for every quest. When a step needs better wording, like "Bring the legs back to Cook Orson Pell", I can still write a description.

Under the Hood

The main thing I wanted to avoid was quest logic leaking into everything. NPCs shouldn't need to know they are part of a quest. So the systems that own each behavior just announce what happened, as a generic gameplay event:

pub enum GameplayEvent {
    EntityKilled { player: Entity, target_res_id: ResourceId },
    ItemCrafted { player: Entity, item_res_id: ResourceId, amount: u32 },
    ItemConsumed { player: Entity, item_res_id: ResourceId },
    NpcTalkedTo { player: Entity, target_res_id: ResourceId },
    ObjectInteracted { player: Entity, target_res_id: ResourceId },
    ChunkEntered { player: Entity, coords: ChunkCoords },
    ZoneEntered { player: Entity, zone_id: ZoneId },
}

The health system fires EntityKilled when a player kills something, crafting and cooking fire ItemCrafted, dialog fires NpcTalkedTo, and so on. The quest system reads those events and checks them against the objectives on each player's current stage, and only the current stage. Kills from before you took the quest don't count, and neither do kills for a stage you haven't reached yet.

Progress comes in two kinds. Most objectives are cumulative. A kill is a kill, so it gets counted and saved to the database, and survives logging out. Objectives that ask you to have an item are derived. Their progress is read straight from your inventory whenever it's needed, and never saved. If you have 3 of the 4 of some objective's items and you drop one, the objective goes back down to 2/4. Trying to count items as they come in and go out would have been a bug factory.

Since objectives are just IDs pointing at other content, the server checks all of them when it loads. Every target has to exist, an automatic stage has to say where it goes next, stages can only move forward, and a quest can't require itself. A typo in a quest file shows up as an error on startup (and a failing test) instead of a broken quest.

Quest Log and Markers

For a while we just had a simple journal display that gave some handwritten info about the current objective. The quest log now lists the objectives for your current stage, with counts, and turns each one yellow once it's done. Progress also shows up in the system chat as it happens, so you don't need the quest log open while you're out killing rats. Maybe I'll add an on screen quest tracker at some point, not sure.

Quest markers used to be super janky, but they are much improved now. They more reliably reflect your current quest state, prerequisites, etc.

Scripting

We still have Lua support, it just handles anything that needs actual storytelling logic. Objectives just take care of the boring parts, and scripts can ask about them through new functions like is_objective_complete, get_objective_progress, and advance_quest_stage.

A player outside the orientation halls at Intake Facility 45.

Orientation at Intake Facility 45.

Changelog

Release versions: Client 0.1.10, World Editor 0.1.6, Game Server 0.1.11, Addressables 0.3.3, Website 0.1.21.

Here is everything else that has landed since the last post.

Combat

  • Combat skill levels now actually scale your accuracy, damage and defense. Before, they only gated what you could equip.
  • Hit chance takes level into account, so out-leveled enemies are easier and higher level ones are harder. Low level hit chance is no longer stuck around 50% no matter what you're carrying.
  • Accuracy and Power are separate stats now, and every weapon was retuned and repriced around them.
  • Weapons train the skill they require, so Brass Knuckles train Brawling instead of Melee Weapons.
  • Added a Combat Level based on your combat skills. Enemy levels show when you mouse over or right click them, colored by how tough they are compared to you. Most enemies leave you alone once you're at least twice their level.
  • Shields have a block roll of their own. A block stops the hit completely, trains Shields, and wears down the shield.
  • Combat experience comes from the damage you actually deal, so overkill doesn't pay extra. Armor experience comes from both the damage you deal and the damage you take.
  • Only combat skills raise your max health and stamina now. Nobody loses what they already have.
  • Fighters stop swinging the moment their target dies, and no longer give up mid-fight in crowded spots. Melee hits also land on the next tick instead of after a delay.
  • Fixed unintended bonuses that made low level enemies tougher than intended.
  • Combat sounds depend on the weapon and what it hits. Misses and two-handed weapons finally make noise, and hurt and block reactions are back.

Gameplay

  • Added a new entry level armor tier, crafted at the anvil: a Rat Hide set, Scrap chainmail, Scrapiron plate and a Scrap shield. Iron gear was retuned to sit above it. Most of the new pieces still need art.
  • Added the Black Leather Duster, sold by the armor merchant.
  • Wolves are now Razorhounds, and their pelts and meat were renamed to match. Razorhound Hides can also be salvaged into Hide Scraps for Patchleather.
  • Each tutorial lesson explains its goal and controls when it starts, and shows your overall progress when you finish it.
  • Furnaces only use smelting recipes now, so Scrap Metal smelts into Scrap Ingots instead of matching anvil recipes.
  • Iron Ingots take two Iron Ore now, and mining Iron Ore gives a little more experience.
  • Smelting or cooking a bigger batch than you have materials for no longer eats the ingredients.
  • Crafted gear sells for more than its materials, and Patchleather Tunic, Patchleather Pants and Vespex Carapace can be salvaged.
  • Some interactions take a moment now, like harvesting Vespex resin, and objects can set their own left click action.
  • Matching stacks merge in the vault, like they do in your bag.
  • New characters respawn in their starter outfit instead of naked.
  • The /die command is admin only now.
  • NPCs that stand at a post walk back to it after a fight, and bones no longer pile up after the loot they mark is gone.
  • Fixed weighted drops favoring the first item in a table, and sometimes dropping nothing at all.
  • Fixed the server and client disagreeing about which tile you were on at negative coordinates, which is about a quarter of the map.

Client

  • Added an exp tracker at the top of the screen. It replaces the "+N exp" chat messages.
  • Quests you haven't started show up as ??? at the bottom of the quest log.
  • The exported world map shows the whole map instead of being cropped, with the longest side capped at roughly 4K.
  • Hovering a sub zone on the world map shows its parent zone too.
  • Cleaned up more fallout from the world rescale: minimap click-to-move went four times too far, vehicle audio carried twice as far as everything else, dropped items arced too high, and effects were twice their size.
  • Fixed the minimap freezing in negative world coordinates.
  • Fixed fishing-line attachment, and corrected gun muzzle and torch-light positions after the item models were rescaled.
  • Fixed small items not showing in the crafting preview, dropped items at half size, invisible dropped boots and sandals, and the Shock Stick's sparks and missing texture.
  • Fixed an inventory error after dying, and windows opened by the server reopening empty instead of closing.
  • Zone music plays again after a break instead of going silent after the first track.

Stability and Security

  • Fixed a server memory leak that grew with every entity ever spawned.
  • Item saves now update the whole inventory together, and failed recipe saves can be retried instead of silently losing progress.
  • Vault moves are bounds checked and always saved, and items stranded in out of range slots are recovered.
  • Closed an exploit that could put an enchantment on an item it wasn't meant for.
  • Pathfinding is bounded and much cheaper, so one click can't search the whole map anymore.
  • The server runs its database and network syncs once per tick instead of about 60 times a second.
  • Hardened the server's Lua environment and fixed scripts sometimes reusing the wrong cached code.
  • Added checks for missing or mismatched references between game data files before release.
  • Invalid map versions and oversized allocations fail with an error instead of loading unchecked.
  • A failed feedback submission reports an error instead of crashing the server.

Editor

  • World bounds are correct for maps that sit entirely on one side of the origin.
  • Changing water quality no longer overwrites the shared ocean material.

Site

  • The login session cookie is marked Secure now.

What Next?

I'll keep working on the actual map since it's still mostly a block out. Then I need to add more quests, gear items, and get some playtesting in. Things are really feeling like they are slowly coming together.

Thanks for reading.

- Declan (@dooskington)