137 lines
4.2 KiB
GDScript
137 lines
4.2 KiB
GDScript
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
|