Dekiro Dev Blog

Part 23 of 28

Weather That Fights Back

Six maps, six mechanics: heat in the desert, cold on the mountain, cult rituals on the moor, lava that moves, drakes that dive, demon portals to close, and a shade that grows in you at the edge of the world. All of it runs on the server from one data file, and all of it had to learn that the ground isn't flat.

A map should play differently, not just look different #

By day six the game had nine world maps, each with its own monsters, music, sky and town. But a level 55 fight on Frostpeak played exactly like a level 35 fight on Duskmoor with different colours. The world plan had always listed a mechanic per map, one line each:

  • Duskmoor: "rituals: cult priests start rituals at circles on the moor; if not stopped in time..."
  • Sunscar Desert: "heat away from shade; oases reset it."
  • Frostpeak: "cold (standing still in the open slows you), campfires; ice patches; avalanche lines."
  • Hellmouth Rift: "demon portals open now and then and pour out waves until closed."
  • Shadowlands: "corruption that builds away from Sanctuary Shards; destroying Shadows cleanses it."

When the AI listed what was still open, "map mechanics from the world plan" was item seven of nine. The developer picked "1-7 and 9", and on the morning of day six one agent built all six in a single run.

The brief had a tone as well as a list, and it ended up written at the top of the data file where every later change will see it:

"_doc": "World-map mechanics (MapMechanics, scripts/world/mechanics/): the rules here, the placement in
each map's \"map_mechanics\" block (written by the map generators' MECHANICS blocks). Everything runs on
the server; clients get the state (telegraphs, HUD) from MapMechanics. [...] Keep it modest: flavour and
reward, not frustration."

A grind game is played for hours at a time. A mechanic that's exciting the first time and a chore the fiftieth is worse than none. So every one of these is either mild and constant, or rare and rewarding.

One node, two halves #

Every map that has mechanics gets a child node, MapMechanics, built on every machine from the map's data. The split of work follows the project's network rules:1 The multiplayer post explains the server-authoritative model and the "single player is a local server" rule this relies on.

  • The server runs everything: it steps the meters, rolls the timers, deals the damage, spawns the monsters and hands out the rewards.
  • Clients only show it: each part has a state() the server sends to peers on that map when it changes and every two seconds anyway, and one-off telegraphs ("a drake is about to land here") go out through broadcast() to everyone on the map.

Where things go is split the same way. The rules live in data/mechanics.json: rates, timers, damage as shares of max HP, rewards. The placement lives in each map's "map_mechanics" block: where the shade is, where the avalanche chutes run, where the ritual circles stand. That block is written by the map's generator, in a marked MECHANICS section, so regenerating a map keeps its mechanics in step with its buildings and roads.

Five of the mechanics are event-like and share a small base class, MechanicPart: a server tick, a state for clients, a way to show one-off events, lines for the quest tracker, and an optional bar on the HUD. The meters are different enough to get their own code.

Heat and Cold: one meter, two personalities #

Heat (the Sunscar Desert) and Cold (Frostpeak) are the same thing: a number from 0 to 100 on the player, Actor.exposure, that builds out in the open and falls in shelter. The rules are pure functions in Exposure, unit tested, and the two maps differ only in their data:

Heat (Sunscar) Cold (Frostpeak)
Builds 0.55 / s in the open, standing or moving 2 / s standing still in the open
Falls 7 / s in shade, 25 / s in water 4 / s while moving, 15 / s near warmth
Shelter 78 spots: tents, ruins, palms, oases; safe zones 57 spots: 21 roadside campfires, buildings; safe zones
Penalty starts at 60 35
At 100 x0.88 move and attack speed, 1.2% max mana lost a second x0.8 move, x0.85 attack speed
Counter Water Skin (120 gold): no heat for 3 minutes keep moving

The interesting part is that they reward opposite behaviour. Heat is about where you stand: it takes about three minutes in the sun to fill, so a long fight in the open dunes slowly costs you, and a run to the nearest palm or pool fixes it. Cold is about whether you move: walking warms you, standing still in the snow chills you in under a minute, so a fight on Frostpeak pushes you to stay mobile or to fight near a campfire.

Heat in the Pale Sands: the bar under the health bar fills in the open (left) and falls in the shade of a tent at Palm Hollow, with the hint line "In the shade: cooling off" (right)

The server steps the meters once a second, and the value replicates with the player's other synced state (the network protocol version was bumped for it). The penalties are computed on every machine from the replicated number, which matters for movement: your own client moves your character, so it needs to know how slow you are without asking. Heat is reset when you change maps or die, and every safe zone counts as shelter, so no one ever respawns into a full meter.

Cold on Frostpeak: a roadside campfire warms you ("By the fire: warming up"), with an Elite snow wolf pack close by

The cold meter also turned out to be reusable. The Frost Monastery dungeon, built the same day by a different agent, switched it on inside: every brazier is a warm spot, and the watchers' sight cones make standing still a bad idea for two reasons at once.

The event mechanics #

Duskmoor: the Lightless Rite #

The moor's mechanic is a world event with a map, the same kind as the desert's sandstorm.2 The desert post (21) explains how world events grew a "map", a "look" and an "aftermath" for the sandstorm. Every 40 to 70 minutes the server may start "The Lightless Rite": the moor's sky turns purple for six minutes, and one or two of its four ritual circles (the Ritual Circle, Nine Widows, Witch-light Hollow, the Hanging Tree) light up, each with three priests and two guards.

Kill a circle's priests before the event ends and it breaks: everyone within 55 m gets 4% of their level bar in XP, 700 gold and a roll on the Elite loot table, reserved for them. Leave it burning and a Ritual Shadow rises at the circle, and up to eight monsters within 45 m turn Elite until someone breaks it. Breaking every circle ends the event early. It's a choice the map offers rather than forces: race to the circles for a reward, or ignore them and accept a tougher moor for a while.

A circle the cult finished: the Ritual Shadow has risen in the purple light, and an acolyte nearby has turned Elite

Frostpeak: avalanches #

Nine chutes run down Frostpeak's crags, each marked by a pale strip of snow and red-tipped poles, so you can learn where they are. Every 110 to 220 seconds, but only while a player is within 70 m, one of them rumbles: a red warning strip lies along it for 3.5 seconds, then the snow comes down. Caught in it, you take 14% of your max HP, get pushed downhill and gain 25 Cold.

An avalanche chute on Frostpeak: the warning strip lies on the slope for 3.5 seconds (left), then the snow slides down it (right)

Ember Wastes: lava that moves, and drakes that dive #

Eight lava pools on the Ember Wastes have three shapes each and switch to the next one every 170 to 260 seconds. Twenty seconds before a shift, glowing cracks (a new shader) appear on the ground where the lava is about to run, players within 70 m are told, and the quest tracker counts down. The map generator keeps the ground flat and free of trees under every shape a pool can take, so the lava never flows through a tree or up a hill.

Lava shifting on the Ember Wastes: glowing cracks warn of the new flow 20 seconds ahead (left), then the lava has moved (right)

In the Lava Fields and the Caldera, Fire Drakes also dive at players every 35 to 60 seconds: a 2.4 second red circle with the drake's shadow sweeping in, then a fiery impact for 10% of max HP and a knockback. Nothing kills you on its own; it keeps you looking at the ground.

Hellmouth Rift: demon portals #

The rift's mechanic is the one players act on most. Nine spots across its three hunting bands can open a demon portal, at most two at a time: the first 40 to 90 seconds after players arrive, then every 150 to 300 seconds, announced on the map. While a player is within 90 m, an open portal sends out a wave of its zone's own monster packs every 22 seconds, with at most eight alive.

You close it by standing in it for eight seconds without being hit. Any damage interrupts the channel, so it's a team play (someone holds the monsters off, someone channels) or a solo puzzle (clear the wave, then channel in the gap). Everyone within 30 m shares the reward: 5% of the level bar, 1,500 gold, and an Elite loot roll each. A portal nobody closes collapses after twelve minutes, with nothing.

Closing a demon portal on the Hellmouth Rift: the channel bar ("Sealing the demon portal") fills while you stand in it unhurt

Shadowlands: the shade #

The last map is the prison of the First Shadow, and its mechanic is the one the final raid already had: corruption, the Shade bar. Out in the open it builds at 0.35 a second (about five minutes to full). Near a Sanctuary Shard (18 new jade shards along the roads, plus the safe zones and the waystones) it falls at 12 a second, and breaking a Shadow stone takes 45 off everyone within 35 m. A Fullmoon weapon in hand halves the build-up, which makes the endgame weapon do something the stat sheet doesn't show.3 The Fullmoon weapons post (17) is about those weapons and their upgrade track.

The damage is gentle on purpose: up to 1.2% of max HP a second while the bar is high, a 12% burst when it fills, then it settles back to 65. Rather than copy the Citadel boss's logic, the agent moved it into one shared function in Corruption and made the boss call it, so the map and the raid can't drift apart.

The Shadowlands' shade: the bar fills out in the open ("The shade grows: break Shadows or find a Sanctuary Shard", left) and lifts in a shard's light (right)

The ground isn't flat #

The most useful finding of the run was a side note in the agent's report:

Existing skill effects (SkillFx) are drawn at a fixed y=0.06, so on hilly maps the terrain hides them. I added telegraphs that follow the ground. Boss warnings and skill effects on the overworld probably have the same problem; that is outside this task.

The game's combat is 2D: everything happens on the X/Z plane, and characters snap to the terrain's height after they move. Skill effects and boss warnings had been designed in flat dungeons and on the flat test map, so they were flat quads at a fixed height just above zero. On a map with terrain, a 3.5 m avalanche warning on a slope would be partly underground, and a drake's circle on a hillside would show as a sliver. For a telegraph that's the worst possible bug: the warning exists, the player just can't see it.

So MechanicPart got its own ground shapes. ground_disc() builds a disc as rings of triangles (one ring every 1.5 m, 32 segments), and ground_strip() a strip as a grid of about 2 m cells. Every vertex samples the map's height and sits 14 cm above it:

var at := func(r: float, a: float) -> Vector3:
	var p := center + Vector3(cos(a), 0.0, sin(a)) * r
	return Vector3(p.x, map().height_at_local(p) + lift, p.z)

The mesh drapes over the hill like a cloth. It costs a few hundred triangles per telegraph, built once when the warning appears, which is nothing next to what's on screen. Every map mechanic's warning (the avalanche strip, the drake circle, the ritual ring, the portal's channel ring) uses these.

The note about skill effects was correct, and it came back eight days later from the other direction. In the first playtest4 The first-playtest post (18) has a before-and-after picture of the street fix. the developer reported:

Skills in the city seem to be "under" the texture of the street.

Town streets and paved squares lie slightly above the terrain, and the skill effects, still at their fixed height, vanished underneath. The fix took the same idea further: ground effects now take their height from the terrain under them and lean with the slope over their size (a big glyph tilts with the hill instead of cutting into it), and enemy and boss warnings got the same treatment. The flaw had been written down the first time it was seen; it only became urgent when a human tripped over it.

Testing weather #

Headless unit tests cover the rules: the exposure math, the corruption steps, ritual outcomes, portal channelling, lava shape schedules, about 150 checks in ten tests. But "is the warning visible on the slope?" is a question for a picture, so there's a scenario, tests/scenario_mechanics.gd, that visits each mechanic on its real map, forces it to happen in front of the camera, and saves a screenshot: heat in the sun and in the shade, cold in the open and at a fire, the avalanche warning and the slide, lava cracks before and after a shift, a drake dive, a portal open and channelling, a ritual burning and risen, the shade in the open and by a shard. Every image in this post comes from one run of it.

What none of these tests can answer is whether the numbers are right. Is three minutes the right time for the sun to get to you? Is eight seconds of channelling tense or tedious? Does a 14% avalanche every few minutes add to a hunt or interrupt it? The agent's report ended where all of these reports end: "All rates, penalties and timings are first guesses and need a playtest." They're one data file, so changing them is cheap; knowing what to change needs players.

Takeaways #

  • Give each map a verb, not just a palette. Seek shade, keep moving, break the circles, close the portal, find the shards: each map asks something different of the same combat.
  • Mild and constant, or rare and rewarding. A grind game is played for hours; nothing here kills you on its own, and the risky ones pay.
  • Rules in one data file, placement in the map generator. A map rebuild keeps its mechanics in the right places, and a balance change never touches code.
  • The server decides, clients draw. One node built on every machine, state on change, one-off events by broadcast, and penalties computed from replicated values where movement needs them.
  • A telegraph you can't see is worse than none. Drape warnings over the terrain; and when an agent writes "probably the same problem elsewhere" in a report, put it on the list.