Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1362,9 +1362,12 @@ Three traps that slice paid for, all transferable:
guard, not the correctness gate.

**One TypeScript file in a ported directory was ported for a reason that is not
obvious.** `cliffConnections.ts` has **zero `src/` consumers** - only 23
investigation specs import it - so that #84's cliff investigation can be run
against the engine.
obvious.** `cliffConnections.ts` has **zero consumers of any kind** since #360 deleted the
23 investigation specs that imported it. It is kept as the human-readable
reference `crates/fmw-noise/src/cliffs/connections.rs` cites as its source, so
that #84's cliff investigation can still be run against the engine. The
type-checker still covers it, because `tsconfig.json` includes `src/**/*` by
glob rather than by reachability.

#### The three tiers, and what each one cannot see

Expand Down
2 changes: 1 addition & 1 deletion src/model/elevationPreviewCtx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Point } from "../noise/distanceFromNearestPoint";
import type { VulcanusResourceControls } from "../noise/eval/ctx";
import { readMapType } from "./mapType";
import { readResourceControls, readVulcanusResourceControls } from "./resourceReads";
import type { ResourceControlLevers } from "../noise/resources/resolveResource";
import type { ResourceControlLevers } from "../noise/resources/resourceCatalog";
import type { Preset } from "./types";
import { ENEMY_CONTROL_NAME, type EnemyControls } from "../noise/enemies/enemyCatalog";
import {
Expand Down
2 changes: 1 addition & 1 deletion src/model/resourceReads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
import type { AutoplaceSetting } from "./types";
import type { VulcanusResourceControls, VulcanusResourceLevers } from "../noise/eval/ctx";
import type { ResourceControlLevers } from "../noise/resources/resolveResource";
import type { ResourceControlLevers } from "../noise/resources/resourceCatalog";
import { RESOURCE_CATALOG } from "../noise/resources/resourceCatalog";

const DEFAULT_LEVERS: ResourceControlLevers = { frequency: 1, size: 1, richness: 1 };
Expand Down
18 changes: 18 additions & 0 deletions src/noise/cliffs/cliffCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,21 @@ export function cliffCollisionTileBox(
bottom: Math.floor(centerY + b),
};
}

/**
* The Vulcanus tiles whose `CollisionMask` shares a layer with the cliff's, so a
* cliff whose collision box touches one is never placed.
*
* `tile_collision_masks.lava()` sets `water_tile = true` and the cliff mask
* holds `water_tile`; no other Vulcanus tile does. Notably
* `volcanic-jagged-ground` - the tile the ore patches paint, which the Lua
* itself labels "CLIFF TILE" - is `tile_collision_masks.ground()`, which the
* cliff mask does not touch, so ore does NOT exclude cliffs. That distinction is
* the whole reason the earlier ore-separation work correctly found no exclusion
* rule while this one exists.
*
* Lives here rather than beside the Vulcanus renderer because that renderer is
* deleted by #227 and this rule is not. The Rust carries the same pair inlined
* at `crates/fmw-noise/src/cliffs/vulcanus_fields.rs:218`.
*/
export const VULCANUS_CLIFF_BLOCKING_TILES: ReadonlySet<string> = new Set(["lava", "lava-hot"]);
26 changes: 21 additions & 5 deletions src/noise/cliffs/cliffConnections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@
* | `oppositeSide` | immediate `0x01000302` | `N<->S`, `E<->W` |
* | `destroyEnd` | 4 jump tables under `0x102cfc9db` | `side -> none`, else destroy |
*
* `test/cliffConnections.spec.ts` re-derives all four from the names and asserts
* they match, so a transcription slip fails rather than shifts the model.
* `crates/fmw-noise/src/cliffs/connections.rs` re-derives all four from the names
* and asserts they match, so a transcription slip fails rather than shifts the
* model. The TypeScript spec that used to do this went with #227.
*/

import {
Expand All @@ -131,7 +132,22 @@ import {
CLIFF_GRID_SIZE,
CLIFF_ORIENTATION_NAMES,
} from "./cliffCatalog";
import type { PlacedCliffCell } from "./cliffPlacement";

/**
* A placed cliff: the cell centre, plus the 8-bit edge-crossing `code` it was
* placed by. The code is carried out rather than discarded because it is the
* only thing that names the cliff's ORIENTATION, and therefore its collision
* box - `cliffOrientationForCode(code)` in `cliffCatalog.ts`.
*
* Inlined from `cliffPlacement.ts`, which #227 deletes. This module is kept as
* the reference `crates/fmw-noise/src/cliffs/connections.rs` cites, so it must
* not import anything that deletion removes.
*/
export interface PlacedCliffCell {
readonly x: number;
readonly y: number;
readonly code: number;
}

/**
* `CellSide`, in the engine's enum order. Read off `getNeighborPosition`
Expand Down Expand Up @@ -310,8 +326,8 @@ export interface CliffConnectionOptions {
* `<= 0x31`, so during a real generation sweep a cliff pointing into a
* not-yet-generated chunk keeps its end, and this model destroys it. It is
* therefore an UPPER bound on how much the rule removes - see
* `test/cliffConnections.spec.ts`, which is what measures whether that bound is
* tight.
* `crates/fmw-noise/src/fixtures.rs`, which runs over the same fixture and is
* what measures whether that bound is tight.
*/
export function applyCliffConnections(
cells: readonly PlacedCliffCell[],
Expand Down
2 changes: 1 addition & 1 deletion src/noise/preview/elevationRenderRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { EnemyControls } from "../enemies/enemyCatalog";
import type { Planet } from "../../model/planets";
import { PLACEMENT_MARK_RADIUS_PX } from "../placement/placementRoll";
import { NAUVIS_MAX_STARTING_POINTS } from "../wasm/request";
import type { ResourceControlLevers } from "../resources/resolveResource";
import type { ResourceControlLevers } from "../resources/resourceCatalog";
import type { RockControls } from "../rocks/rockCatalog";
import { renderCliffs } from "./renderCliffs";
import { renderElevation } from "./renderElevation";
Expand Down
19 changes: 19 additions & 0 deletions src/noise/preview/palette.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Colours the map preview paints with, kept apart from the renderers so they
* outlive them. #227 deletes the TypeScript renderers these were declared in;
* the Rust engine now does the painting and these values are what the surviving
* parity specs grade its output against.
*
* Deliberately an independent copy of the Rust constants rather than a read of
* them - a spec that imported the engine's own numbers would assert nothing.
* The mirrors are `crates/fmw-wasm/src/render.rs:156` and `:159` for the
* elevation pair, and `crates/fmw-noise/src/trees/catalog.rs:69` for the tree
* colour. Rust stores RGB and writes the alpha separately.
*/

/** RGBA for water (elevation < 0) and land, as [r, g, b, a] byte tuples. */
export const WATER_RGBA: readonly [number, number, number, number] = [40, 90, 150, 255];
export const LAND_RGBA: readonly [number, number, number, number] = [70, 120, 60, 255];

/** `{0.19, 0.39, 0.19}` in 8-bit - utility-constants.lua:201. */
export const TREE_MAP_COLOR: readonly [number, number, number] = [48, 99, 48];
5 changes: 1 addition & 4 deletions src/noise/preview/renderElevation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { makeElevationLakes, type ElevationLakesParams } from "../expressions/elevationLakes";
import { makeElevationNauvis } from "../expressions/elevationNauvis";
import { makeElevationIsland } from "../expressions/elevationIsland";

/** RGBA for water (elevation < 0) and land, as [r, g, b, a] byte tuples. */
export const WATER_RGBA: [number, number, number, number] = [40, 90, 150, 255];
export const LAND_RGBA: [number, number, number, number] = [70, 120, 60, 255];
import { LAND_RGBA, WATER_RGBA } from "./palette";

export interface RenderElevationOptions {
/** Map seed (= map_seed / seed0). Callers resolve a null "random" seed first. */
Expand Down
8 changes: 2 additions & 6 deletions src/noise/preview/renderResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,9 @@ import {
makePlacementSet,
} from "../placement/placementRoll";
import type { PlacementCollisionBox } from "../placement/placementRoll";
import { RESOURCE_CATALOG } from "../resources/resourceCatalog";
import { RESOURCE_CATALOG, type ResourceControlLevers } from "../resources/resourceCatalog";
import { makeResourcePatches } from "../resources/resourcePatches";
import {
comparePriority,
makeResourceResolver,
type ResourceControlLevers,
} from "../resources/resolveResource";
import { comparePriority, makeResourceResolver } from "../resources/resolveResource";
import { makeTileResolver } from "../tiles/resolve";
import { paintMark } from "./renderCliffs";

Expand Down
3 changes: 1 addition & 2 deletions src/noise/preview/renderTrees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@
*/
import { WATER_TILE_COLORS } from "./renderResources";
import { makeTreeDensity, type TreeFieldParams } from "../trees/treeField";
import { TREE_MAP_COLOR } from "./palette";

/** `{0.19, 0.39, 0.19}` in 8-bit - utility-constants.lua:201. */
export const TREE_MAP_COLOR: readonly [number, number, number] = [48, 99, 48];
/** The alpha component of the same constant: fully-forested tiles blend at 40%. */
export const TREE_MAX_ALPHA = 0.4;

Expand Down
15 changes: 1 addition & 14 deletions src/noise/preview/renderVulcanusCliffs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import type { EvalCtxInput } from "../eval/ctx";
import { withCtxDefaults } from "../eval/ctx";
import { makeCliffPlacementFromFields } from "../cliffs/cliffPlacement";
import { VULCANUS_CLIFF_BLOCKING_TILES } from "../cliffs/cliffCatalog";
import {
VULCANUS_CLIFF_ELEVATION_0,
VULCANUS_CLIFF_ELEVATION_INTERVAL,
Expand All @@ -40,20 +41,6 @@ import {
makeVulcanusTileResolverFrom,
} from "../tiles/vulcanusCatalog";

/**
* The Vulcanus tiles whose `CollisionMask` shares a layer with the cliff's, so a
* cliff whose collision box touches one is never placed.
*
* `tile_collision_masks.lava()` sets `water_tile = true` and the cliff mask
* holds `water_tile`; no other Vulcanus tile does. Notably
* `volcanic-jagged-ground` - the tile the ore patches paint, which the Lua
* itself labels "CLIFF TILE" - is `tile_collision_masks.ground()`, which the
* cliff mask does not touch, so ore does NOT exclude cliffs. That distinction is
* the whole reason the earlier ore-separation work correctly found no exclusion
* rule while this one exists.
*/
export const VULCANUS_CLIFF_BLOCKING_TILES: ReadonlySet<string> = new Set(["lava", "lava-hot"]);

export interface RenderVulcanusCliffsOptions {
readonly seed0: number;
/** World tile at the top-left pixel. Default (0, 0). */
Expand Down
13 changes: 5 additions & 8 deletions src/noise/resources/resolveResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@
*/
import type { Point } from "../distanceFromNearestPoint";
import { makeResourcePatches, type ResourcePatches } from "./resourcePatches";
import { RESOURCE_CATALOG, type ResourceParams } from "./resourceCatalog";

/** control:<res>:frequency|size|richness levers for one resource. */
export interface ResourceControlLevers {
readonly frequency: number;
readonly size: number;
readonly richness: number;
}
import {
RESOURCE_CATALOG,
type ResourceControlLevers,
type ResourceParams,
} from "./resourceCatalog";

export interface ResourceResolverCtx {
readonly seed0: number;
Expand Down
7 changes: 7 additions & 0 deletions src/noise/resources/resourceCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ export interface ResourceParams {
*/
export type ResourcePlacement = "threshold" | "roll";

/** control:<res>:frequency|size|richness levers for one resource. */
export interface ResourceControlLevers {
readonly frequency: number;
readonly size: number;
readonly richness: number;
}

/** map_color (0..1) -> 0..255, rounded, matching the game's preview tint. */
function color255(r: number, g: number, b: number): readonly [number, number, number] {
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
Expand Down
11 changes: 11 additions & 0 deletions test/cliffCatalog.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vite-plus/test";
import {
CLIFF_MAP_COLOR,
VULCANUS_CLIFF_BLOCKING_TILES,
getModifiedElevationInterval,
getModifiedRichness,
isCliffPlaced,
Expand Down Expand Up @@ -46,3 +47,13 @@ describe("cliff orientation predicate", () => {
]);
});
});

// #360 deleted the block in cliffOreDirection.spec.ts that pinned this literal.
// The set survives and two specs still read it, so the value is re-pinned here.
// The Rust holds the same pair inlined twice, at
// `crates/fmw-noise/src/cliffs/vulcanus_fields.rs:218` and `fixtures.rs`.
describe("VULCANUS_CLIFF_BLOCKING_TILES", () => {
it("is exactly the two lava tiles", () => {
expect([...VULCANUS_CLIFF_BLOCKING_TILES].sort()).toEqual(["lava", "lava-hot"]);
});
});
2 changes: 1 addition & 1 deletion test/cliffOreLeverTileConfound.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vite-plus/test";

import lever from "./fixtures/oracle-vulcanus-tile-lever.seed123456.json";
import { VULCANUS_CLIFF_BLOCKING_TILES } from "../src/noise/preview/renderVulcanusCliffs";
import { VULCANUS_CLIFF_BLOCKING_TILES } from "../src/noise/cliffs/cliffCatalog";
import { makeVulcanusTileResolver } from "../src/noise/tiles/vulcanusCatalog";
import { DEFAULT_VULCANUS_RESOURCE_CONTROLS } from "../src/noise/eval/ctx";

Expand Down
2 changes: 1 addition & 1 deletion test/multisampleChannelAudit.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vite-plus/test";

import fixture from "./fixtures/oracle-vulcanus-tile-names.seed123456.json";
import { VULCANUS_CLIFF_BLOCKING_TILES } from "../src/noise/preview/renderVulcanusCliffs";
import { VULCANUS_CLIFF_BLOCKING_TILES } from "../src/noise/cliffs/cliffCatalog";
import {
makeVulcanusStack,
makeVulcanusTileResolverFrom,
Expand Down
2 changes: 1 addition & 1 deletion test/wasmElevationRenderParity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
runRenderRequest,
type ElevationRenderRequest,
} from "../src/noise/preview/elevationRenderRequest";
import { LAND_RGBA, WATER_RGBA } from "../src/noise/preview/renderElevation";
import { LAND_RGBA, WATER_RGBA } from "../src/noise/preview/palette";

/**
* Tier 3 for the ELEVATION view (#227): the Rust engine's elevation render
Expand Down
2 changes: 1 addition & 1 deletion test/wasmNauvisRenderParity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
runRenderRequest,
type ElevationRenderRequest,
} from "../src/noise/preview/elevationRenderRequest";
import { TREE_MAP_COLOR } from "../src/noise/preview/renderTrees";
import { TREE_MAP_COLOR } from "../src/noise/preview/palette";
import { planTiles } from "../src/noise/preview/tiling";

/**
Expand Down