Research and implement authored liquid types in PRK.Client
Outcome Replace PRK.Client's single generic water presentation with data-driven authored liquid types, including verified water, lava, acid/toxic and special Shadowlands variants. First establish the actual type/material mapping from original client behavior and installed resources; do not invent a closed list from hazard names. Active checkout: /home/mathias/Projects/engines, branch feature/water-types, created from local develop aca198056. User requested moving implementation to the main checkout to retain local resources. All15 tracked changes and14 new files were transferred byte-for-byte. Implementation and runtime fixes are now committed as e40e085f6 (feature: Add authored liquid presentations); active checkout is clean. Final isolated client build passed with zero warnings/errors, and all10 changed/new fragment shadow variants compiled and passed SPIR-V validation. Original /home/mathias/Projects/worktrees/engines-water-types is detached and retains a safety copy; transfer backup /tmp/prk-water-checkout-move-96px34_o. Main-checkout run-client-dev.sh --build-only passes with zero warnings/errors. No GUI launch. User completed visual review and accepted all currently rendered liquid appearances. Differences in tinted water colours are accepted as intentional consequences of the modern water shader; no legacy visual parity or further colour tuning is required. Nonvisual integration/lifetime gates remain separate. Behavior and scope Authoritative asset corpus, explicitly selected by the user: /home/mathias/Downloads/linux-x64 (1)/client/cd_image/rdb.db and that client's accompanying tweaks/resources. Do not use the old native/ctree RDB for coverage, fixture selection or asset availability. Original AO binaries remain behavioral research evidence only. PRK resource baseline main-0.7.2; verified SQLite size 2148802560 bytes and SHA256 b5a0198f74d130eb1194220ee113c2e7f6e2cd32a38fa8f85e06de4610668e68, matching resource-baseline.json. Read-only revalidation confirms the same complete decoded body inventory as the previously inspected repository SQLite: 601 playfields, 6167 bodies, zero finite-geometry/index/prefix diagnostic failures; required liquid texture name mappings/payloads and liquid-damage records also match. The exploratory old native/ctree census is excluded from conclusions and is not a validation dependency. Each liquid body retains its authored identifier and resolves to verified visual properties/assets, surface behavior and submerged-camera presentation. Ordinary water remains supported across existing quality settings. Different liquid types may coexist in one playfield. Surface queries expose body identity/type as well as height and authored depth for camera-medium selection. Keep the descriptor reusable for future consumers, but do not implement locomotion/swimming/audio integration in this task. Define a safe explicit fallback and diagnostics for unrecognized/missing data. Keep damage and Brink teleport authority on the server; no client hazard simulation. User-confirmed exclusions: recovering original semantic names is not a requirement; use numeric IDs with descriptive labels without claiming original names. Damage-table research/parsing, server hazards and damage/teleport changes are out of scope. Swimming is currently unimplemented and is explicitly excluded, including eligibility, entry/exit, movement physics and swimming audio. Earlier damage/locomotion research below is retained only as evidence for separate future work, not an acceptance gate. Swimming audio remains with tsk_832s5g445n; no integration dependency on that task. Coordinate shader/composite contracts with HDR task tsk_3zyv3ddcyc without requiring a renderer rewrite or native HDR output. Evidence and reuse - RdbPlayfieldResource retains a packed liquid descriptor in WaterData.Unknown. Native rendering and collision paths confirm low five bits select the variant, bit zero distinguishes normal versus fall/decorative geometry, and upper bits encode depth in tenths (zero uses a 100000-unit sentinel). Preserve the raw serialized field and expose a shared decoded representation; do not compare the complete word to a plain type enum. - Anarchy/Playfield.cs LoadWater ignores that identifier and shares one material, normal texture and water_fragment.glsl across all bodies. RendererVulkan and composite_fragment.glsl use global fixed underwater murk. Extend existing MaterialBuilder, material identity/batching, texture loading, liquid render list, reflection targets and resource disposal rather than building parallel liquid renderers. - WaterSurfaces currently stores only geometry and returns the first surface height above a probe. Extend its existing triangle-based query with typed body context, authored depth and collision eligibility; exclude fall/decorative surfaces from ordinary immersion and do not treat bounded pools as infinitely deep. Native queries use ordered triangle selection with strict edge tests and a surface-minus-depth lower bound; test and explicitly document any intentional boundary/overlap improvements rather than invent nearest-surface parity. Address malformed indices/nonfinite data, winding, overlaps and sloped bodies. - Include room-local WaterData, not only GlobalWaterData. The installed census found 278 room-local bodies. Reuse Playfield.RoomPlacement, RoomInstance and AcgRoomAssembler's resolved placements for rotation, translation and floor offsets; preserve template ownership, avoid double transforms, and keep room visuals and queries in the same world coordinates. - Server WaterCollisionPfSystem.GetWaterType compares complete raw words and collapses visual variants into Water/Lava/Acid/Brink/Unknown. Native evidence shows this is not a suitable client classifier: 338 is variant 18 with depth 1, and 960 is variant 0 with depth 3. Introduce/reuse a neutral shared descriptor decoder without a client dependency on PlayfieldEngine. Keep exact visual variant, render recipe, collision eligibility and hazard category separate. Any server classifier correction requires separate explicit regression coverage/scope; do not silently change server damage or Brink teleport behavior as part of client visuals. - Installed cd_image/twk/Tweak_Liquid_Default.txt and Tweak_Liquid_Shadowlands.txt include ProgressiveLava; Shadowlands declares star/mist textures and scrolling parameters. Tweak_ProgressiveLava.txt describes animated authored texture/render-target behavior. Reuse existing Sky tweak parsing/evaluation and TextureLoader where applicable, not a second parser. Verify native dispatch and resource selection before translating these recipes to modern C# and Vulkan. Import original binaries plus neighboring dependencies through ghidra_container before any Ghidra analysis; keep assets and native research details out of production code. - Tools/DumpPfDynels has a water-column diagnostic printing raw types and surface heights. Extend/reuse for installed-data census and representative locations. Existing WaterSurfacesTests, WaterReflectionTests, material/render-list/order tests, shader variants and uniform layout tests provide regression foundations. Acceptance criteria - A per-variant visual matrix maps native-supported variants 0..19, each observed packed descriptor and safe handling for slots 20..31. Record exact variant/pair, native render path, assets/tweaks, tint/alpha, animation, lighting/emission, blend/depth/cull policy, fall behavior, camera-volume eligibility/depth, submerged-camera treatment, corpus presence and representative placed-world fixtures. Each conclusion has its evidence/confidence; numeric/descriptive labels are sufficient, and unobserved paired variants are not marked visually verified. Required mapping is visual behavior and asset selection, not recovery of original semantic names, damage rules or swimming behavior. - Body identity survives loading and queries; mixed types render independently with correct verified texture/animation, lighting/emission, opacity/reflection/refraction and submerged treatment. Non-water liquids do not automatically inherit blue-water foam/reflections/murk. Unverified properties are not guessed as parity. - Unknown identifiers, missing textures/tweaks, malformed geometry, empty playfields, shared edges, overlapping/sloped surfaces and zoning are handled safely and deterministically. Rendering and camera queries agree on the selected body; GPU resources are released through existing owners. - Existing ordinary-water quality, reflections, draw ordering and client/audio contracts remain intact. Client/shader builds and focused/full relevant regression tests pass; installed-data checks and available Vulkan validation cover representative variants and lifetimes. User clarification: original-client visual parity/comparison is not required. Preserve authored design through verified mapping into good-looking modern recipes; modern in-scene visual acceptance remains required and cannot be inferred from unit tests. No GUI launch or screenshot capture without user permission; unavailable platform/visual gates are reported. Confirmed planning research: liquid discrimination - Original binaries imported together through ghidra_container into /data/inbox before analysis. SHA256: Gamecode 654969a6b65946cb161f0e60aed8589260fc5eca1795488f66bb56f8fff73726; DisplaySystem d4dd19f3277d70ff5fc0146b5474e8c5abdd55f45e81288fe018192669046c91; N3 8c019efd72d547879a06585b69147ab1546b9617a2fce090e5863791aec8b0bb. - Gamecode 0x100b84b9 (outdoor batch) and 0x100b304d (transformed room triangle) pass raw & 31 plus (raw >> 5)/10 to VisualWater.Create. Zero depth becomes 100000. Raw disassembly at 0x100b854e confirms the mask, shift and division. N3 0x1000b0d1 independently decodes collision records and rejects odd variants; it uses an unsigned upper-bit shift, while Gamecode uses signed shift. All observed corpus values are nonnegative; define defensive handling for unobserved high-bit inputs explicitly. - DisplaySystem 0x1003ad24 tests variant bit zero and shapes sloped/fall geometry. 0x1003a0d6 registers collision info only for even variants and dispatches visual groups separately from variant identity. 0x10039b5a stores decoded variant/depth; N3 GetLiquidSurfaceHeight 0x1001ab68 checks the returned surface minus depth against the probe/cutoff. This establishes bounded volumes, not just texture selection. - Confirmed dispatch: 0/1, 4/5, 8/9, 10/11 and 12/13 use the ordinary-water group but have distinct packed vertex tints/alpha. Preserve these variants instead of collapsing them into one blue preset. Names for these tint variants are not recovered and should remain numeric/neutral rather than guessed sewage/mud labels. - 2/3 dispatch to lava; 6/7 to toxic liquid. Initialization 0x100406d3 loads water5.png, env_lava2.png and toxicwater.png into corresponding materials. Lava render 0x1003c356 uses multiple animated passes including water5 and env_lava2; toxic render 0x1003bebf has its own UV animation/material. These are not mere recolors of the current blue-water shader. Exact legacy blend/state and progressive-render-target translation remain to finish. - 14/15 dispatch to the special star-water group. Renderer 0x1003d1b9 draws that group's stars and two scrolling mist textures through LiquidTweaks StarWater_* settings, corroborated by installed Tweak_Liquid_Shadowlands.txt. 16/17 skip surface triangle submission in 0x1003a0d6; even 16 still registers collision info. Preserve a collision-only/no-surface distinction rather than drawing blue water over every Brink-related record. 18/19 use a separate water group/material and renderer 0x1003b411 with different UV scale/animation. Observed 18 occurs as raw 338 in Adonis Abyss; do not name it normal or star water by hazard inference. - Read-only census revalidated against the user-selected PRK SQLite at /home/mathias/Downloads/linux-x64 (1)/client/cd_image/rdb.db (matching the earlier repository SQLite liquid inventory), following existing RdbPlayfieldResource framing through room/global water: 601 playfield records, zero prefix parse failures, 6167 bodies (5889 global, 278 room-local). Decoded variant totals: 0=2994, 1=402, 2=1424, 3=110, 4=190, 5=9, 6=76, 8=41, 9=6, 10=477, 11=52, 14=142, 16=233, 18=11. No observed 7/12/13/15/17/19; native support is separate from corpus presence. Packed values beyond 31 include 160,166,320,326,338,960. This is the supplied SQLite snapshot, not a claim of current live AO or complete RDB format validation. - Representative fixtures: toxic 6 in Smuggler's Rearranged (125); lava 2 in Camelot (120) and lava-fall 3 in Avalon (505); brown-tinted water 8 and fall 9 in Omni1 Entertainment (705); star-water 14 in Elysium_pre-split (4524); collision-only 16 in Jobe Research (4001); special bounded water raw 338 in Adonis Abyss (4873), e.g. vertex (2021.38,106,516.72). Room examples are local template coordinates, not teleport destinations. Identify triangle-interior/world-space comparison positions before visual tests. Further verified variant behavior - Reproducible read-only diagnostic artifacts are outside the repository at /tmp/prk-liquid-research/census.py and prk-client-census.json, now configured for the user-selected PRK SQLite. census.json retains the earlier repository-SQLite result for comparison; exploratory native-census artifacts are superseded and must not drive this work. The census now validates finite vertices and triangle indices, records room placement metadata, per-variant playfield distributions and nondegenerate XZ triangle-interior centroids. All 601 records still yield 6167 bodies without a diagnostic failure. Centroids establish surface coverage, not safe teleport/spawn locations, visible accessibility or original-client comparison. Native constructor-state extraction and its captured disassembly are in native_states.py, native-states.json and native-state-constructor.asm in the same scratch directory. - Exact surface vertex ARGB from DisplaySystem 0x1003a0d6: 0/1=CC00186A; 2/3=FFFF0000; 4/5=F21F3A21; 6/7=F727FC05; 8/9=ED663B1B; 10/11=9933997F; 12/13=CCC8E6FF; 14/15=FFFFFFFF; 18/19=33349DFA. These are authored native colors/alpha, not modern shader final colors or proof of sewage/mud/ice names. 16/17 have no submitted surface color because that path returns without a draw. - Camera Gamecode 0x100b77cb and 0x100b79f7 both switch on exact liquid variant. AddFog RGB/weight presets: 0/1,12/13,18/19=(0.1,0.3,0.25;0.99); lava 2/3=(1,0,0;0.99); dark-green 4/5=(0.062,0.114,0.065;1); toxic 6/7=(0.154,0.991,0.02;1); brown 8/9=(0.2,0.1175,0.0545;0.99); teal 10/11=(0.1,0.3,0.25;0.98). 14..17 bypass these explicit AddFog presets but still call CameraIsUnderWater. DisplaySystem AddFog 0x1005820c blends accumulated fog colors by the supplied weights and retains the maximum weight; do not mislabel these weights as Beer-Lambert density or directly copy them into PRK's exponential murk alpha. CameraIsUnderWater 0x10039edd sets a flag also used by liquid frame processing for a vertical visual offset. Remaining fog-to-distance/lighting translation is a separate research step. - Native render policy confirmed with raw ECX receiver offsets in DisplaySystem constructor 0x1003db41: all explicit state blocks use cull-none. Ordinary water 0x1003b5e9 uses two passes/state blocks +4D0 and +554; +4D0 enables specular and blending, while +554 explicitly uses inverse-source-alpha/source-alpha blending. Group 18/19 renderer 0x1003b411 validates those blocks but draws only the first, uses its own blue-gray material and XZ UV scale four times the ordinary-water scale, without ordinary water's scrolling/two-pass animation. Keep this recipe separately configurable even though it shares underlying assets. - Toxic renderer 0x1003bebf uses +65C/+6E0 with lighting disabled and an animated secondary inverse-alpha pass. Lava 0x1003c356 uses +764/+7E8/+86C with lighting disabled, animated UVs and multiple texture stages; first two explicitly use source-alpha/inverse-source-alpha and the last the reversed pair. Star-water 0x1003d1b9 uses +32C/+3B0, stars projection plus two authored scrolling mist layers; mist explicitly blends ONE / INVERSE_SOURCE_COLOR with lighting disabled. State inheritance/default depth behavior is not fully resolved; absence of a setter is not proof that depth writes are enabled/disabled. No unnecessary reflection/refraction or progressive render target should be inferred from these paths. - RdbNamedResourceResolver already supplies immutable installation-specific name indexes and is the right reusable asset resolver. Read-only RdbInfoObject record 1 confirms texture type 1010004 IDs and present payloads: stars01.png=6325, water5.png=6329, toxicwater.png=14465, env_lava2.png=119187, smokefield.png=162574, edge_gradient.png=200949. Resolve by authored name at runtime, not these snapshot-specific numeric constants. No game assets were exported/committed. The name index contains 12 trailing bytes beyond existing declared-type parsing, which were not interpreted. There are additional liquid-named mesh resources; do not assume their material/render paths are WaterData variants without tracing them. - Newly discovered separate authored liquid-damage RDB type 1000031: records 0..13 exist in this snapshot with two leading words: 0/1=(0,1),2/3=(25,1),4/5=(0,1),6=(3,3),7=(4,3),8/9=(1,10),10/11=(0,1),12/13=(20,2). Native Gamecode 0x100c6872 attempts to load IDs 0..19 into a separate table; 0x100c4ce0 reads two integers. Their units, damage-kind meanings and effective runtime consumers are not yet established, so these are evidence of distinct authored gameplay metadata, not permission to change server hazards or assert damage ticks. Existing RdbLiquidDamage has no implemented parser. In particular, 8/9 and unobserved 12/13 cannot be assumed harmless because they share water shaders. No authored damage records 14..19 were found in this snapshot; that does not disprove Brink behavior. - Gameplay audio raw disassembly 0x1004540b..0x10045462 reads actual Vehicle liquid depth/type: positive depth overrides terrain material, exact type 2 selects game material 10, type 6 selects 11, others select shallow/deep water materials 8/9 at depth 0.35. This is an audio classification rule, not proof of swimming eligibility, and must be handed to tsk_832s5g445n rather than implemented as a separate audio system here. N3 room/tile surface adapters 0x10013ee6/0x10018f0c carry liquid variant through LiquidMediumData and raise the closest-point floor to surface minus 1.2 when deeper, with authored depth cutoff checked upstream. Original movement constraints remain separate from shader behavior. - Better fixture distribution: 4/5 are especially represented in Crypt of Home and western Elysium; 8/9 in Omni1 Entertainment; 10/11 in Broken Shores/4 Holes/Andromeda; 14 only Elysium_pre-split and Central Elysium in this corpus; 16 across multiple Shadowlands playfields; every observed 18 is raw 338 in Adonis Abyss. Example global surface-interior centroids: toxic 6 Wailing Wastes 551=(1254.8171,27,2580.3982), brown 8 Newland Desert 565=(1759.0284,49.2,1597.808), star 14 Elysium_pre-split 4524=(107.0781,0.6977,2563.2619), invisible 16 Jobe Research 4001=(1029.0758,6.5004,1014.0624), bounded 18 Adonis Abyss 4873=(1890.6637,105.9982,238.5944). Original client scene checks must prefer reachable live playfields over old pre-split/demo fixtures. Rendering research closure — implementation-facing static research complete - Final machine-readable map: /tmp/prk-liquid-research/visual-variant-map.json; generator build_visual_map.py. Maps all 20 native-supported slots individually, the 12 unsupported slots, observed counts/packed words and fixtures, assets with decoded format/size/alpha, rendering recipes, depth/material policy, camera presets and wave inputs. Read-only assertions verify 6167-body coverage, recipe completeness, packed descriptors 338/960/166, decorative camera-volume exclusion, invisible 16/17 and finite/bounded wave channels. Python compilation and asset decoding passed. These are diagnostic/reference checks, not production tests or visual parity. - Imported/analysed randy31.dll and FXS.dll with dependencies through ghidra_container; SHA256 randy31=9d9e0da25ac6f8c7ecdce7a47a2409575945966a29e3b77850f7c62033a04be3, FXS=381ad306d1889ebdcf11de3fc129d90c8802448df3465841ed5f3dc5206ed78a. Ghidra headless load_program/run_analysis was used after the GUI import tool refused; no host analyzeHeadless or original GUI launch. - Depth/default chain resolved: Randy initialization 0x10041ede raw instructions set Z enabled, ZWRITE=1, ZFUNC=LESS_EQUAL(4), SRCBLEND=SRC_ALPHA(5), DESTBLEND=INV_SRC_ALPHA(6), alpha testing off. StateBlob.Set/Reset 0x10025032/0x10024d1e save/restore only declared state changes. Material SetTexture 0x10040bec -> transparency policy 0x10040645 can independently enable blending and disable depth writes for alpha textures. RViewPort.SetMaterial/ResetMaterial 0x1004b199/0x1004b724 apply/restore that delta. Thus texture/material policy plus pass state determines effective depth, not MaterialRenderMode or a blanket liquids-are-transparent rule. Declare the effective policy explicitly in modern recipes instead of reproducing ambient legacy state leakage. - Selected SQLite textures independently decoded in memory: water5.png=256x256 RGB JPEG opaque grayscale; toxicwater.png=64x64 RGB JPEG opaque grayscale; stars01.png=256x256 RGB JPEG opaque; smokefield.png=256x256 RGB JPEG opaque; edge_gradient.png=256x256 RGB JPEG opaque; env_lava2.png=512x512 RGBA PNG with alpha range 60..255. Authored .png names do not specify actual encoding. Preserve alpha and use existing image decoder/name resolver. Ordinary/tinted water and toxic materials inherit baseline depth writes; lava's initial water-texture passes do too, while its final alpha-lava material disables writes. Stars/mist have no liquid material override and inherit the baseline. These are baseline native policies for the supplied asset types, not a claim about every external asset replacement or debugger configuration. - Native material layout resolved via RMaterial constructor 0x10041043 and InitD3DMaterial 0x100409c6: fields +2C diffuse, +38 specular, +44 ambient, +50 emissive, +60 power, +64 specular strength, +68 opacity. Liquid initialization 0x100406d3 supplies black diffuse/emissive; ambient varies by recipe (water black, lava white, toxic approx 0.153/0.988/0.019, star white, variant18 0.2/0.3/0.4). Ordinary water adds white specular, power 10, strength 0.505. WaterLightCurrent/Direction are authored environment inputs: 0x1005d589 resolves them, and 0x1003f3db -> 0x10059b98/0x10059b6e publishes the water light. Lava/toxic passes explicitly disable lighting; do not make them depend on ordinary water's daylight/specular shading. Star projected texture selection and unlit mist composition are independently configurable. - Native linear camera fog fully traced: Gamecode variant presets -> DisplaySystem AddFog 0x1005820c (weighted color accumulation/max weight) -> AddFogLightIntensity 0x10058390 (minimum light factor) -> process 0x10058443 -> 0x100790d4 -> Randy.SetFogParameters 0x10041876. Let N/F be submitted near/far clip planes and W the max accumulated weight times fog-mode scale: effective end is F - max(F-N-5,0)*W. Color is weighted accumulated RGB times the minimum fog-light factor. The renderer explicitly selects linear vertex fog mode 3 and table fog off; it does not use Beer-Lambert density. Fog-mode zero internally uses scale 0.1 while other modes use 1; do not mistake a 0.99 preset for density or assume a fixed visibility distance independent of view distance. Variants14..17 add no liquid-specific preset, so preserve existing environment fog rather than applying default blue murk. The underwater flag remains independent and native processing consumes it for a 0.2 vertical visual offset; any modern offset/boundary adaptation must be explicit rather than simulated as swimming. - Scene-sampling eligibility resolved: VisualLiquid's secondary virtual render path 0x1003d13a calls 0x1003b9c9 only for visual groups 0 (ordinary/tinted water) and 4 (18/19). It samples scene render target 2 with screen-projected disturbed UVs and angle/alpha handling; target sampling is suppressed by a separate native flag. Lava/toxic/star never inherit this path. This is a scene-sampling/refraction-style native pass, not evidence that every liquid requires PRK planar reflections. Keep modern reflection/refraction capabilities recipe-specific and preserve the existing water quality policy. - Wave generation resolved: Gamecode 0x100b05b4 advances phase by deltaSeconds*50 degrees modulo360 and computes 16 normalized cosine channels, using legacy 3.14/180 and offsets [90,180,0,270,45,135,350,50,100,0,45,270,36,112,350,50]. Integer channels truncate 255*normalizedChannel. DisplaySystem 0x1005cdcf publishes float channels as GAME.GameWaveCurve0..15. Native defaults: water XZ repeat scale40/wave amplitude0.036, toxic scale12/integer-wave divisor4096, lava scale8/wave amplitude0.36, variant18 scale160; lava projected texture scales75/250 (7.5/25 indoors). Water primary scroll uses elapsed delta at -0.01 cycles/second, star mist scroll uses its authored velocities; fall-flow phases additionally contain per-frame increments (water0.003, toxic/lava0.03 with wrap1000). Modern elapsed-time animation should intentionally translate those frame-dependent pieces, with a documented reference rate and deterministic tests, rather than claim native frame-rate independence. - Progressive lava resolved for this surface path: selected Tweak_ProgressiveLava.txt defines four legacy pre-render FillSquare objects writing e_BB_ProgressiveLava with jittered UVs, wave/weather-derived colors and different blends; FXS 0x1000c7c8/0x1000c827 maps that target to3. The native lava surface 0x1003c356 instead binds named water5/env_lava2 assets directly; its texture loader 0x100604da resolves a normal named RDB resource and the surface contains no SetRenderTargetAsTexture(3) call. Do not require a feedback render target simply because an included legacy tweak mentions one. Keep the authored target recipe as reference for an optional independent advanced lava animation, not a dependency of the verified surface recipe or a general FXS renderer rewrite. - Geometry/fall policy: the native odd-variant path normalizes triangle heights, shapes eligible falls with cubic easing and may spawn separate non-lava fall spray; lava is explicitly excluded from that spray branch (DisplaySystem 0x1003ad24/0x1003a72c/0x1003b1cd). Preserve real triangle normals and fall identity; do not use UnitY for all slopes or include fall geometry in camera volumes. Any fall spray should reuse existing effects ownership and be optional per recipe, never mandatory water effects on lava. - Reuse clarified: Skybox owns already assembled/evaluated EnvironmentScript and its frame clock; TweakScriptSource handles playfield/zone entry and fallback selection. Expose typed liquid/environment inputs from that owner and reuse RdbNamedResourceResolver/TextureLoader; do not tick a second parser or duplicate GameWaveCurve timing. Water light/profile publication and linear medium fog are integration work, not missing source behavior. Keep current modern water appearance while deriving variant-specific properties; explicit shader/animation customizations remain possible without changing descriptor identity. - Research completion boundary: numeric/descriptive labels suffice; damage, swimming and server hazards excluded. Source/data/rendering policy is now implementation-ready. Original-AO in-game comparison, GPU output validation and tuned modern visual acceptance remain implementation validation gates, not results inferred from disassembly. No original-client GUI launch, screenshots, production build/tests or repository edits occurred. Task stays open during planning; implementation checklist items remain unchecked. Modular architecture decision - Separate source identity/geometry, rendering recipe and immersion policy. A neutral immutable descriptor retains packed source, exact variant, depth and fall/collision flags; it does not dictate a shader or equate appearance with hazards/swimming. - Use a variant-to-recipe registry. Similar tinted water variants can share one water implementation with different authored parameters; lava, toxic and star-water can select independent shaders, texture layouts, animation and render-pass requirements. No giant water shader with a type switch, mandatory water inheritance, one class per numeric variant or duplicated playfield loaders. - Recipes may compose surface/fall passes and optional animated texture preparation. Declare needs for reflection, refraction/scene copies, depth sampling and additional targets explicitly; execute only requested work through existing frame scheduling/target/resource owners. Collision-only variants produce no surface material. Support independent camera-medium policy rather than using the surface shader as the authority on submerged effects. - Extend existing MaterialBuilder/material uniform publication and shader infrastructure. Material.Equals/GetHashCode currently include shader/textures/state and InstanceBuilder caches uniform buffers keyed by Material; include recipe-specific immutable parameter/resource identity wherever necessary so different recipes cannot accidentally batch or share uniforms. Publish time-varying animation through existing frame-safe mechanisms rather than mutating dictionary keys or allocating material resources every frame. - Keep game-resource mapping and authored defaults data-driven, but use readable typed C#/GLSL for specialized shading/animation. Do not build a universal shader scripting system, asset plugin framework or general render graph for this feature. Adding a new independent recipe must not require editing packed decoding, geometry queries or introducing a new world loader. - Validation covers recipe selection and capability isolation (lava does not request water reflections/foam merely because it is liquid), distinct material parameters/batching, mixed custom recipes, animated resource lifetimes and quality fallbacks. Keep visual-variant identity stable as modern appearance recipes are tuned. Plan refinement and remaining gates Finish the per-variant visual matrix and resolve rendering/camera-policy gaps. Numeric IDs with neutral descriptive labels are sufficient; do not delay implementation to recover original names. Implement shared packed decoding, retain exact variant plus depth and fall/collision flags, then resolve a visual recipe independently. Gather both global and placed room surfaces through existing indoor/ACG transforms. Extend tests for packed 338/960 and depth bottom bounds, non-colliding odd geometry, collision-only 16, mixed/tinted recipes and room placement. Do not infer submerged colors from visual group alone. Static rendering research is complete: effective baseline depth/material policy, linear fog composition/distance, special14/16 camera handling, scene-sampling eligibility, shared wave generator and direct versus legacy progressive lava paths are documented in the closure above and visual-variant-map.json. Proceed with modular implementation and reference tests; keep native quirks/intentional modern timing or boundary changes explicit. Original-AO in-game comparison and GPU/modern appearance checks remain validation gates against the selected PRK resource corpus, not completed research results or permission to use the old ctree dataset. Original names, damage and swimming are not blockers. No visual parity claim yet; gameplay parity is outside this task. Validation availability Graphics tests target .NET 10; no host dotnet executable was found on PATH. Docker is available with existing client SDK images (podman is not on PATH); use the supported SDK/container workflow. Current implementation milestone - Shared LiquidDescriptor preserves packed source/exact variant, bounded depth, fall and camera-volume eligibility; unsupported/high-bit inputs explicitly produce no surface/volume. No server hazard changes. - LiquidBody normalizes/sanitizes geometry once. Playfield gathers global and placed room/ACG bodies with the existing placement matrix. Rendering and typed WaterSurfaces camera queries share identity and coordinates. Queries retain authored overlap order, include shared edges with tolerance, exclude falls and respect authored bottom bounds. Invalid/nonfinite/degenerate triangles are omitted with diagnostics. - Immutable LiquidRecipeRegistry selects independent water/lava/toxic/star shaders and supports tested per-variant overrides without changing decoding or loading. Native tint/alpha is retained; water quality remains shared, variant18 has a separate static normal recipe, non-water recipes do not select water reflection/foam. Material hash/equality/uniform publication includes immutable liquid parameters. Named multi-layer texture arrays retain alpha and are owned/retired with playfield shaders; empty/invisible bodies allocate no surface resources. User-provided lava screenshot exposed an incorrect default TextureOptions.Texture type for multi-layer assets, causing DataSizeMismatch and a white neutral fallback/uniform yellow lava. CreateNamedLayers now explicitly selects ArrayTexture for lava/star/toxic resources. Isolated client build passes with zero warnings/errors; textured visual confirmation remains user-owned. - Camera medium uses per-variant authored RGB/weight with clip-dependent linear visibility in the composite before any HDR display transform. Variants14/16 retain environment fog. Skybox publishes shared verified wave channels and typed star mist scale/velocity/pass inputs through its existing evaluated environment/frame clock. No swimming/audio/hazard integration. - Shader validation, compilation and module loading now consistently prefer executable-owned shader sources and artifacts, with installation fallback only when an application source is absent. This prevents PRK_DATAROOT from selecting stale installed shaders or rejecting new liquid shaders. Includes and artifacts remain with the selected source; game assets still use PRK_DATAROOT. The regression reproduced FragmentShaderDoesNotExist before the fix and43 isolated path/liquid/layout/material/variant tests pass afterward against the selected installation. Validation uses isolated --artifacts-path output to avoid locks on user-running client files; containers use host UID/GID. Linux now prefers the working system glslc over the stale copied compiler requiring unavailable libglslang.so.15; Windows retains bundled-first lookup. User explicitly requested no further new tests: subsequent runtime fixes use build/shader validation and user-provided visual feedback. No client launch or session interruption. - User requested removal of added docs before committing: both Docs/Liquids JSON files were deleted and no documentation/assets are included in the commit. Static source mapping remains in this task's evidence and scratch /tmp/prk-liquid-research/visual-variant-map.json. Modern combined liquid passes intentionally use existing alpha-blended/depth-tested/no-depth-write policy rather than legacy multi-pass state inheritance. Water/toxic fall timing retains the explicit60Hz reference conversion; lava intentionally uses slower modern viscous timing based on user visual feedback. No legacy fall reshaping/spray or progressive render-target dependency is claimed. - Selected PRK_DATAROOT is /home/mathias/Downloads/linux-x64 (1)/client/. Tests mount installation read-only and overlay only tempPatch.rdbpatch with a disposable scratch copy because existing RDBController initialization writes it. Installed production decoder census verifies601 playfields/6167 bodies/278 room-local bodies. - Validation: focused liquid/material/layout tests27/27 pass; full graphics suite3500 passed/53 skipped/one unrelated terminal-mesh fixture failure, reproduced on unchanged base branch. Five changed/new fragment shaders compile for both shadow variants and all ten modules pass spirv-val. Eleven non-window Vulkan allocation/upload/target tests pass with disposable-container Vulkan libraries; RADV reports non-conformant testing-only support, so this is not liquid renderer/visual acceptance. Remaining acceptance work Runtime authored texture-name overrides and complete star projection/water-light inputs; capability-driven scene-copy scheduling; mixed-type integration checks and renderer zoning/lifetime validation. User-owned visual tuning of all current recipes is accepted. User-provided lava/reference images guide neutral dark crust over red flow. Current lava tuning retains64-world-unit crust repeat and independent authored-alpha coverage. User rejected the bright palette and overly slow fixed-direction animation: molten emission now has a roughly60% lower baseline plus subtle0.84..1.16 breathing; mixed-frequency bounded UV motion varies direction/speed, with faster surface movement than the previous slow scroll. Falls retain net downward flow; crust moves separately using slower world-space oscillations so its scale does not determine speed. This is modern artistic adaptation, not asserted native animation parity. Shader compilation and SPIR-V validation pass; user accepted the current lava appearance and animation as good enough. Keep these lava settings. User subsequently confirmed all liquid variants look fine, accepting tinted-water differences from the original modernized shader. Visual appearance tuning is closed for the current recipes; this does not certify remaining integration/lifetime gates. Toxic goo tuning in Perpetual Wastelands is active: user rejected both the initial excessive brightness and the0.04..0.38 dark reduction. Supplied reference/ours images guide current0.08..0.73 surface intensity plus(0.23,1,0.30) RGB appearance multiplier to reduce olive/yellow cast, while retaining source tint identity, animation and camera preset. This is modern appearance tuning, not a claimed native colour-space conversion. Shader compilation/SPIR-V validation pass; user accepted the current toxic goo appearance as pretty much spot on against the supplied reference. Keep these toxic settings alongside the previously accepted lava settings. Current settings are in the committed typed recipes/shaders; this task retains the relevant modern timing decisions. Existing headless Vulkan checks cover shared owners, not rendered liquid output. No GUI or screenshot was launched. User explicitly owns all visual verification: do not launch the client or capture/review screenshots for this task. Continue automated/non-GUI implementation and validation; leave modern appearance acceptance to the user. Task remains in_progress; recipe and final validation checklist items remain open. Coordinate merge of engine/material uniform and composite changes with HDR task tsk_3zyv3ddcyc: liquid output and fog remain scene-referred with no liquid shader tone mapping. Pull request Draft PR #2636 — https://git.project-rk.com/prk/engines/pulls/2636 Head feature/water-types -> base develop; commit e40e085f6. Branch is pushed with upstream origin/feature/water-types. Draft records the remaining nonvisual integration/lifetime follow-ups; opening it does not change task status.
Checklist
3 of 5 complete- Map native-supported and observed variants by visual behavior: assets, shading/animation/render state, fall geometry, camera-volume depth and submerged treatment, with evidence and comparison fixtures. Numeric/descriptive labels suffice; original names, damage and swimming are excluded.
- Verify shared packed-descriptor decoding, exact variant/depth and collision eligibility; preserve body identity through global and placed room/ACG surfaces with tested depth bounds, boundaries and malformed-data handling.
- Verify modular variant-to-recipe selection with independent water/lava/toxic/star shaders and animation/pass needs, shared infrastructure and correct material identity/batching; support mixed recipes, fall geometry and collision-only surfaces without inherited water effects.
- Verify per-liquid submerged-camera treatment using shared body identity, authored depth and decorative-surface exclusion, without implementing swimming, audio integration or client-side hazards.
- Pass client/shader builds, geometry/material/rendering regressions and installed-data checks; verify available Vulkan lifecycle and original-client visual comparisons, explicitly recording unavailable gates.
- Area
- Project Rubi-Ka
- Project
- engines
- Kind
- Feature
- Task ID
tsk_m6hneajjgj- Updated