feat(client): cutaway modes — corridor / bottom-walls-low / both, C-key live toggle (T-1088)
Live-session two-minds resolution: the trippy sightline corridor stays as a mode (candidate signature look); mode 1 drops any wall with a KNOWN non-wall tile on its camera-far side to the stub (X2 bottom-walls read — the player sees the room without walking to it); mode 2 combines. Bottom-wall flag rides MultiMesh INSTANCE_CUSTOM.r, updated on paint and when newly-learned floors promote camera-ward walls; camera-far directions derived from the live WorldRoot basis, never hand-derived. Default stays corridor pending the feel verdict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,11 @@ var _free_slots: Array = [[], []]
|
||||
var _state_colors: Array[Color] = []
|
||||
var _last_tick: int = -1
|
||||
var _capacity_warned := false
|
||||
## Cutaway mode: 0 = sightline corridor, 1 = bottom walls low, 2 = both.
|
||||
var cutaway_mode: int = 0
|
||||
## Camera-far sim-neighbor deltas for the bottom-wall rule (see
|
||||
## _compute_behind_dirs — derived from the live WorldRoot basis).
|
||||
var _behind_dirs: Array[Vector3i] = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -190,7 +195,12 @@ func _ready() -> void:
|
||||
_wall_material.set_shader_parameter("u_cut_height", SandboxConstants.CUT_HEIGHT)
|
||||
_wall_material.set_shader_parameter("u_corridor_half_w", SandboxConstants.CUT_CORRIDOR_HALF_W)
|
||||
_wall_material.set_shader_parameter("u_cut_band", SandboxConstants.CUT_BAND)
|
||||
_wall_mm = _make_layer("WallMM", wall_mesh, _wall_material)
|
||||
_wall_mm = _make_layer("WallMM", wall_mesh, _wall_material, true)
|
||||
# Camera-far neighbor directions for the bottom-wall rule — derived from the
|
||||
# actual WorldRoot basis at runtime (never hand-derived; the E/W-mirror
|
||||
# lesson). A wall is a "bottom wall" when a KNOWN non-wall tile sits on its
|
||||
# camera-far side. Deferred: global_transform is not final until tree-ready.
|
||||
_compute_behind_dirs.call_deferred()
|
||||
|
||||
|
||||
## Push the world->sim transform into the floor grid shader (deferred from
|
||||
@@ -227,13 +237,17 @@ func set_cutaway_char_pos(world_pos: Vector3, reach_m: float) -> void:
|
||||
_wall_material.set_shader_parameter("u_cut_reach", reach_m)
|
||||
|
||||
|
||||
func _make_layer(layer_name: String, mesh: Mesh, material: ShaderMaterial) -> MultiMeshInstance3D:
|
||||
func _make_layer(
|
||||
layer_name: String, mesh: Mesh, material: ShaderMaterial, with_custom := false
|
||||
) -> MultiMeshInstance3D:
|
||||
var mm := MultiMesh.new()
|
||||
# Format flags must be set before instance_count (buffer layout is fixed at
|
||||
# allocation). use_colors routes set_instance_color() to the shaders' COLOR
|
||||
# built-in, which multiplies albedo (§3.2).
|
||||
# built-in, which multiplies albedo (§3.2). use_custom_data (walls only)
|
||||
# carries the bottom-wall flag to INSTANCE_CUSTOM.r for the cutaway modes.
|
||||
mm.transform_format = MultiMesh.TRANSFORM_3D
|
||||
mm.use_colors = true
|
||||
mm.use_custom_data = with_custom
|
||||
mm.mesh = mesh
|
||||
mm.instance_count = INSTANCE_CAPACITY
|
||||
for i in INSTANCE_CAPACITY:
|
||||
@@ -267,6 +281,63 @@ func _paint_new(key: Vector3i) -> void:
|
||||
mm.set_instance_transform(slot, _tile_xform(key, kind))
|
||||
mm.set_instance_color(slot, _state_colors[store.state_of(key)])
|
||||
_slots[key] = Vector2i(kind, slot)
|
||||
if kind == Store.Kind.WALL:
|
||||
_refresh_bottom_flag(key)
|
||||
else:
|
||||
# A newly-learned floor can promote camera-ward walls to bottom walls.
|
||||
for d in _behind_dirs:
|
||||
_refresh_bottom_flag(key - d)
|
||||
|
||||
|
||||
## Cutaway mode (design amendment, live session 2026-07-06): 0 = sightline
|
||||
## corridor, 1 = bottom walls low, 2 = both. Pushed to the wall shader.
|
||||
func set_cutaway_mode(mode: int) -> void:
|
||||
cutaway_mode = mode % 3
|
||||
_wall_material.set_shader_parameter("u_cutaway_mode", cutaway_mode)
|
||||
print("GreyboxWorld: cutaway mode %d (%s)" % [
|
||||
cutaway_mode, ["sightline corridor", "bottom walls low", "both"][cutaway_mode]
|
||||
])
|
||||
|
||||
|
||||
# Dev affordance (same pattern as the camera's T tilt cycle): C cycles the
|
||||
# cutaway mode live so the corridor-vs-bottom-walls call is made by feel.
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey:
|
||||
var key := event as InputEventKey
|
||||
if key.pressed and not key.echo and key.physical_keycode == KEY_C:
|
||||
set_cutaway_mode(cutaway_mode + 1)
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
## World-space camera-far sim directions (as tile-key deltas): the wall face
|
||||
## pointing toward the camera hides the tile at key + behind_dir. Computed from
|
||||
## this node's global basis so the WorldRoot rotation can never desync it.
|
||||
func _compute_behind_dirs() -> void:
|
||||
_behind_dirs.clear()
|
||||
for d: Vector3i in [Vector3i(1, 0, 0), Vector3i(-1, 0, 0), Vector3i(0, 1, 0), Vector3i(0, -1, 0)]:
|
||||
# Sim x -> local X, sim y -> local Z (SandboxSpace convention).
|
||||
var world_dir := global_transform.basis * Vector3(float(d.x), 0.0, float(d.y))
|
||||
# Camera looks world -Z, so a camera-ward face points +Z; the tile it
|
||||
# hides lies OPPOSITE that face -> behind_dir = -d for camera-ward d.
|
||||
if world_dir.z > 0.01:
|
||||
_behind_dirs.append(-d)
|
||||
# Repaint flags for walls painted before the deferred computation ran.
|
||||
for key: Vector3i in _slots:
|
||||
if (_slots[key] as Vector2i).x == Store.Kind.WALL:
|
||||
_refresh_bottom_flag(key)
|
||||
|
||||
|
||||
func _refresh_bottom_flag(key: Vector3i) -> void:
|
||||
var slot: Vector2i = _slots.get(key, Vector2i(-1, -1))
|
||||
if slot.y < 0 or slot.x != Store.Kind.WALL:
|
||||
return
|
||||
var flag := 0.0
|
||||
for d in _behind_dirs:
|
||||
var n: Vector3i = key + d
|
||||
if store.has_tile(n) and store.kind_of(n) != Store.Kind.WALL:
|
||||
flag = 1.0
|
||||
break
|
||||
_wall_mm.multimesh.set_instance_custom_data(slot.y, Color(flag, 0.0, 0.0, 0.0))
|
||||
|
||||
|
||||
func _repaint_color(key: Vector3i) -> void:
|
||||
|
||||
Reference in New Issue
Block a user