Skip to content

Releases: devnullnoop/rotorwash-game

v0.12.1 — the water drop you could never see

Choose a tag to compare

@devnullnoop devnullnoop released this 25 Sep 17:21

Download for macOS · Download for Windows — assets below. Neither build is
code-signed, so your OS will complain the first time; see the README for the two clicks.

The bucket drop had no visual at all

Releasing 900 kg of water used to change the simulation and nothing else. The fire went
out, the score updated, and the only evidence the pilot got was a line of text. The shipped
screenshots have shown a bucket hanging over a flame front with no water anywhere for three
releases, because the game could not draw it.

There are now three events, because a drop is three events and they happen at different
times: the column leaving the bucket, the sheet bursting on the ground, and steam coming off
wet fuel for several seconds after. The delay between the first two is the real fall time,
t = (√(v₀² + 2gh) − v₀)/g — about 1.2 s from 12 m and 2.7 s from 45 m — so the height you
released from reads in the picture. Steam appears only where the simulation says fuel was
actually wetted, so what you see and what you scored cannot disagree.

Fixed

  • A slung load hung from a slack, curved rope while you were in the air. agl() is
    altitude − ground_alt, and set_spawn plants the airframe at ground + 0.6 — so it
    returns 0.6 whatever you pass it. The sling believed the ground was 0.6 m under the
    helicopter and rested the bucket on that phantom surface. It now asks the terrain for the
    ground under the load, which is also correct on a slope or a shoreline.
  • The game said "NO WATER HERE" while you hovered over a river. Refusing the fill is
    right — rivers here run 0.30 m deep at the median against a 1.6 m requirement — but
    refusing and lying are different things. It now refuses with the true depth, and a
    genuinely deep pool fills the bucket on its merits.
  • A rattle over the rivers that grew as you descended. The ambience bed modulated 70%
    deep at 8–20 Hz, which is the band where hearing reads periodicity as roughness. Moving
    water has no periodic component; the envelope is now a slow random walk.
  • Rock on the near banks rendered 1.2–1.3× brighter than the same stone beside it: the
    triplanar half of the terrain shader was the only albedo not paying the macro-variation
    darkening.
  • A camera told to point at "the fire" pointed at burnt-out scar — the fire's head POI was
    publishing the ignition point.

Known and open

The tree LOD hand-off at ~820 m is measured but not fixed: the near tier and the far tier
draw visibly different forests and which one is wrong is still undecided.
ROTORWASH_NOIMPOSTORS=1 shows the comparison.

Full detail in CHANGELOG.md.

v0.12.0 — the water pass

Choose a tag to compare

@devnullnoop devnullnoop released this 24 Sep 18:44

Water now brightens when you look along its surface, which is how you see a lake from a helicopter. Inland water had been shipping at a quarter of the correct reflectance, with its grazing response capped at 0.18 instead of 1.0 — so lakes rendered as black slabs. The value that fixes it was already derived in a comment in the shader and had never been applied.

Shallow rivers now show their bed and deep pools do not, and rivers run out of lakes and down to the sea as one continuous system instead of three effects meeting at a seam.

Terrain texture detail reaches the whole view instead of stopping at 400 m. That also removed a white ring that swept across the hills as you flew, and made the landing light's pool on the ground visible for the first time.

Known and not yet fixed: riverbanks and some hillsides wash out pale where the rock texture takes over too eagerly, and there is a turquoise cast on some steep faces. Most screenshots on the front page predate this release. Both are the next thing.

macOS — Universal (Apple Silicon and Intel), macOS 11+. Not code-signed: open it, click Done, then System Settings → Privacy & Security → Open Anyway.
Windows — 64-bit. SmartScreen will warn: More info → Run anyway.

v0.11.0 — the realism pass

Choose a tag to compare

@devnullnoop devnullnoop released this 17 Sep 23:52

The realism pass. The world stops cheating when you look closely — or fly far,
or stay out after dark.

Highlights

  • A landing light. L (or right shoulder). A real nose searchlight that
    slews to keep its pool ahead of you on approach — steep in the hover,
    shallow on final — with a lens glint, a HUD annunciator, and rain that
    flares inside the beam.
  • Breaking surf on every shore. Swell lines steepen and dump white water
    as they shoal, building with the weather. Lakes stay glass; rivers stay
    rivers.
  • Fire that survives a close look. Up close, flames writhe and tear
    instead of reading as sprite tongues; the far look you know is untouched.
    Embers now arc and rain down glowing.
  • A high tactical view. V (or D-pad down): ~45° down on the aircraft so
    bucket, ground, and fire share one frame — and the bucket HUD line now
    reminds you of the views while the bucket is on the hook.
  • The world holds together in motion. Weather cells, cloud shadows, smoke
    columns, burn scars and every ground pattern stay put as you cross the big
    island; cloud shadows drift at the wind forever instead of accelerating; a
    storm sea is finally rougher than a calm one, and the sea drifts downwind.
  • Dusk behaves. The setting sun hands off to the moon instead of sweeping
    across the sky; distant rain shafts survive climbing into the cloud deck;
    flying inside cloud actually murks. Roads carry real dashes; about a fifth
    of houses stay dark at night.

Full details in the
CHANGELOG.

Installing on macOS: the app is not notarized. Double-click, choose Done,
then System Settings → Privacy & Security → "Open Anyway".

v0.10.0 — the distance pass

Choose a tag to compare

@devnullnoop devnullnoop released this 16 Sep 23:28

The distance pass. The Medium island was a setting you could select but not a
place you could fly: the camera clipped everything past 4 km, the home base
did not exist on it at all, and past ~7 km from spawn the terrain hit a hard
rendering wall — refused terrain, floating forests. This release makes the far
country real: fly the Medium island end to end, and what you see on the way
holds together.

Highlights

  • The world now travels with you. The render window re-anchors itself
    every ~6 km of flight, invisibly, so terrain, forests, roads and water exist
    wherever you go instead of only in a bubble around your spawn.
  • Steep country earns dirt tracks, not highways. A road that only routes
    at the relaxed grade ceilings is built as a narrow dirt track switchbacking
    up the hillside — and the telegraph line still follows it over the pass.
  • A home base on the Medium island, where you start, instead of pinned to
    an island centre you could never see.
  • See 14 km instead of 4. The far plane finally matches the island, with
    the content budget clamped so the distance costs views, not framerate.
  • Roads are measured honestly now — per class, on the surface the mesh
    actually builds — and the network dropped ~315 km of phantom trunk roads
    that pretended two settlements a lake apart were neighbours.
  • Audio rides out streaming stalls instead of crackling through them, and
    the mix leans toward the rotor.

Full details in the
CHANGELOG.

Installing on macOS: the app is not notarized. Double-click, choose Done,
then System Settings → Privacy & Security → "Open Anyway".

v0.9.0 — the fire pass

Choose a tag to compare

@devnullnoop devnullnoop released this 11 Sep 13:36

The fire pass

The old flames were bonfire-shaped. Every 32 m cell of fire drew one smooth
symmetric mound, so a fire front read as a row of giant campfires — and scaling a
campfire's silhouette up to wildfire size is exactly what it looked like.

They are now built from the fire's own behaviour instead of from a lookup table
per fuel type:

  • Flame length, band depth and lean all come from fireline intensity — how
    many kilowatts per metre of front the fire is actually putting out. A grass
    fire lies over at about 46° with metre-scale flames; a crown fire stands nearly
    upright with ten-metre ones; and the slow back edge of a fire draws visibly
    shorter flames than the head racing away from it.
  • The burning band runs along the front. It used to run along a fixed world
    axis no matter which way the fire was travelling.
  • And it is deeper than it is tall, which is the shape a spreading fire's
    flaming zone actually has.
  • Flame colour is a temperature. There is no blue in it any more, because a
    sooty flame that cool has none to emit — that was the "gas burner" tell.
    Flames no longer glow through daylight, because a real one is dimmer than
    sunlit dry grass.

The smoke column rises on the standard buoyant-plume law rather than an invented
one, widens the way plumes are measured to widen, bends over where the updraft
stops beating the wind rather than at a fixed height, and takes its strength from
the fire's heat output — so a big fire punches a column a small one cannot.

Fixed

  • The ocean was painting over the smoke. The sea is a 16 km transparent
    plane, and without a draw priority it sorted in front of the plume and erased
    it wherever sea was behind the smoke — a straight cut at the horizon. This was
    never smoke-specific: everything transparent seen against open water was
    affected, including the spray under the aircraft during a bucket dip.
  • The benchmarking harness had been measuring the wrong place for any
    fire-anchored viewpoint.
  • Fuel consumption was being discounted twice, putting cured grass well below
    what it really carries.

Performance

Faster than the version it replaces, despite all of the above — close pass on a
burning ridge goes 131 → 137 fps, the 1% low 94 → 123, and the worst frame
19.2 ms → 8.7 ms.

Known open

The fire-sim harness's terminates check still fails exactly as it did before
this pass. Flame orientation is uniform across a fire — length and depth vary
cell by cell, but the sim has one wind direction, not one per cell. Looking
straight up into the plume from directly underneath, the sprites are still
countable.


macOS: universal (Apple Silicon + Intel), ad-hoc signed. Gatekeeper will
object to an unsigned app: System Settings → Privacy & Security → Open Anyway.

Windows: x86_64.

v0.8.1 — fun mode, flown

Choose a tag to compare

@devnullnoop devnullnoop released this 10 Sep 12:48

Fun mode got flown by an actual human — an 8-year-old's co-pilot — a few hours
after v0.8.0 shipped. Two findings came back, and both are here.

It would not let you crash

The terrain floor, the ground rescue and the near-ground pitch limit were all
absolute, so no sequence of inputs could put the aircraft into the ground.

That is not forgiving, it is invincible. Worse, it made the vertical axis a
control that stopped doing anything at the exact moment it was being used
hardest — which is the same bug this project keeps finding in other clothes.

Every ground protection is now gated on downward intent, read off the
vertical axis alone so the rule stays simple enough for a child:

Let go and you are safe. Hold it down and you will hit.

The band sits hard against the stop, and the command slew means holding it there
for about a second before the floor is fully gone. Anything you do casually — a
normal descent, a nervous jab, coming down to land — keeps the whole envelope.
A committed dive into a hillside now ends the way it should.

The other protections are unchanged: still no rolling inverted, no settling with
power, no running the tail rotor out of authority. Those are things nobody
chooses. The ground is a thing you can choose.

It was slow near the ground

Nose-down authority faded from 220 m down to a floor of 5.7 degrees under 60 m —
tight enough to read as the stick going dead just when you want to move the nose.
It now floors at 12.6 degrees and fades over a lower, narrower band, and a
committed pilot lifts it the rest of the way through the intent gate above.

Notes

Fun mode remains a first pass and the pilot who flew it said so — there is more
refinement to come. macOS is a universal build (Apple Silicon and Intel).
Neither build is signed; see the README for the two-click way past the warning.

v0.8.0 — Fun mode, and the island can finally catch fire

Choose a tag to compare

@devnullnoop devnullnoop released this 10 Sep 05:21

Two things: a fourth flight-assist level built for an 8-year-old, and the
discovery that the wildfire this project has shipped three releases about has
never once started in anybody's game.

Fun mode

I fly this with my 8-year-old and we both found the controls too twitchy. There
is now a Fun (kid mode) option in Settings → Assist. It is a different
control law, not a stronger version of the same one:

  • The right stick is where the helicopter points. Stick position is the
    bank angle and is the dive angle. The command is rate-limited — a full
    second corner to corner — so a slammed stick swoops instead of snapping. That
    is the actual fix for "too touchy": the softness lives in the command, where
    it costs no responsiveness on a gentle input.
  • The left stick is altitude. Up is up, centre holds the height you are at.
  • Turns carve. Bank it and the nose comes round on its own at the proper
    coordinated rate, with the collective easing up so the turn holds its height
    instead of sagging out of it.
  • You cannot break it. No rolling inverted, no settling with power, no
    running the tail rotor out of authority, and it will not fly you into the
    ground in a dive.

Tip past about 30 degrees and it commits: the speed cap opens, you get a real
dive, and pulling back zooms you out of it.

It is not on rails. The same blade-element flight model is still underneath, so
the machine still has weight and still lags behind you — deliberately, because
without inertia there is nothing to carve. Tab can neither reach Fun mode nor
leave it, so a kid pressing everything cannot fall out of it by accident.

The wildfire never started

The only code that lit a fire was behind a developer environment variable.
_boot_ignite() had exactly one caller, gated on ROTORWASH_FIRE_NOW, and
nothing else in the repository called pick_incident() or ignite_at().

So if you downloaded v0.7.0, v0.7.1 or v0.7.2 — three releases whose headline
was a wildfire and a water bucket, with a feature bullet about it on the front
page — the island could not burn. I am sorry; that is a bad one, and it is the
kind of thing worth saying plainly rather than quietly fixing.

A fire now starts on its own a short while after the world settles. Verified in
this exact build, with no environment variables set.

The end of a sortie was unreachable too, in both directions: the "fire is out"
and "fire is lost" checks each had zero callers, so a fire you put out never
registered as won and the HUD's finished FIRE OUT banner had never been seen
by anyone. Both are wired now, both are announced, and a new fire gets called in
once the last one closes.

Also fixed: the burnt-hectares figure — the number the end-of-sortie banner
quotes as your result — went down when you put fire out, because doused
cells were being subtracted. Burnt ground does not un-burn.

Everything else

  • Ignition no longer stalls the frame: worst single frame 158 ms → 69 ms.
  • Repeat call-outs no longer send you back to ground you already burnt.
  • Ending a sortie no longer deletes the burn scar 30 seconds later.
  • The scorch pass no longer copies and re-uploads a whole tree buffer per call.
  • The assist level persists between runs, and now actually reaches the flight
    model — there was no code pushing it there at all before.
  • The collective tape turns amber when the lever is pinned.
  • Smoke reads as a plume rather than a chain of soap bubbles. Better, not fixed:
    up close you can still see the individual puffs, and it remains the weakest
    thing in the frame.

Honest notes

Fun mode has not been flown by a human yet. It passes 118 flight-model tests
and a lot of measured step responses, but this project's own history says
headless checks do not settle whether something feels right. If it feels
wrong, that is useful and I would like to hear it.

macOS is a universal build (Apple Silicon and Intel). Neither build is signed —
see the README for the two-click way past the OS warning.

v0.7.1 — the fire looks like a fire

Choose a tag to compare

@devnullnoop devnullnoop released this 05 Sep 01:21

A fly-check of the wildfire produced four complaints. All four were defects rather than taste, and the vaguest one — "it doesn't look associated with anything being on fire" — turned out to be hiding a bug that had quietly disabled a whole feature.

The fire looks like a fire now

Flames stay where they were lit. The ten emitters were being reassigned every frame from a list that returns cells in grid order, so as the front advanced each emitter landed on a different cell and its flame simply teleported. Nothing was appearing out of nowhere — the same ten flames were being flung around the burn sixty times a second. The point lights had the same problem, only brighter and at night.

Each fuel burns as itself. The simulation has always had six fuel classes with their own spread rates, flame durations and resistance to water; the renderer drew one identical emitter for every one of them. Grass and crop now burn as a wide, low, pale sheet that is gone in seconds. Scrub stands taller and holds. Forest is the crown fire — tall, deep and slow.

Flames stand on something. A forest flame is planted on an actual tree trunk and scaled to that tree's height, rather than hovering over the centre of a 32 m cell. The rest are offset by a positional hash, so a burn no longer reads as identical stamps on a chessboard.

The fire is on the map. It was on no map at all — the only way to navigate to it was to watch the range in the banner tick down and guess the heading. The marker is a ring sized to the real burnt area, so the chart shows it growing, and it clamps to the edge with a range readout when it is off the map.

And the bug underneath

The trees a fire burns over were supposed to char. They almost certainly never have.

Both walks over the tree instances used MultiMeshInstance3D.get_aabb() as a coarse reject. That is a rendering-side quantity, and when the renderer has not computed one it is a degenerate box at the origin — which fails every overlap test against a region that does not happen to contain the world origin. So the reject discarded every chunk, and the walk found nothing unless the caller asked about the entire world. A burn scar asking about its own two hundred metres is exactly the caller that does not.

It failed silently, which is why it survived: the walk reports how many instances it touched, and zero touched reads identically to "nothing has burnt yet".

Measured on a 32 m box centred on a known tree: 0 instances before the fix, 1 after, against a hand census of 1.


Platform File Notes
macOS Rotorwash-macOS.zip Universal — Apple Silicon and Intel. macOS 11+.
Windows Rotorwash-Windows.zip 64-bit.

Neither build is code-signed, so your OS will complain the first time. On macOS,
double-click, click Done, then open System Settings → Privacy & Security
and click Open Anyway (right-click → Open no longer works). On Windows, choose
More info → Run anyway.

Controls, including the full controller layout, are in the
README.

v0.7.0 — the wildfire release

Choose a tag to compare

@devnullnoop devnullnoop released this 05 Sep 00:40

There is a fire burning somewhere on the island, and putting it out is a flying problem.

The wildfire

It spreads on the fuel that is actually under it, up the slope and downwind. Measured on the headless harness rather than asserted: 10.2× faster uphill than down, 4.4× faster with the wind than against it, and open water stops it dead — of 434 cells burnt against a shoreline, none crossed. The same seed burns the same way twice.

You can see it: flame and embers, a smoke column that leans downwind and glows on its underside at night, and ground that goes black behind the front with the trees standing in it charred.

The fire's look is the least finished thing in this release. Every burning cell
currently draws the same flame regardless of whether it is standing in grass or
closed canopy, and the map does not mark the fire at all — you navigate to it on
the bearing and range in the banner. Both are the next thing being worked on.

The bucket

Land on a pad and press B. Hover until the bucket is in a lake or the sea — about 9 m on the altimeter, and the HUD shows you the bucket's own height on a tape as you come down, because your altimeter reads the skids and the bucket is eight metres of rope below them. Then fly to the fire and press G.

Nine hundred kilos, and you feel every one of them on the collective the moment they arrive. One drop will not kill a canopy fire — it lands about half of what it takes — so you will be shuttling, and the round trip to the nearest water is the decision the sortie is really about. Drop while you are moving and the water paints a longer, thinner stripe than a hover does.

Fly it on a controller

Any Xbox, PlayStation or generic pad is picked up at launch with nothing to configure. Left stick collective and pedals, right stick cyclic, triggers as an alternative yaw, the job on the face buttons.

This is the single biggest change to how the game feels. A helicopter is four continuous axes and a keyboard has none of them — every key is fully on or fully off, so on the arrow keys you are metering the cyclic by counting milliseconds. The sticks carry a dead band and an expo curve, so the middle of the travel, where a hover is actually flown, is far finer than the ends while the stops keep full authority.

On a Mac: hold the small pair button on the top edge of the controller until the logo flashes, then add it under System Settings → Bluetooth. Or just plug it in.

Fixed

  • The water bucket could never be filled. The test asked for a value both at or above zero and at or below minus fifteen centimetres — no aircraft position satisfies that. It also read a height that is clamped at zero, so it could not have reported a submerged bucket in the first place.
  • The sling was not attached to the helicopter. The hook was held bolt upright over the aircraft's centre no matter what the airframe did, so pitching forward to accelerate slid the belly out from over the rope.
  • The sling still stretched, twice over — four cables of four different lengths that changed as the load swung, and a rope drawn taut across a shorter gap whenever the load rested on the ground, which is a rope visibly contracting.
  • Nothing that refuses you is silent any more. Pressing B off a pad, or too high over water, or over a river too shallow to dip, used to do nothing and say nothing about why. A control that does nothing is indistinguishable from one that is broken — which is how that impossible fill test survived into v0.6.0.

Also

Freight looks like freight now — planks, battens, steel bands, a pallet — and the bucket's fabric shell swells as it fills, so how much water you have aboard is readable at a glance from any angle.


Platform File Notes
macOS Rotorwash-macOS.zip Universal — Apple Silicon and Intel. macOS 11+.
Windows Rotorwash-Windows.zip 64-bit.

Neither build is code-signed, so your OS will complain the first time. On macOS,
double-click, click Done, then open System Settings → Privacy & Security
and click Open Anyway (right-click → Open no longer works). On Windows, choose
More info → Run anyway.

Controls, including the full controller layout, are in the
README.

v0.6.0 — a job, and something to lose

Choose a tag to compare

@devnullnoop devnullnoop released this 03 Sep 14:27

The release where it becomes a game. v0.5.0 was a world with a validated
helicopter in it and nothing to do; this one gives you a job, something to lose,
and a record of how well you did it — all of it read off the flight model that
was already there rather than bolted alongside it.

Added

  • Transport contracts — a reason to fly somewhere. Set down on a friendly pad
    and it offers you a load for a named settlement; take it, fly it, and put it down
    in the landing zone beside the place. Contracts are a pure function of the pad and
    the world seed, so the same pad always offers the same job until you fly it, and
    what you have delivered is remembered per island.
  • Cargo has weight, and the weight is real. A load is added to the airframe's
    mass and nothing else — there is no separate "carrying" code path — so the flight
    model charges for it the way it charges for any other weight: more collective to
    hover, more torque and therefore more pedal, more power, a worse hover ceiling and
    a faster autorotation. An empty aircraft hovers on 71% collective; at the 1400 kg
    ceiling it needs 89%, which is why a heavy contract wants a running takeoff — the
    same load only asks 77% at 60 kt.
  • You can see the cargo. The load hangs under the aircraft on a four-point sling,
    sized to its weight, trailing behind you in forward flight and settling onto the
    ground when you land. The contract on offer sits on the pad as crates on a pallet,
    and the destination is marked with panels and an orange smoke column you can find
    from the air — which doubles as the wind indicator for the approach.
  • The record is visible. Delivering shows a debrief with the run and the totals
    behind it, the island map carries a contract tally, and every settlement you have
    served wears a ring on the chart. The delivery log was already being written to
    disk; now something reads it.
  • Fuel. The tank is not a timer: burn is specific fuel consumption against the
    power the engine is actually delivering, so a hover drinks about twice what a
    cruise does and a heavy load costs range because it always cost power. Fuel is
    mass, so the aircraft gets lighter as it burns and heavier as it fills. Run the
    tank dry and the engine simply stops — you are in an autorotation, which the
    model already does properly, and the landing is still yours to make. Any
    friendly pad refuels as well as repairs.
  • Cargo can be broken. A set-down harder than about 1.6 m/s starts damaging the
    load, reaching total at the speed that would break the aircraft — so there is a
    real band of arrivals the airframe walks away from and the freight does not.
    Only what arrives intact counts towards your record.

Fixed

  • Rain looked like lit sticks from the air. The streaks were drawn unshaded at
    a fixed near-white, so in the one condition rain exists in — a storm, which takes
    the sunlight down to a sixth and the shadows to nothing — everything else in
    frame went dark and the rain alone stayed bright. It is now lit like the rest of
    the world, and thins out with altitude, where the weather is carried by the far
    curtain and the wet ground rather than by drops a metre from the eye.
  • The vertical-speed readout and the rotor-speed number were drawn on the same row
    of the instrument cluster and ran into each other.

Changed

  • The pilot's guide is shot against this build. Its screenshots were from the
    first commit in June, and the HUD they showed — and the prose describing it —
    had drifted two months out of date.

Downloads

Platform File Notes
macOS Rotorwash-macOS.zip Universal — Apple Silicon and Intel. macOS 11+.
Windows Rotorwash-Windows.zip 64-bit.

Neither build is code-signed, so your OS will complain the first time. On macOS,
open System Settings → Privacy & Security and click Open Anyway; on
Windows, choose More info → Run anyway.

Full changelog: CHANGELOG.md