extends Node3D @export var day_length := 120.0 # seconds for full rotation @export var start_light_angle := -90.0 @export var show_spawn_dialog := true @export_multiline var spawn_dialog_text := "Welcome to Promiscuity.\n\nPress E to close this message." @export var spawn_dialog_auto_close_seconds := 4.0 var end_light_angle = start_light_angle + 360.0 var start_radians = start_light_angle * PI / 180 var time := 0.0 @onready var sun := $DirectionalLight3D @onready var _player: Node = $Player @onready var _quest_text: RichTextLabel = $PhoneUI/Control/PhoneFrame/QuestText const FIRST_QUEST_ID := "first_drive" const QUEST_PROMPT_META_PREFIX := "quest_intro_prompt_shown_" const SPAWN_DIALOG_META_KEY := "level_spawn_dialog_shown" const FIRST_QUEST := { "id": FIRST_QUEST_ID, "title": "RepoBot's First Task", "description": "Get familiar with movement and vehicles.", "steps": [ { "id": "enter_vehicle", "text": "Get in the car (E).", "complete_event": "entered_vehicle", }, { "id": "reach_checkpoint", "text": "Drive the car into the checkpoint marker.", "complete_event": "reach_checkpoint", }, ], } func _ready() -> void: _setup_quests() if _should_show_spawn_dialog() and DialogSystem and DialogSystem.has_method("show_text"): await get_tree().process_frame DialogSystem.show_text(spawn_dialog_text) _mark_spawn_dialog_shown() if spawn_dialog_auto_close_seconds > 0.0: await get_tree().create_timer(spawn_dialog_auto_close_seconds).timeout if DialogSystem and DialogSystem.has_method("close_if_text"): DialogSystem.close_if_text(spawn_dialog_text) _show_quest_intro_dialog() func _process(delta): time = fmod((time + delta), day_length) var t = time / day_length # Rotate sun around X axis var angle = lerp(start_light_angle, end_light_angle, t) # sunrise → sunset → night → sunrise sun.rotation_degrees.x = angle # Adjust intensity var curSin = -sin((t * TAU) + start_radians) var energy = clamp((curSin * 1.0) + 0.2, 0.0, 1.2) sun.light_energy = energy func _setup_quests() -> void: if QuestManager == null: return if not QuestManager.has_quest(FIRST_QUEST_ID): QuestManager.register_quest(FIRST_QUEST) if not QuestManager.is_active_quest(FIRST_QUEST_ID) and not QuestManager.is_quest_completed(FIRST_QUEST_ID): QuestManager.start_quest(FIRST_QUEST_ID) if not QuestManager.is_connected("quest_state_changed", Callable(self, "_refresh_quest_ui")): QuestManager.quest_state_changed.connect(_refresh_quest_ui) if _player and _player.has_signal("vehicle_entered"): if not _player.is_connected("vehicle_entered", Callable(self, "_on_player_vehicle_entered")): _player.vehicle_entered.connect(_on_player_vehicle_entered) _refresh_quest_ui() func _on_player_vehicle_entered(_vehicle: Node) -> void: if QuestManager: QuestManager.emit_event(&"entered_vehicle") func _refresh_quest_ui() -> void: if _quest_text == null or QuestManager == null: return var state: Dictionary = QuestManager.get_active_quest_state() if not bool(state.get("active", false)): _quest_text.text = "No active quest." return var title := String(state.get("title", "Quest")) if bool(state.get("completed", false)): _quest_text.text = "[b]%s[/b]\nComplete." % title return var step_index: int = int(state.get("current_step_index", 0)) var total_steps: int = int(state.get("total_steps", 0)) var step_text := String(state.get("current_step_text", "")) _quest_text.text = "[b]%s[/b]\nStep %d/%d\n%s" % [title, step_index + 1, total_steps, step_text] func _show_quest_intro_dialog() -> void: if QuestManager == null: return var state: Dictionary = QuestManager.get_active_quest_state() if not bool(state.get("active", false)) or bool(state.get("completed", false)): return var quest_id := String(state.get("quest_id", "")).strip_edges() var step_id := String(state.get("current_step_id", "")).strip_edges() var step_text := String(state.get("current_step_text", "")) if quest_id.is_empty() or step_id.is_empty() or step_text.is_empty(): return var prompt_key := "%s%s_%s" % [QUEST_PROMPT_META_PREFIX, quest_id, step_id] if QuestManager.has_meta(prompt_key) and bool(QuestManager.get_meta(prompt_key)): return if DialogSystem and DialogSystem.has_method("show_text"): DialogSystem.show_text("RepoBot: New task assigned.\n\n%s" % step_text) QuestManager.set_meta(prompt_key, true) func _should_show_spawn_dialog() -> bool: if not show_spawn_dialog: return false if QuestManager == null: return true if not QuestManager.has_meta(SPAWN_DIALOG_META_KEY): return true return not bool(QuestManager.get_meta(SPAWN_DIALOG_META_KEY)) func _mark_spawn_dialog_shown() -> void: if QuestManager == null: return QuestManager.set_meta(SPAWN_DIALOG_META_KEY, true)