Files
tactical-shooter/client/scripts/apply_visibility_ranges.gd
T
shawn e7299b17e9 Phase 7: netfox + godot-jolt stack upgrade
Stack installed:
- netfox v1.35.3 (core + extras + noray + internals)
- godot-jolt v0.16.0-stable

Architecture:
- Server: ENet transport (works headless, no netfox deps)
- Client/Editor: netfox rollback (RollbackSynchronizer, TickInterpolator)

New/modified:
- docs/migration-netfox-plan.md — migration architecture
- scripts/network/network_manager.gd — netfox-aware ENet fallback
- scripts/network/player.gd — clean base player
- client/characters/player_netfox.gd — rollback player w/ WeaponManager
- client/characters/input/player_net_input.gd — BaseNetInput subclass
- client/characters/character/fps_character_controller.gd — netfox input feed
- client/weapons/ — weapon data, registry, TacticalWeaponHitscan, WeaponManager
- client/scripts/round_replicator.gd — client-side round state bridge
- server/scripts/round_manager.gd — improved state machine
- server/scripts/plugin_api/plugin_manager.gd — refined plugin system
- config: enemy_tag, ally_tag for meatball targeting

Removed: old C++ SimulationServer GDExtension (replaced by netfox rollback)
2026-07-02 17:39:22 -04:00

154 lines
5.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@tool
extends EditorScript
# Apply distance-based visibility ranges to all modular CSG pieces.
#
# This is the equivalent of LOD for CSG-based geometry — it hides
# entire pieces when they're beyond a useful visual distance.
#
# Ranges (defined per asset type):
# Walls: hide past 50 m
# Floors: hide past 60 m
# Pillars: hide past 30 m
# Beams: hide past 30 m
# Accent panels: hide past 20 m
# Door/Window: hide past 40 m
#
# Usage:
# Select a scene in the Godot editor, then:
# Scene > Run Script (select this file)
#
# Or from CLI on a specific scene:
# godot --script scripts/apply_visibility_ranges.gd \
# --scene res://assets/scenes/modular/kit_demo.tscn
const DEBUG := false # Enable for verbose logging
# Default ranges per type (begin = 0.0 means always visible up to end)
const RANGES := {
"wall": {"begin": 0.0, "end": 50.0, "margin": 5.0}, # fade out over 5m
"floor": {"begin": 0.0, "end": 60.0, "margin": 10.0},
"pillar": {"begin": 0.0, "end": 30.0, "margin": 3.0},
"beam": {"begin": 0.0, "end": 30.0, "margin": 3.0},
"accent": {"begin": 0.0, "end": 20.0, "margin": 2.0},
"doorway": {"begin": 0.0, "end": 40.0, "margin": 5.0},
"window": {"begin": 0.0, "end": 40.0, "margin": 5.0},
"endcap": {"begin": 0.0, "end": 50.0, "margin": 5.0},
"default": {"begin": 0.0, "end": 40.0, "margin": 5.0},
}
func _run():
var scene_root: Node
if Engine.is_editor_hint():
scene_root = get_scene() as Node
if not scene_root:
printerr("No open scene in editor. Open a scene first.")
return
else:
var args = OS.get_cmdline_args()
var scene_path := ""
for i in range(args.size()):
if args[i] == "--scene" and i + 1 < args.size():
scene_path = args[i + 1]
if scene_path.is_empty():
printerr("Usage: godot --script apply_visibility_ranges.gd --scene res://path/to/scene.tscn" +
"\nOr run from the Godot editor with a scene open.")
return
var packed = ResourceLoader.load(scene_path)
if not packed:
printerr("Failed to load scene: ", scene_path)
return
scene_root = packed.instantiate()
print("=== Visibility Range Applicator ===")
print("Scene: ", scene_root.name)
var counts := {} # type → number of nodes modified
var total := 0
_apply_to_node(scene_root, counts, total)
print("")
print("Modified nodes: ", total)
for type_name in counts:
print(" %s: %d" % [type_name, counts[type_name]])
print("")
print("=== Ranges applied ===")
func _apply_to_node(node: Node, counts: Dictionary, total: int) -> void:
# Only process CSG nodes (the modular pieces)
if node is CSGShape3D:
var range_config := _get_range_for_node(node)
if range_config != null:
node.visibility_range_begin = range_config.begin
node.visibility_range_end = range_config.end
node.visibility_range_begin_margin = range_config.margin
node.visibility_range_end_margin = range_config.margin
var type_name := _classify_node(node)
counts[type_name] = counts.get(type_name, 0) + 1
total += 1
if DEBUG:
print(" %s%.0f%.0f m (type: %s)" % [node.name, range_config.begin, range_config.end, type_name])
# Also apply to CSGCombiner3D parents (hold doorway/window subtractions)
if node is CSGCombiner3D:
var range_config := _get_range_for_combiner(node)
if range_config != null:
node.visibility_range_begin = range_config.begin
node.visibility_range_end = range_config.end
node.visibility_range_begin_margin = range_config.margin
node.visibility_range_end_margin = range_config.margin
var type_name := _classify_node(node)
counts[type_name] = counts.get(type_name, 0) + 1
total += 1
if DEBUG:
print(" %s%.0f%.0f m (type: %s)" % [node.name, range_config.begin, range_config.end, type_name])
# Recurse
for child in node.get_children():
_apply_to_node(child, counts, total)
func _classify_node(node: Node) -> String:
var name_lower := node.name.to_lower()
if name_lower.contains("wall"):
if name_lower.contains("door"): return "doorway"
if name_lower.contains("window"): return "window"
if name_lower.contains("corner"): return "wall"
if name_lower.contains("endcap"): return "endcap"
return "wall"
if name_lower.contains("floor"): return "floor"
if name_lower.contains("pillar"): return "pillar"
if name_lower.contains("beam"): return "beam"
if name_lower.contains("accent") or name_lower.contains("blue") or name_lower.contains("red"):
return "accent"
if name_lower.contains("panel"):
return "accent"
return "default"
func _get_range_for_node(node: CSGShape3D) -> Dictionary:
# Detect CSGBox3D size to infer type
if node is CSGBox3D:
var box := node as CSGBox3D
var size := box.size
# Floor slabs: thin, wide (Z > X on rotated ones, check min dimension)
if size.y <= 0.1 and (size.x >= 2.0 or size.z >= 2.0):
return RANGES.floor.duplicate()
return _get_range_by_name(node)
func _get_range_for_combiner(node: CSGCombiner3D) -> Dictionary:
return _get_range_by_name(node)
func _get_range_by_name(node: Node) -> Dictionary:
var type_name := _classify_node(node)
return RANGES.get(type_name, RANGES.default).duplicate()