extends StaticBody3D # PortalDoor.gd # Specialized door for "Tekton Doors" mode. # Teleports players to a target room/door when they step into it. signal player_entered_portal(player_node, door_node) @export var room_id: int = 0 @export var door_id: int = 0 # 0: North, 1: South, 2: East, 3: West # State synced by PortalModeManager var target_room_id: int = -1 var target_door_id: int = -1 var is_active: bool = true var portal_color: Color = Color.WHITE: set = set_portal_color func set_portal_color(value: Color): portal_color = value _update_visuals() @onready var detection_area: Area3D = $Area3D func _ready(): add_to_group("PortalDoors") if detection_area: detection_area.body_entered.connect(_on_body_entered) # Visual feedback: indicate door is active _update_visuals() # Adjust GroundIndicator position based on spawn_offset metadata _adjust_indicator_position() func _on_body_entered(body: Node3D): if not is_active: return if body.is_in_group("Players") or body.get("is_bot"): var current_time = Time.get_ticks_msec() if body.has_meta("last_portal_time"): # Reduce cooldown to 200ms to match manager logic and allow fast re-entry if current_time - body.get_meta("last_portal_time") < 200: return body.set_meta("last_portal_time", current_time) print("[PortalDoor] Player %s entered Door %d in Room %d" % [body.name, door_id, room_id]) emit_signal("player_entered_portal", body, self ) var _materials_initialized: bool = false func _update_visuals(): # Removed is_node_ready() check to allow early setter calls to prepare variables, # but we still need the nodes to exist to apply them. if not is_inside_tree(): return var vortex = get_node_or_null("Vortex") var frame_left = get_node_or_null("Frame_Left") # If children aren't there yet, we can't update visuals. # This usually happens if called before or during early _ready. if not vortex or not frame_left: return if not _materials_initialized: _initialize_unique_materials() _materials_initialized = true if vortex: var mat = vortex.get_surface_override_material(0) if mat: mat.albedo_color = portal_color mat.albedo_color.a = 0.5 if mat.has_method("set_emission"): mat.set("emission", portal_color) for part_name in ["Frame_Left", "Frame_Right", "Frame_Top"]: var frame = get_node_or_null(part_name) if frame: var mat = frame.get_surface_override_material(0) if mat: mat.albedo_color = portal_color.lerp(Color.BLACK, 0.4) var ground = get_node_or_null("GroundIndicator") if ground: var mat = ground.get_surface_override_material(0) if mat: mat.albedo_color = portal_color mat.albedo_color.a = 0.4 mat.emission = portal_color mat.emission_energy_multiplier = 2.0 func _initialize_unique_materials(): var vortex = get_node_or_null("Vortex") if vortex: var mat = vortex.get_surface_override_material(0) if not mat: mat = vortex.mesh.surface_get_material(0) if mat: vortex.set_surface_override_material(0, mat.duplicate()) for part_name in ["Frame_Left", "Frame_Right", "Frame_Top"]: var frame = get_node_or_null(part_name) if frame: var mat = frame.get_surface_override_material(0) if not mat: mat = frame.mesh.surface_get_material(0) if mat: frame.set_surface_override_material(0, mat.duplicate()) var ground = get_node_or_null("GroundIndicator") if ground: var mat = ground.get_surface_override_material(0) if not mat: mat = ground.mesh.surface_get_material(0) if mat: ground.set_surface_override_material(0, mat.duplicate()) func _adjust_indicator_position(): # This uses the spawn_offset metadata set by PortalModeManager # to push the ground indicator "into" the room. if not has_meta("spawn_offset"): return var ground = get_node_or_null("GroundIndicator") if not ground: return var offset_2d = get_meta("spawn_offset") # Vector2i var offset_3d = Vector3(offset_2d.x, 0, offset_2d.y) # Convert the global direction (into the room) to local coordinates var local_dir = to_local(global_position + offset_3d).normalized() # Nudge the indicator in that direction ground.position = local_dir * 0.5 # Reduced from 0.6 to close the gap ground.position.y = 0.05 # Keep it just above the floor