extends Node3D ## NS-18 / NEO-25: **Client preview only** — glow per interactable when [CharacterBody3D] is within ## horizontal radius (X/Z, inclusive). Data comes from [method setup_glow_groups] after ## `GET /game/world/interactables`. Server `POST …/interact` remains authoritative. const EMISSION_DIM := 0.12 const EMISSION_BRIGHT := 7.0 var _player: CharacterBody3D ## Each element: [code]anchor[/code] [Vector3], [code]radius[/code] [float], [code]material[/code] ## [StandardMaterial3D], [code]markers[/code] [Array] of [MeshInstance3D] (optional; emission driven via material). var _glow_groups: Array = [] func setup_player(player: CharacterBody3D) -> void: _player = player func setup_glow_groups(groups: Array) -> void: _glow_groups = groups func _process(_delta: float) -> void: if _player == null or _glow_groups.is_empty(): return var px: float = _player.global_position.x var pz: float = _player.global_position.z for g in _glow_groups: if not g is Dictionary: continue var d: Dictionary = g var anchor: Variant = d.get("anchor", null) var mat: Variant = d.get("material", null) if not anchor is Vector3 or not mat is StandardMaterial3D: continue var a: Vector3 = anchor var m: StandardMaterial3D = mat var r: float = float(d.get("radius", 0.0)) var dx: float = px - a.x var dz: float = pz - a.z var in_range: bool = (dx * dx + dz * dz) <= r * r m.emission_energy_multiplier = EMISSION_BRIGHT if in_range else EMISSION_DIM