Home

末日港湾

A 3D post-apocalyptic survival and building game I'm making in Godot. You scavenge the ruins, build up the harbor, and talk with the townsfolk around a campfire at night. The loop from opening to ending to free-form management is all in code, but a full playthrough hasn't been verified yet.

TYPE
3D post-apocalyptic survival building game
STATUS
The loop is coded; a full playthrough hasn't been verified
WHEN
since April 2026
BUILT WITH
Godot, GDScript, toon shading with outlines
WHO
just me

What it is

A day has three parts. Daylight is for scavenging the ruins, dusk for repairing facilities and restocking, night for talking by the campfire. I started out writing it as a "cozy" game, then changed the goal: the player faces scarcity, decay, and isolation first, and the little warmth by the campfire has to be earned.

In the code a day defaults to 10 minutes, with day, dusk, and night at 300, 180, and 120 seconds, and a time multiplier adjusts how fast it runs. The day-one tutorial goes in order: move, pick things up, build a workbench, wait for night, talk.

A ten-minute day and the five things that countONE DAY10 minutes at default speedDayscavenge, explore, push quests5 minDuskrepair, reinforce, stock up3 minNighttalk at the campfire2 minRECOVERYfive capped terms, weighted; the ending comes at 1.40Affinity210 across three people.25Buildings7 key kinds.18Quests9 done.10Lanterns8 lit.07Meals5 sharedAt 1 the ending plays, and you keep running the same world afterwards.

FIG. 1 · Ten minutes a day, five bars of progress

The durations and weights are parameters from the code. Each of the five caps out on its own; together they reach 1 and the ending starts.

How townsfolk change their minds

Ailin, Boshi, and Lao Chen each carry a set of beliefs between 0 and 1, their read on the world, on me, and on each other. Cognition refreshes about once every second of real time, taking in observations converted by rules. Gifts count, and so does the intent produced when you open the gift or building panels.

In the figure, b is a belief, o is an observation, p is the prediction computed from related beliefs, e = |p − o| is how far off this one was, and E is the average error over valid events. Beliefs update as b_new = clamp(b_old + 0.35(o − b_old), 0, 1), moving 35% of the gap toward o and staying inside 0 to 1. Feed in the same observation over and over and the belief settles near that value.

Besides beliefs there are two more quantities, surprise S and accumulated error F. Both update with incoming error and decay over time, and F is also nudged by the group mood around the campfire. The names sound like they come from a theory, but in the code they are just accumulators; I have not implemented the full theory.

Rule observations, prediction error and belief updatesRULE OBSERVATION oBELIEF GIVES pACCUMULATING ERRORGiftlove1.00like.75dislike.15day.35dusk.62night.90PhaseRules map events to 0–1Mean of relevant beliefsBoth p and o are bounded to 0–1poe = |p − o|Magnitude, without the signSkip events without a valid keyE is the mean valid-event errorS_new = .45 S_old + ES is surpriseRetain part of S, add this errorF_new = .72 F_old + E × stepF is a stored error totalstep is the nonnegative tick intervalS / F are not capped at 1Group emotion also affects FBELIEF UPDATEb_new = clamp(b_old + .35 × (o − b_old), 0, 1)b stays in 0–1 and moves 35% of the gap toward the observation.Rule parameters and a mechanism schematic; no measured trajectory or claim of a complete theory.

FIG. 2 · How observations change beliefs

A schematic; numbers like 0.35 are parameters from the code. It shows how the rule works, not curves recorded from the running game.

What to do today

There are two activity paths in the code. The old rule re-scores about every 0.75 seconds; the new candidate selection kicks in when day, dusk, and night switch over, taking at most 3 activities. The external language model interface is optional; if it isn't connected or is unavailable, the game falls back to local candidates.

The new path first predicts what each activity would do to the beliefs, squares the gap against each expectation, adds those up weighted by importance, subtracts the reduction in uncertainty, and calls the result G. It only counts the keys listed in the preference table, and expectations and weights stay within 0 to 1.

The smaller G is, the more likely an activity gets picked, with probability proportional to exp(−4G). When an activity ends, beliefs change by a fixed table. Scavenging, for example, lowers scarcity by 0.06 and route safety by 0.04. That's where it stops; the step that would compare the outcome against the prediction and write the error back hasn't been built.

Two activity paths and candidate scoringOLD RULE PATHrefresh ≈ .75 srule score + optional hintsexisting activitiesAT PHASE CHANGEday → dusk → nightOptional language-model candidatesNo interface: use local candidatesup to 3 candidatesThe paths keep separate activity entriesPREDICTED b′b′ = clamp(b + Δ, 0, 1)Δ is the predicted belief change01External Δ is limited to ±.2u(b) = 1 − 2|b − .5|u is an uncertainty proxy0.51CANDIDATE SCORE GG = Σ w (b′ − preferred)²Preference gap: larger is worse− .3 Σ w [u(b) − u(b′)]Uncertainty reduction lowers Gpreferred is the target; w is importancePr ∝ exp(−4G)Lower G is favored; sample oneExecute activityFixed outcome tableUpdate belief bFixed outcomes update beliefs; no prediction–outcome error comparison or writeback was found.Rule parameters and a mechanism schematic; runtime behavior was not verified.

FIG. 3 · How candidate activities get picked

A schematic; the numbers in the figure are code parameters. Comparing outcomes to predictions and writing the error back hasn't been built yet.

Where it stands