The save-dialog name field is a focused TMP_InputField, but global discrete
hotkeys kept reading the keyboard every frame, so typing a name like
"Checkers" cycled the pose (C) and shut the server down (K). Add a shared
TypingGuard.IsTyping check (focused TMP/legacy InputField) and short-circuit
the global handlers on it: pose cycle, network dev keys, mode switch, and
paint undo/redo. Movement was already gated out via ControlEnabled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The middle-mouse pan now persists when the button is released and only glides back to center on leaving paint mode. Pan sensitivity and max distance scale with the orbit zoom distance, so panning stays consistent on screen at any zoom and unchanged at the default zoom.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the BrushesSquare texture, replace the duplicated brush slots on the
player with it, show a brush preview in the paint HUD, and surface the
spawn-area tags and conversion reveal delay in the inspector.
Save and restore the emission intensity and the three camo knobs
(half-Lambert, half-Lambert brightness, specular dampen) with a named
painting. The .paint format moves to v3 with a small scalar block
between the header and the pixel maps; v2 paintings still load with
their painter values left untouched. Loading re-syncs the emission and
camo sliders, and peers pick the values up for free through the
existing owner-authoritative paint NetworkVariables.
Add a HUD reset (Lib_ResetHud button plus context-menu fallback) that
behaves as if the saved hud-layout file were deleted: it removes the
file, restores panels to their authored prefab positions, and resets
the brush values to the painter/material defaults captured before any
carry/restore runs.
Remove the alpha bar from HsvColorPicker so alpha is driven by
standalone Slider_AlbedoAlpha / Slider_EmissionAlpha bindings; the
picker now writes RGB only and the sliders write alpha only, with no
feedback between them. Add a generic SliderCurve component that reshapes
a slider's handle position into a non-linear value (with an inverse for
setting values back), and make the slider wiring curve-aware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix the in-place Conversion respawn snapping to the origin (0,0,0),
which surfaced when a hider was tagged mid wall-climb. The owner holds
authority over the NetworkTransform, so the server's Instantiate
position is ignored on the owner. Deliver the tagged world position
through a replicated _spawnOverride the server writes in OnNetworkSpawn,
and have the owner re-assert it before PlayerCharacterController's first
Update snapshots the kill-plane rescue target. This replaces the fragile
SpawnedMidSeek phase check.
Also hide a freshly converted hunter on other clients for a short
settle window (conversionRevealDelay) so nobody sees it pop in or
interpolate from the spawn frame; the shot hider is already despawned,
then the hunter cleanly materializes in its place.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a second match mode alongside Classic. In Conversion mode
hiders are invisible to each other (and to hunters during Hide), and a
hunter hit converts the hider into a full hunter in place instead of
sending them to the spectator freecam. The mode is replicated to all
clients through the existing phase named-message byte; visibility is
computed locally per client per mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drive the match through three server-authoritative phases (hide, seek,
resolution) with per-phase durations, broadcasting the active phase and
countdown to clients over the existing named-message infrastructure so
MatchManager stays a plain MonoBehaviour and Main keeps zero in-scene
network objects.
During hide, hider meshes are culled on hunter clients only (each client
computes visibility locally from role, phase and found state). During
seek, hunters shoot a single raycast from the first-person camera centre
(left click, cooldown-gated); a validated hit marks the hider found,
which hides its mesh for everyone and binds the owner to spectator
freecam. Seek ends early once every hider is found, then resolution runs
before returning to the lobby.
Force AlwaysAnimate on remote replicas so the animator keeps writing bone
transforms while the body is hidden or off-screen. The Paintman animator
is authored CullUpdateTransforms, so a culled renderer froze the pose on
the rest pose and the skinned collider baked that frozen pose, landing
replicated paint stamps on the wrong body location and leaving the
painting desynced after hiders were shown again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Choose the spawn area by player role: Hunter avatars look for a
HunterSpawnArea-tagged collider, Hiders for HiderSpawnArea. When the
role-specific area is missing, fall back to the shared SpawnArea tag, and
when that is absent too, keep the existing client-id ring placement.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Attach the HeroCutout component to the Main Camera with dialed-in cone values (radius 0.42, feather/depthFeather 0.15) and lower the deoccluder MinimumDistanceFromTarget to 0.05.
PickFromForeignMaterial read rend.sharedMaterial, which is always slot 0, so any renderer with several materials (one per submesh) always sampled the first material's values. Resolve the submesh from the hit triangle (collider triangleIndex, or the recovered render-mesh triangle) and read sharedMaterials[submesh] (clamped) instead. Single-material renderers stay on slot 0, byte-identical to before.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the multi-sample silhouette probe with one camera->target raycast: any world geometry in the line of sight arms the cut, a clear line keeps occluders solid. The shader carves a world-space cone (apex at the camera, radius growing toward the target) so the screen-space hole stays a constant size regardless of occluder depth, with a soft radial fade plus a symmetric axial fade around the target so geometry just behind the player feathers out instead of hard-cutting along a plane. Anything well past the target stays solid. Also suppresses the deoccluder pull-in so the camera holds its orbit distance and the cut takes over.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a geometry-based gate in PaintAtScreen that estimates how much of the
brush footprint lands on faces whose normals deviate sharply from the hit
surface. When that fraction exceeds the threshold the whole dab is
suppressed (no draw, emit, or undo mark) instead of smearing around the
edge. Backend-independent (no GPU readback) and winding-robust. Tunable via
cornerGuard / cornerGuardAngle / cornerGuardMaxFraction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move DefaultNetworkPrefabs.asset out of Assets/Settings back to the Assets root (where NGO expects it), and fix the PlaySceneWindow domain-reload toggle to read and write EnterPlayModeOptions correctly.
Add the HalfLambert, HalfLambert brightness and specular-damp sliders to the paint HUD panel, dial the camo values on Player.mat, and disable shadow casting on the Paintman renderer so the player's cast shadow no longer gives away its position.
Surface the PlayerCamoLit material knobs (HalfLambert, HalfLambert brightness, specular dampen) as material-backed properties on MeshPainter, guarded by an IsReady gate since the HUD binds before the material instance exists. Wire the three HUD sliders to them and seed the sliders from the live material once the painter is ready. Persist the values into the same HUD JSON as the brush settings (with a hasCamoValues flag so pre-camo save files never clobber the authored material) and carry them across a scene-switch respawn. Replicate them owner-authoritatively via a NetworkVariable in PlayerPaintSync, exactly like emission intensity, so remotes and late joiners show the same camo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuild the camo controls around hiding a painted player in a dark scene. _HalfLambert is now a two-segment curve (0 = no effect / URP Lit, 0.5 = classic half-lambert soft terminator, 1 = fully flat) and a companion _HalfLambertBrightness (-1..1) additively shifts the flattened diffuse so the body tone matches the scene. _SpecularDampen still scales down the direct highlight. The view-coupled _FormSoftness normal-bend is removed entirely (it flipped light/dark with the camera); all knobs are now term-based and view-independent, and stay byte-identical to URP/Lit at their no-op values.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hybrid foreign-object pipette previously always overwrote albedo with the lit on-screen pixel. Gate that behind a new serialized pipetteAlbedoFromScreen flag (default off): off reads albedo texel-exact from the material like the other channels, on takes it from the rendered, fully-lit pixel. The data channels still always come from the material.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Heavy authoring pass on The Room scene plus lighting/render tuning across
scenes: Lobby gets lower ambient/reflection intensity, softer shadow
strength and a baked lighting data asset; Main and Test Map drop their
lighting data/settings links. Replace the living-room Glass materials with
reworked versions under Assets/Materials, update BrightDaySettings, and set
hunterCount to 0 in Main.
Move editor scripts to Assets/Scripts/Editor (PlaySceneWindow, SceneCollection),
move DefaultNetworkPrefabs and the Main Camera custom blends asset into
Assets/Settings, and shelve the unused HeroCutout shader/include under
Assets/Unused. Rename the Chameleon background texture, delete the obsolete
DefaultParentForEditor script and New Lighting Settings asset, add a The Room
SceneCollection asset, and drop the redundant Assets menu entry from the
StraightenKnees tool.
Custom/PlayerCamoLit is a URP 17 Lit clone whose ForwardLit pass routes
through PlayerCamo.hlsl - a faithful copy of UniversalFragmentPBR that
delegates all GI/APV/shadow/cluster-light helpers unchanged and only swaps
the three light calls for a camo variant. Three sliders flatten the form
cues that give a painted player away: _FormSoftness bends the per-pixel
normal toward the camera (softens diffuse + specular + reflections),
_HalfLambert lifts the diffuse shadow side, _SpecularDampen scales down the
direct highlight. All sliders at 0 are byte-identical to URP/Lit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On a foreign object the pipette now reads the data channels
(metallic/smoothness/occlusion/emission) texel-exact from the material
while taking albedo from the rendered, fully-lit on-screen pixel - so the
picked colour honours baked lightmaps/ambient/SSAO while keeping the
material's PBR fidelity. Falls back to a pure screen pick when no readable
UV is available. Pull the render+readback out of PickFromScreen into a
shared TrySampleScreenColor.
The pipette now also arms on Ctrl (in addition to Shift), and Ctrl+wheel
resizes the brush; the orbit camera ignores the wheel while either modifier
is held so it does not zoom and resize at once.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When leaving first person for an orbit mode (Paint / Player Control), snap
the orbit axes to Camera.main's current view direction instead of restoring
the orbit's last-used angle. With WorldSpace binding + Sphere orbit + a
centered composer the camera look rotation is exactly
Euler(VerticalAxis, HorizontalAxis, 0), so the inverse mapping is exact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New The Room scene built from the Barking_Dog living room asset: prefab
variants under Assets/Prefabs/3D Living Room, an Environment Light prefab and
baked lighting settings. Point MatchManager at The Room (was Test Map) and
swap it into the build settings scene list. Floor_Wood material and the
living room meta files are reserialized by the URP/Unity importer. Main.unity
also carries the inspector-pinned defaults of the orbit-zoom and hunter-count
fields (matching the C# defaults, no behavior change).
The orbit radius (min/max/default and per-notch step) was in absolute world
units and ignored the player's localScale, so a small Hider started too far
out and a large Hunter too close and could not zoom out far enough. Multiply
range, step and default radius by the follow target's lossyScale (PlayerScaler
keeps the body uniformly scaled), making the inspector fields size-1 design
values that frame every role body-relative. Re-clamp every frame so a runtime
grow/shrink stays in range. GameModeManager.BindLocalPlayer calls ResetZoom
after rebinding so each new avatar starts at its scaled default zoom.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
StartMatch rolled exactly one random Hunter. Add a serialized hunterCount
field (default 1, [Min(0)], inspector-tunable) and roll N distinct random
Hunters via a partial Fisher-Yates on the client-id list, clamped to the
player count: 0 = all Hiders, >= player count = all Hunters. The match
sequence now carries a HashSet of hunter ids instead of a single id.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hit.textureCoord is only filled for non-convex MeshColliders; on a convex
MeshCollider (or Box/Sphere/Capsule) it returns Vector2.zero, so the pick
sampled the foreign material's base map at texel (0,0) - a constant, wrong
color ("base map ignored"). Now the UV is recovered from the renderer mesh
itself (camera ray vs triangles, Moeller-Trumbore, barycentric UV) for
convex/primitive colliders, with the screen-pixel pick as fallback when the
mesh is not readable. The fast non-convex path is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reveal the player when geometry sits between the orbit camera and the
character: a screen-space circular mask in a URP/Lit clone (HeroCutoutLit)
clips occluders in front of the player, depth-gated so only geometry nearer
than the hero is cut. The cutout lives outside UnityPerMaterial (SRP batcher
intact) and is a no-op when disarmed, so occluders look identical to URP/Lit.
A runtime driver (HeroCutout.cs on Main/GameManager) feeds the player's
screen position, radius, feather and depth into global shader properties and
only arms while the third-person orbit vcam is live; it disarms in
first-person, free-cam and the pause menu. Applied to the lobby walls via a
dedicated occluder material (floor left solid). Shipped but shelved for now.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hold Shift to momentarily enable the pipette (restores prior state on
release) and Shift+Scroll to resize the brush. The orbit zoom ignores the
wheel while Shift is held so it does not zoom and resize at once. The HUD
brush-size slider follows live via a BrushSizeChanged event, and the brush
preview now stays visible while Shift is held (drawn before the pipette
early-return) so the new size is visible while scrubbing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
NetworkBootstrap now destroys itself outside the editor. Camera FOV/background
tweaks in Main, lobby light intensity, and paint HUD panel font/color polish.
SetPaused no longer Exits the active mode, so the First-Person/Free-Cam rig
keeps its CinemachineCamera priority and the view stays frozen in place instead
of blending back to the orbit camera. Pausing now only disables each rig's
per-frame look input (dropping the controller's enabled flag) and parks the
orbit rig in Disabled to free the cursor; resuming re-enters the same mode whose
snap-to-current-view is a no-op, so there is no jump on close either.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Main scene gets a full-rect Background image (Chameleon Background 1) in
the menu UI and re-enables a UI component. Lobby scene receives extensive
authoring (sky, lighting, props). New Lobby Sky and White Glow materials;
Yellow Glow emission toned down from ~89 to 64.