Compare commits

..

2 Commits

23 changed files with 325 additions and 224 deletions

View File

@ -15,7 +15,7 @@ func _ready() -> void:
func start_room(): func start_room():
save_game = State.save_game save_game = State.save_game
%PlayerController.has_stage = true Scenes.player_enable.emit(true)
on_first_station() on_first_station()
var left_first_station: bool = false var left_first_station: bool = false

View File

@ -19,7 +19,7 @@ func start_room():
save_room()) save_room())
%PlayerController.process_mode = Node.PROCESS_MODE_INHERIT %PlayerController.process_mode = Node.PROCESS_MODE_INHERIT
ini_room.emit() ini_room.emit()
%PlayerController.has_stage = true Scenes.player_enable.emit(true)
func _ready(): func _ready():
id = State.rooms.ADULTHOOD id = State.rooms.ADULTHOOD

View File

@ -0,0 +1,25 @@
extends AnimatableBody3D
var start_position:Vector3 = position
var risen:bool = false
var raiser : Tween
func raise(body) -> void:
if body is not PlayerController:
push_warning("Unexpected body in raiser trigger", body)
return
if risen:
position = start_position
if raiser and raiser.is_valid():
raiser.kill()
raiser = create_tween()
raiser.tween_property(self, "position", position + Vector3(0,1.1,0), 1)
risen = true
func reset(_discard) -> void:
risen = false
position = start_position

View File

@ -0,0 +1 @@
uid://hji6r2e8mcqo

View File

@ -1,15 +1 @@
class_name CrouchVolume extends Area3D class_name CrouchVolume extends Area3D
func ready():
body_entered.connect(notify_player_entry)
body_exited.connect(notify_player_exit)
func notify_player_entry(body: RigidBody3D):
print_debug("Player entered Crouch area.")
if body is PlayerController:
body.inside_crouch_volume.append(self)
func notify_player_exit(body: RigidBody3D):
if body is PlayerController:
body.inside_crouch_volume.erase(self)
body.crouched = false

View File

@ -78,13 +78,8 @@ func collapse():
revealed = false revealed = false
func _on_playback_finished(): func _on_playback_finished():
# Restore player controller control using the room's unique node reference # Restore player control via central signal
var player_controller := owner.get_node_or_null("%PlayerController") as PlayerController Scenes.player_enable.emit(true)
if player_controller and "has_stage" in player_controller:
player_controller.has_stage = true
else:
# Fallback: ensure mouse is visible if we can't find player controller
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func handle(event: InputEvent): func handle(event: InputEvent):
viewport.push_input(event) viewport.push_input(event)

View File

@ -1,8 +1,8 @@
class_name YouthRoom class_name YouthRoom
extends RoomTemplate extends RoomTemplate
## Used by the stage system when this room takes stage for intro playback ## Used by the room system when this room becomes active
# var has_stage: bool = false # Reminder, do not uncomment, this field is inherited from RoomTemplate! # var is_active: bool = false # Reminder, do not uncomment, this field is inherited from RoomTemplate!
@onready var board_trigger: InteractiveSprite = %MindBoard @onready var board_trigger: InteractiveSprite = %MindBoard
@onready var door_trigger: InteractiveSprite = %Door @onready var door_trigger: InteractiveSprite = %Door
@ -21,7 +21,7 @@ func start_room():
%LightAnimation.lights_on() %LightAnimation.lights_on()
# Give player control after intro (or immediately if repeating) # Give player control after intro (or immediately if repeating)
%PlayerController.has_stage = true Scenes.player_enable.emit(true)
func _play_intro_scene() -> void: func _play_intro_scene() -> void:

View File

@ -5,6 +5,7 @@
[ext_resource type="PackedScene" uid="uid://mkccbig41bqb" path="res://logic-scenes/player_controller/player_controller.tscn" id="3_foj4y"] [ext_resource type="PackedScene" uid="uid://mkccbig41bqb" path="res://logic-scenes/player_controller/player_controller.tscn" id="3_foj4y"]
[ext_resource type="AudioStream" uid="uid://bbpo1hu35yer8" path="res://base-environments/youth_room/import/sounds/thunder.mp3" id="3_wcypa"] [ext_resource type="AudioStream" uid="uid://bbpo1hu35yer8" path="res://base-environments/youth_room/import/sounds/thunder.mp3" id="3_wcypa"]
[ext_resource type="Script" uid="uid://c281w7earok6w" path="res://base-environments/youth_room/crouch_volume.gd" id="3_x3dlb"] [ext_resource type="Script" uid="uid://c281w7earok6w" path="res://base-environments/youth_room/crouch_volume.gd" id="3_x3dlb"]
[ext_resource type="Script" uid="uid://hji6r2e8mcqo" path="res://base-environments/youth_room/climb_volume.gd" id="4_dqyng"]
[ext_resource type="PackedScene" uid="uid://bnskiyx1sksww" path="res://logic-scenes/board/physics-board.tscn" id="4_gyjxx"] [ext_resource type="PackedScene" uid="uid://bnskiyx1sksww" path="res://logic-scenes/board/physics-board.tscn" id="4_gyjxx"]
[ext_resource type="AudioStream" uid="uid://1tvopjmo6dp2" path="res://base-environments/youth_room/audio/Azure Studios - mgd-723687677.mp3" id="5_fe1yj"] [ext_resource type="AudioStream" uid="uid://1tvopjmo6dp2" path="res://base-environments/youth_room/audio/Azure Studios - mgd-723687677.mp3" id="5_fe1yj"]
[ext_resource type="PackedScene" uid="uid://citwb7f4dl3l1" path="res://thank-you.tscn" id="5_kts6y"] [ext_resource type="PackedScene" uid="uid://citwb7f4dl3l1" path="res://thank-you.tscn" id="5_kts6y"]
@ -110,25 +111,6 @@ height = 3.78697
radius = 1.73984 radius = 1.73984
height = 5.43669 height = 5.43669
[sub_resource type="GDScript" id="GDScript_uyt24"]
script/source = "extends StaticBody3D
var risen:bool = false
var start_position:Vector3 = position
func raise(entering_player):
if not entering_player.on_crouch_cooldown:
if risen:
position = start_position
var raiser = create_tween()
raiser.tween_property(self, \"position\", position + Vector3(0,1.1,0), 1)
risen = true
func reset(_discard):
risen = false
position = start_position
"
[sub_resource type="BoxShape3D" id="BoxShape3D_gim5a"] [sub_resource type="BoxShape3D" id="BoxShape3D_gim5a"]
size = Vector3(1.63347, 0.305693, 0.775269) size = Vector3(1.63347, 0.305693, 0.775269)
@ -1683,9 +1665,9 @@ collision_mask = 2
transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0, 0, 0) transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0, 0, 0)
shape = SubResource("CapsuleShape3D_n20ff") shape = SubResource("CapsuleShape3D_n20ff")
[node name="ladder" type="StaticBody3D" parent="logic/Bed and Ladders"] [node name="ladder" type="AnimatableBody3D" parent="logic/Bed and Ladders"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0699199, 0, -0.082733) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0699199, 0, -0.082733)
script = SubResource("GDScript_uyt24") script = ExtResource("4_dqyng")
[node name="CollisionShape3D" type="CollisionShape3D" parent="logic/Bed and Ladders/ladder"] [node name="CollisionShape3D" type="CollisionShape3D" parent="logic/Bed and Ladders/ladder"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.336261, -0.199846, 0.0549462) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.336261, -0.199846, 0.0549462)

View File

@ -3,10 +3,10 @@ class_name RoomTemplate extends Node3D
var initialised: bool = false var initialised: bool = false
var id: State.rooms = State.rooms.NULL var id: State.rooms = State.rooms.NULL
var has_stage: bool: var is_active: bool:
set(value): set(value):
has_stage = value is_active = value
if has_stage and not initialised: if is_active and not initialised:
start_room() start_room()
@ -19,7 +19,7 @@ func _ready() -> void:
State.current_room = id State.current_room = id
func disable()-> void: func disable()-> void:
has_stage = false is_active = false
set_process_input(false) set_process_input(false)
set_process(false) set_process(false)

View File

@ -25,16 +25,16 @@ var focus_stickies:bool = true:
else: else:
current_dropzone_id = current_dropzone_id current_dropzone_id = current_dropzone_id
var has_stage := false: var focused := false:
set(focus): set(focus):
if focus: if focus:
has_stage = true focused = true
get_tree().call_group("interactables", "collapse") get_tree().call_group("interactables", "collapse")
current_dropzone_id = 0 current_dropzone_id = 0
current_sticky_note_id = 0 current_sticky_note_id = 0
focus_stickies = true focus_stickies = true
else: else:
has_stage = false focused = false
if is_node_ready(): if is_node_ready():
if focus: if focus:
process_mode = Node.PROCESS_MODE_INHERIT process_mode = Node.PROCESS_MODE_INHERIT
@ -43,7 +43,7 @@ var has_stage := false:
for sticky in dropzone.get_children(): for sticky in dropzone.get_children():
if sticky is StickyNote: if sticky is StickyNote:
sticky.is_dragged = false sticky.is_dragged = false
visible = has_stage visible = focused
@onready var dropzone := $HBoxContainer/dropzone @onready var dropzone := $HBoxContainer/dropzone
@ -144,7 +144,7 @@ func _ready():
populate_board(["c_comic_heroes", 'c_teasing', "p_agent_q", "p_good_intended"]) populate_board(["c_comic_heroes", 'c_teasing', "p_agent_q", "p_good_intended"])
populate_board(["c_out_of_world", 'c_confusion', "p_outer_conflict", "p_unique"]) populate_board(["c_out_of_world", 'c_confusion', "p_outer_conflict", "p_unique"])
has_stage = has_stage focused = focused
get_viewport().gui_focus_changed.connect(reclaim_lost_focus) get_viewport().gui_focus_changed.connect(reclaim_lost_focus)
@ -157,12 +157,12 @@ func _ready():
func reclaim_lost_focus(_thief): func reclaim_lost_focus(_thief):
if has_stage: if focused:
grab_focus() grab_focus()
#func _process(delta): #func _process(delta):
# # drops dragged area when Mouse is no longer pressed. # # drops dragged area when Mouse is no longer pressed.
# if has_stage and !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and current_context == DRAG: # if focused and !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and current_context == DRAG:
# currently_active_node.is_dragged = false # currently_active_node.is_dragged = false
## Will be used later to spawn Cards and Post-Its and remember them in the dictionary ## Will be used later to spawn Cards and Post-Its and remember them in the dictionary
@ -408,10 +408,10 @@ func insert_area(parent: Control, node: Area2D):
# Takes the inputs for control inputs # Takes the inputs for control inputs
func _input(event) -> void: func _input(event) -> void:
if not has_stage or not is_instance_valid(currently_active_node): return if not focused or not is_instance_valid(currently_active_node): return
if event.is_action_pressed("ui_cancel"): if event.is_action_pressed("ui_cancel"):
has_stage = false focused = false
get_viewport().set_input_as_handled() get_viewport().set_input_as_handled()
if event is InputEventMouse: if event is InputEventMouse:
@ -571,7 +571,7 @@ func on_scene_skipped(i: int):
mementos_collected += i # FIXME: sometimes -1 is passed here, why? mementos_collected += i # FIXME: sometimes -1 is passed here, why?
func claim_focus(): func claim_focus():
has_stage = true focused = true
func find_first_free_card() -> int: func find_first_free_card() -> int:
for i in range(dropzone.get_child_count()): for i in range(dropzone.get_child_count()):

View File

@ -1,9 +1,9 @@
class_name CardBurner extends CenterContainer class_name CardBurner extends CenterContainer
var has_stage: bool = false: var focused: bool = false:
set(focus): set(focus):
if is_node_ready(): if is_node_ready():
if not focus == has_stage: if not focus == focused:
if focus: if focus:
process_mode = Node.PROCESS_MODE_INHERIT process_mode = Node.PROCESS_MODE_INHERIT
self.show() self.show()
@ -13,7 +13,7 @@ var has_stage: bool = false:
else: else:
self.hide() self.hide()
process_mode = Node.PROCESS_MODE_DISABLED process_mode = Node.PROCESS_MODE_DISABLED
has_stage = focus focused = focus
@onready var cursor: CandleCursor = %CandleCursor @onready var cursor: CandleCursor = %CandleCursor
@onready var ancors: Array[Control] = [%Ancor1, %Ancor2, %Ancor3, %Ancor4] @onready var ancors: Array[Control] = [%Ancor1, %Ancor2, %Ancor3, %Ancor4]
@ -59,7 +59,7 @@ func handle_mouse_button(event: InputEventMouseButton, card: Card):
card.burn_state = Card.burned.BURNING card.burn_state = Card.burned.BURNING
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if has_stage: if focused:
if event.is_action_pressed("ui_up"): if event.is_action_pressed("ui_up"):
handle_direction_input(Vector2.UP) handle_direction_input(Vector2.UP)
elif event.is_action_pressed("ui_down"): elif event.is_action_pressed("ui_down"):

View File

@ -1,33 +1,34 @@
class_name PlayerController extends RigidBody3D class_name PlayerController extends RigidBody3D
var has_stage: bool = false: @export var enabled: bool = false:
set(on_stage): set(value):
print_debug("%s player controller take stage" % "True: " if on_stage else "false") if enabled == value:
if has_stage != on_stage: return
if on_stage: enabled = value
has_stage = true if is_node_ready():
if is_node_ready(): _apply_enabled_state()
camera.make_current()
get_viewport().gui_release_focus()
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED func _apply_enabled_state() -> void:
var jitter_tween: Tween = create_tween() # Kill any existing jitter tween to prevent stacking
jitter_tween.tween_property(self, "jitter_strength", 1, 1) if jitter_tween and jitter_tween.is_valid():
if has_entered: emit_signal("ui_exited") jitter_tween.kill()
elif has_stage:
await ready if enabled:
camera.make_current() camera.make_current()
jitter_strength = 1 get_viewport().gui_release_focus()
else: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if is_node_ready() and has_stage: jitter_tween = create_tween()
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE jitter_tween.tween_property(self, "jitter_strength", 1.0, 1.0)
var jitter_tween: Tween = create_tween() if has_entered:
jitter_tween.tween_property(self, "jitter_strength", 0, 0.5) ui_exited.emit()
if has_entered: emit_signal("ui_exited") else:
else: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
jitter_strength = 0 jitter_tween = create_tween()
has_stage = false jitter_tween.tween_property(self, "jitter_strength", 0.0, 0.5)
print_debug("player controller %s take stage" % "did" if has_stage else "didn't") if has_entered:
sleeping = has_stage ui_exited.emit()
sleeping = not enabled
@export var mouse_sensitivity: Vector2 = Vector2(6, 5) @export var mouse_sensitivity: Vector2 = Vector2(6, 5)
@ -47,6 +48,7 @@ var has_stage: bool = false:
@export var location_jitter:Vector3 = Vector3(0.05, 0.05, 0.05) @export var location_jitter:Vector3 = Vector3(0.05, 0.05, 0.05)
@export var location_jitter_speed:Vector3 = Vector3(2, 0.3, 1) @export var location_jitter_speed:Vector3 = Vector3(2, 0.3, 1)
var jitter_strength: float = 0 var jitter_strength: float = 0
var jitter_tween: Tween = null
var loc_noise_spot: Vector3 = Vector3.ZERO var loc_noise_spot: Vector3 = Vector3.ZERO
var rot_noise_spot: Vector3 = Vector3.ZERO var rot_noise_spot: Vector3 = Vector3.ZERO
@ -59,9 +61,9 @@ var crouched:bool = false:
set(set_crouching): set(set_crouching):
if is_node_ready(): if is_node_ready():
if set_crouching and not crouched: if set_crouching and not crouched:
if State.reduce_motion:# or true: if State.reduce_motion:
$PlayerAnimationPlayer.play("reduced_crouch") $PlayerAnimationPlayer.play("reduced_crouch")
elif trigger_slow_crouch:# or true: elif trigger_slow_crouch:
$PlayerAnimationPlayer.play("crouch") $PlayerAnimationPlayer.play("crouch")
trigger_slow_crouch = false trigger_slow_crouch = false
else: else:
@ -69,15 +71,14 @@ var crouched:bool = false:
crouched = set_crouching crouched = set_crouching
elif (not set_crouching and crouched) and not crouch_held: elif (not set_crouching and crouched) and not crouch_held:
if can_stand_up(): if can_stand_up():
if State.reduce_motion:# or true: if State.reduce_motion:
$PlayerAnimationPlayer.play("reduced_stand_up") $PlayerAnimationPlayer.play("reduced_stand_up")
elif trigger_slow_crouch:# or true: elif trigger_slow_crouch:
$PlayerAnimationPlayer.play("stand_up") $PlayerAnimationPlayer.play("stand_up")
trigger_slow_crouch = false trigger_slow_crouch = false
else: else:
$PlayerAnimationPlayer.play("fast_stand_up") $PlayerAnimationPlayer.play("fast_stand_up")
crouched = set_crouching crouched = set_crouching
var on_crouch_cooldown:bool = false
@onready var yaw: Node3D = $Yaw @onready var yaw: Node3D = $Yaw
@onready var pitch: Node3D = $Yaw/Pitch @onready var pitch: Node3D = $Yaw/Pitch
@ -102,25 +103,32 @@ signal ui_exited
func _ready(): func _ready():
_handle_jitter(0) _handle_jitter(0)
pitch.rotation_degrees.x = initial_pitch
ui_prober.area_entered.connect(_on_ray_entered) ui_prober.area_entered.connect(_on_ray_entered)
ui_prober.area_exited.connect(_on_ray_exited) ui_prober.area_exited.connect(_on_ray_exited)
$CrouchDetector.area_entered.connect(enter_crouch)
$CrouchDetector.area_exited.connect(exit_crouch) $CrouchDetector.area_exited.connect(exit_crouch)
pitch.rotation_degrees.x = initial_pitch
crouched = false # Connect to central player enable signal.
# Guard for standalone test scenes without autoloads.
if get_node_or_null("/root/Scenes"):
Scenes.player_enable.connect(_on_player_enable)
# Apply exported enabled state now that nodes are ready
_apply_enabled_state()
func _on_player_enable(enable: bool) -> void:
enabled = enable
func _process(_delta): func _process(_delta):
if has_entered: if not enabled:
if focus_ray.get_collider() == null: return
pass
#emit_signal("ui_exited") if not has_entered:
#dhas_entered = false
if Input.is_action_just_pressed("ui_accept"):
# Pass focus to the collider if it has has_stage property
var collider := focus_ray.get_collider()
if collider and "has_stage" in collider:
has_stage = false
collider.has_stage = true
else:
camera.fov = base_fov / (1 + Input.get_action_raw_strength("zoom_in_controller")) camera.fov = base_fov / (1 + Input.get_action_raw_strength("zoom_in_controller"))
var has_entered:bool = false: var has_entered:bool = false:
@ -155,11 +163,12 @@ func _on_ray_exited(area):
has_entered = false has_entered = false
func _physics_process(delta:float): func _physics_process(delta: float):
if has_stage or true: if enabled:
_handle_movement(delta) _handle_movement(delta)
_handle_rotation(delta) _handle_rotation(delta)
if jitter_strength > 0 and not State.reduce_motion: _handle_jitter(delta) if jitter_strength > 0 and not State.reduce_motion:
_handle_jitter(delta)
func _handle_movement(delta:float): func _handle_movement(delta:float):
var input:Vector2 = Vector2(Input.get_action_strength("player_right") - Input.get_action_strength("player_left"), var input:Vector2 = Vector2(Input.get_action_strength("player_right") - Input.get_action_strength("player_left"),
@ -228,59 +237,47 @@ func _handle_mouse_input(event:InputEventMouseMotion):
var trigger_slow_crouch: bool = false var trigger_slow_crouch: bool = false
var crouch_held: bool = false var crouch_held: bool = false
var crouch_toggled: bool = false var crouch_toggled: bool = false
var cround_start_time: float = 0 var crouch_start_time: float = 0
func _input(event:InputEvent) -> void: func _input(event: InputEvent) -> void:
if Scenes.current_sequence != -1: return if not enabled:
if has_stage and Scenes.current_sequence == -1 or true: return
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
_handle_mouse_input(event)
get_viewport().set_input_as_handled()
if event is InputEventMouseButton and event.pressed:
if Input.is_action_just_pressed("zoom_in_mouse"):
zoomed = true
elif Input.is_action_just_pressed("zoom_out_mouse"):
zoomed = false
if event.is_action_pressed("collect_memento_ui") or event.is_action_pressed("option_memento_ui"): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
if focus_ray.is_colliding(): _handle_mouse_input(event)
var collider := focus_ray.get_collider() get_viewport().set_input_as_handled()
if collider is InteractiveSprite:
collider.handle(event)
get_viewport().set_input_as_handled()
if event.is_action_pressed("crouch"): if event is InputEventMouseButton and event.pressed:
# Remember how long the key is held to tell apart toggle and hold behaviour. if Input.is_action_just_pressed("zoom_in_mouse"):
cround_start_time = Time.get_unix_time_from_system() zoomed = true
if crouch_toggled: elif Input.is_action_just_pressed("zoom_out_mouse"):
cround_start_time = 0 zoomed = false
#Causes the next release action to 100% stand up the player...
else:
crouch_held = true
crouched = true
elif event.is_action_released("crouch"): if event.is_action_pressed("collect_memento_ui") or event.is_action_pressed("option_memento_ui"):
crouch_held = false if focus_ray.is_colliding():
var collider := focus_ray.get_collider()
if collider is InteractiveSprite:
collider.handle(event)
get_viewport().set_input_as_handled()
# Toggles crouch in case the player just pressed the button indicating they want to toggle crouch. if event.is_action_pressed("crouch"):
if Time.get_unix_time_from_system() > cround_start_time + 0.5: crouch_start_time = Time.get_unix_time_from_system()
if crouched and can_stand_up(): if crouch_toggled:
crouch_toggled = false crouch_start_time = 0
crouched = false else:
else: crouch_held = true
crouch_toggled = true crouched = true
elif event.is_action_released("crouch"):
func play_scene(_id: int, _repeat: bool): crouch_held = false
pass if Time.get_unix_time_from_system() > crouch_start_time + 0.5:
#if id == Scenes.id.YOUTH_DRAVEN: if crouched and can_stand_up():
# var rotation_tween := create_tween() crouch_toggled = false
crouched = false
func scene_finished(_id, repeat: bool): else:
if repeat: crouch_toggled = true
has_stage = true
func _on_bed_enter(_body): func _on_bed_enter(_body):
if not (crouched or on_crouch_cooldown): if not crouched:
trigger_slow_crouch = true trigger_slow_crouch = true
crouched = true crouched = true
@ -289,10 +286,6 @@ func _on_bed_exit(_body):
trigger_slow_crouch = true trigger_slow_crouch = true
crouched = false crouched = false
on_crouch_cooldown = true
await get_tree().create_timer(1.0).timeout
on_crouch_cooldown = false
var inside_crouch_volume: Array[CrouchVolume] = [] var inside_crouch_volume: Array[CrouchVolume] = []
#returns true, if the player character can stand upright. #returns true, if the player character can stand upright.
@ -302,6 +295,11 @@ func can_stand_up() -> bool:
return false return false
return true return true
func enter_crouch(body):
if body is CrouchVolume:
crouched = true
func exit_crouch(body): func exit_crouch(body):
if body is Area3D: if body is CrouchVolume:
crouched = false crouched = false

View File

@ -277,7 +277,7 @@ func leave_stage(actor:Object) -> void:
# Currently focused element loses focus, but remains in stack. # Currently focused element loses focus, but remains in stack.
func free_focus(): func free_focus():
if focus_locked: return false if focus_locked: return false
if stage_list.size() > 0: stage_list.front().has_stage = false if stage_list.size() > 0: stage_list.front().focused = false
func reset_focus(): func reset_focus():
stage_list = [stage_list[-1]] stage_list = [stage_list[-1]]

View File

@ -28,10 +28,9 @@ enum id {
ADULT_BURNOUT ADULT_BURNOUT
} }
signal player_enable(enabled: bool);
signal scene_starting(scene_id: id, is_repeating: bool) signal scene_starting(scene_id: id, is_repeating: bool)
signal scene_finished(scene_id: id, is_repeating: bool) signal scene_finished(scene_id: id, is_repeating: bool)
signal player_enable(enabled: bool)
func _ready() -> void: func _ready() -> void:
pass pass
@ -40,6 +39,9 @@ func _ready() -> void:
func begin_sequence(scene_id: id) -> void: func begin_sequence(scene_id: id) -> void:
print_debug(">>> Scenes.begin_sequence(%s)" % id.keys()[scene_id]) print_debug(">>> Scenes.begin_sequence(%s)" % id.keys()[scene_id])
# Disable player movement during cutscenes
player_enable.emit(false)
current_sequence = scene_id current_sequence = scene_id
started_sequences = started_sequences | (1 << scene_id) started_sequences = started_sequences | (1 << scene_id)
@ -60,6 +62,9 @@ func end_sequence(scene_id: id) -> void:
if current_sequence == scene_id: if current_sequence == scene_id:
current_sequence = -1 current_sequence = -1
# Re-enable player movement after cutscene
player_enable.emit(true)
# Legacy support - redirects to begin_sequence # Legacy support - redirects to begin_sequence
func start_sequence(scene_id: id) -> void: func start_sequence(scene_id: id) -> void:
push_warning("start_sequence is deprecated. CollectableUi should call begin_sequence directly.") push_warning("start_sequence is deprecated. CollectableUi should call begin_sequence directly.")
@ -95,6 +100,7 @@ func is_sequence_unlocked(index: id) -> bool:
return (1 << int(index)) & enabled_sequences > 0 return (1 << int(index)) & enabled_sequences > 0
func get_completed_total() -> int: func get_completed_total() -> int:
# https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
var i: int = completed_sequences - ((completed_sequences >> 1) & 0x55555555); var i: int = completed_sequences - ((completed_sequences >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333); i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
i = (i + (i >> 4)) & 0x0F0F0F0F; i = (i + (i >> 4)) & 0x0F0F0F0F;

View File

@ -10,18 +10,18 @@ func _ready() -> void:
pass_to_actor = get_child(current_tab) pass_to_actor = get_child(current_tab)
func _on_tab_changed(tab_id: int): func _on_tab_changed(tab_id: int):
# Transfer has_stage to the new tab's child # Transfer focused to the new tab's child
for child in get_children(): for child in get_children():
if "has_stage" in child: if "focused" in child:
child.has_stage = false child.focused = false
var new_child = get_child(tab_id) var new_child = get_child(tab_id)
if "has_stage" in new_child: if "focused" in new_child:
new_child.has_stage = true new_child.focused = true
pass_to_actor = new_child pass_to_actor = new_child
func _on_stage_left(): func _on_stage_left():
await get_tree().process_frame await get_tree().process_frame
if not pass_to_actor.has_stage: if not pass_to_actor.focused:
get_parent().vanish() get_parent().vanish()

View File

@ -1,13 +1,22 @@
[gd_scene load_steps=7 format=3 uid="uid://cjb4gu40l3jah"] [gd_scene load_steps=17 format=3 uid="uid://bystjfm61jw7t"]
[ext_resource type="Script" uid="uid://c281w7earok6w" path="res://base-environments/youth_room/crouch_volume.gd" id="1_ki2be"] [ext_resource type="Script" uid="uid://c281w7earok6w" path="res://base-environments/youth_room/crouch_volume.gd" id="1_ki2be"]
[ext_resource type="PackedScene" uid="uid://mkccbig41bqb" path="res://logic-scenes/player_controller/player_controller.tscn" id="2_rmwd0"] [ext_resource type="PackedScene" uid="uid://mkccbig41bqb" path="res://logic-scenes/player_controller/player_controller.tscn" id="2_rmwd0"]
[ext_resource type="Script" uid="uid://hji6r2e8mcqo" path="res://base-environments/youth_room/climb_volume.gd" id="3_rmwd0"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ki2be"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ki2be"]
radius = 1.14469 radius = 1.14469
height = 3.78697 height = 3.78697
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ki2be"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ki2be"]
transparency = 1
blend_mode = 1
albedo_color = Color(0.48962224, 0.48962224, 0.48962218, 1)
[sub_resource type="CapsuleMesh" id="CapsuleMesh_ki2be"]
material = SubResource("StandardMaterial3D_ki2be")
radius = 1.145
height = 3.787
[sub_resource type="PlaneMesh" id="PlaneMesh_ki2be"] [sub_resource type="PlaneMesh" id="PlaneMesh_ki2be"]
size = Vector2(50, 50) size = Vector2(50, 50)
@ -16,6 +25,34 @@ size = Vector2(50, 50)
radius = 5.0 radius = 5.0
height = 10.0 height = 10.0
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ki2be"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_8344t"]
radius = 0.156739
height = 1.0
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_wlply"]
radius = 0.787239
height = 4.07459
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_55bee"]
radius = 1.0352
height = 2.3319
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_317jd"]
radius = 1.14469
height = 3.78697
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_coixd"]
radius = 1.73984
height = 5.43669
[sub_resource type="BoxShape3D" id="BoxShape3D_j8gbq"]
size = Vector3(1.63347, 0.305693, 0.775269)
[sub_resource type="BoxShape3D" id="BoxShape3D_akpqw"]
size = Vector3(0.244565, 1, 0.245859)
[node name="CharacterTests" type="Node3D"] [node name="CharacterTests" type="Node3D"]
[node name="bed_crouch" type="Area3D" parent="."] [node name="bed_crouch" type="Area3D" parent="."]
@ -28,7 +65,9 @@ script = ExtResource("1_ki2be")
transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0.107988, 1.56575e-08, -0.358201) transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0.107988, 1.56575e-08, -0.358201)
shape = SubResource("CapsuleShape3D_ki2be") shape = SubResource("CapsuleShape3D_ki2be")
[node name="MeshInstance3D" type="MeshInstance3D" parent="bed_crouch"] [node name="MeshInstance3D" type="MeshInstance3D" parent="bed_crouch/CollisionShape3D"]
mesh = SubResource("CapsuleMesh_ki2be")
skeleton = NodePath("../..")
[node name="Label3D" type="Label3D" parent="bed_crouch"] [node name="Label3D" type="Label3D" parent="bed_crouch"]
transform = Transform3D(1, 0, 0, 0, 0.27838773, 0.96046877, 0, -0.96046877, 0.27838773, 0, 0.29873586, 0) transform = Transform3D(1, 0, 0, 0, 0.27838773, 0.96046877, 0, -0.96046877, 0.27838773, 0, 0.29873586, 0)
@ -36,21 +75,90 @@ text = "CROUCH VOLUME"
[node name="PlayerController" parent="." groups=["camera_owner"] instance=ExtResource("2_rmwd0")] [node name="PlayerController" parent="." groups=["camera_owner"] instance=ExtResource("2_rmwd0")]
unique_name_in_owner = true unique_name_in_owner = true
process_mode = 4
transform = Transform3D(0.686123, 0, 0.727485, 0, 1, 0, -0.727485, 0, 0.686123, 0.63, 0, 0.925) transform = Transform3D(0.686123, 0, 0.727485, 0, 1, 0, -0.727485, 0, 0.686123, 0.63, 0, 0.925)
enabled = true
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="CollisionShape3D"]
shape = SubResource("WorldBoundaryShape3D_ki2be")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0049040318, -0.008946419, 0.003689289) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0049040318, -0.36189413, 0.003689289)
mesh = SubResource("PlaneMesh_ki2be") mesh = SubResource("PlaneMesh_ki2be")
[node name="OmniLight3D" type="OmniLight3D" parent="."] [node name="OmniLight3D" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.221635, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.221635, 0)
omni_range = 9.435307
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."] [node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.5455923, 4.7683716e-07, -5.57273) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.5455923, 4.7683716e-07, -5.57273)
mesh = SubResource("SphereMesh_ki2be") mesh = SubResource("SphereMesh_ki2be")
[node name="StaticBody3D" type="StaticBody3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"]
shape = SubResource("WorldBoundaryShape3D_ki2be")
[node name="Bed and Ladders" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.1783472, 4.7683716e-07, 5.135357)
[node name="ladder_trigger" type="Area3D" parent="Bed and Ladders"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.19478047, 0, 0)
collision_layer = 2
collision_mask = 2
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bed and Ladders/ladder_trigger"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.170028, 0.244833, -0.0455775)
shape = SubResource("CapsuleShape3D_8344t")
[node name="bed_duck" type="Area3D" parent="Bed and Ladders"]
collision_layer = 2
collision_mask = 2
script = ExtResource("1_ki2be")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bed and Ladders/bed_duck"]
transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0.916567, 1.64525, -1.14479)
shape = SubResource("CapsuleShape3D_wlply")
[node name="bed_enter" type="Area3D" parent="Bed and Ladders"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.35717, 0, -0.255744)
collision_layer = 2
collision_mask = 2
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bed and Ladders/bed_enter"]
transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0, 0, 0)
shape = SubResource("CapsuleShape3D_55bee")
[node name="bed_crouch" type="Area3D" parent="Bed and Ladders"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.24356, 0, -0.796765)
collision_layer = 2
collision_mask = 2
script = ExtResource("1_ki2be")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bed and Ladders/bed_crouch"]
transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0.107988, 1.56575e-08, -0.358201)
shape = SubResource("CapsuleShape3D_317jd")
[node name="reset_failover" type="Area3D" parent="Bed and Ladders"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.24356, 0, -0.531366)
collision_layer = 2
collision_mask = 2
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bed and Ladders/reset_failover"]
transform = Transform3D(0.995341, 0.096419, 0, 4.21461e-09, -4.35077e-08, -1, -0.096419, 0.995341, -4.37114e-08, 0, 0, 0)
shape = SubResource("CapsuleShape3D_coixd")
[node name="ladder" type="AnimatableBody3D" parent="Bed and Ladders"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0699199, 0, -0.082733)
script = ExtResource("3_rmwd0")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bed and Ladders/ladder"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.336261, -0.199846, 0.0549462)
shape = SubResource("BoxShape3D_j8gbq")
[node name="CollisionShape3D2" type="CollisionShape3D" parent="Bed and Ladders/ladder"]
transform = Transform3D(0.833085, 0, -0.553144, 0, 1, 0, 0.553144, 0, 0.833085, 0.0114179, 0, -0.412249)
shape = SubResource("BoxShape3D_akpqw")
[node name="CollisionShape3D3" type="CollisionShape3D" parent="Bed and Ladders/ladder"]
transform = Transform3D(0.707304, 0, 0.706909, 0, 1, 0, -0.706909, 0, 0.707304, 0.240986, 0, 0.539403)
shape = SubResource("BoxShape3D_akpqw")
[connection signal="body_entered" from="Bed and Ladders/ladder_trigger" to="Bed and Ladders/ladder" method="raise"]
[connection signal="body_exited" from="Bed and Ladders/bed_duck" to="Bed and Ladders/ladder" method="reset"]

View File

@ -1,11 +1,11 @@
extends Panel extends Panel
@onready var has_stage = true: @onready var focused = true:
set(focus): set(focus):
if focus: if focus:
has_stage = State.request_focus(self) focused = State.request_focus(self)
else: else:
has_stage = false focused = false
State.drop_own_focus(self) State.drop_own_focus(self)
func _ready(): func _ready():

View File

@ -4,10 +4,10 @@ class_name AccessibilitySettings
signal changed signal changed
signal leave_stage signal leave_stage
var has_stage:bool = false: var focused:bool = false:
set(stage): set(focus):
has_stage = stage focused = focus
is_active = has_stage is_active = focused
# used to override state management in case menu is being used while game is paused. # used to override state management in case menu is being used while game is paused.
var is_active:bool = false: var is_active:bool = false:
@ -18,7 +18,7 @@ var is_active:bool = false:
leave_stage.emit() leave_stage.emit()
is_active = active is_active = active
if (is_active and not has_stage) and not get_tree().paused: if (is_active and not focused) and not get_tree().paused:
push_warning("%s has been set active but not been given the stage while the tree is unpaused!") push_warning("%s has been set active but not been given the stage while the tree is unpaused!")
@export var is_in_beginning: bool = false @export var is_in_beginning: bool = false

View File

@ -3,12 +3,12 @@ extends VBoxContainer
signal changed signal changed
signal leave_stage signal leave_stage
var has_stage:bool = false: var focused:bool = false:
set(stage): set(focus):
has_stage = stage focused = focus
if is_node_ready(): if is_node_ready():
%MusicPreview.playing = has_stage %MusicPreview.playing = focused
if has_stage: if focused:
music_mute_switch.grab_focus() music_mute_switch.grab_focus()
@ -86,5 +86,5 @@ func _on_exit_button_pressed() -> void:
State.save_settings() State.save_settings()
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and has_stage: if event.is_action_pressed("ui_cancel") and focused:
leave_stage.emit() leave_stage.emit()

View File

@ -4,10 +4,10 @@ signal changed
var has_unsaved_changes: bool = false var has_unsaved_changes: bool = false
signal leave_stage signal leave_stage
var has_stage:bool = false: var focused:bool = false:
set(stage): set(focus):
has_stage = stage focused = focus
if is_node_ready() and has_stage: if is_node_ready() and focused:
%ExpandButton.grab_focus() %ExpandButton.grab_focus()
@export var is_in_beginning: bool = false @export var is_in_beginning: bool = false
@ -47,5 +47,5 @@ func _on_exit_button_pressed() -> void:
leave_stage.emit() leave_stage.emit()
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and has_stage: if event.is_action_pressed("ui_cancel") and focused:
leave_stage.emit() leave_stage.emit()

View File

@ -3,10 +3,10 @@ extends VBoxContainer
signal changed signal changed
signal leave_stage signal leave_stage
var has_stage:bool = false: var focused:bool = false:
set(stage): set(focus):
has_stage = stage focused = focus
if is_node_ready() and has_stage: if is_node_ready() and focused:
y_switch_gamepad.grab_focus() y_switch_gamepad.grab_focus()
var current_music_decay:float = 0 var current_music_decay:float = 0
@ -39,5 +39,5 @@ func _on_exit_button_pressed() -> void:
State.save_settings() State.save_settings()
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and has_stage: if event.is_action_pressed("ui_cancel") and focused:
leave_stage.emit() leave_stage.emit()

View File

@ -2,11 +2,11 @@ extends VBoxContainer
signal leave_stage signal leave_stage
var has_stage:bool = false: var focused:bool = false:
set(stage): set(focus):
has_stage = stage focused = focus
if is_node_ready(): if is_node_ready():
if has_stage: if focused:
steam_optout.grab_focus() steam_optout.grab_focus()
var current_music_decay:float = 0 var current_music_decay:float = 0
@ -41,7 +41,7 @@ func _on_exit_button_pressed() -> void:
State.save_settings() State.save_settings()
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and has_stage: if event.is_action_pressed("ui_cancel") and focused:
leave_stage.emit() leave_stage.emit()
func reset_all_stats(): func reset_all_stats():

View File

@ -5,9 +5,9 @@ signal changed
signal leave_stage signal leave_stage
var has_unsaved_changes:= false var has_unsaved_changes:= false
var has_stage:bool = false: var focused:bool = false:
set(stage): set(focus):
has_stage = stage focused = focus
match preset_selected: match preset_selected:
1: %PerformancePreset.grab_focus() 1: %PerformancePreset.grab_focus()
2: %BalancedPreset.grab_focus() 2: %BalancedPreset.grab_focus()
@ -286,5 +286,5 @@ func select_id_to_window_mode(item_id) -> DisplayServer.WindowMode:
return item_id + 2 if item_id != 0 else 0 return item_id + 2 if item_id != 0 else 0
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and has_stage: if event.is_action_pressed("ui_cancel") and focused:
_on_exit_button_pressed() _on_exit_button_pressed()