class_name GameKey extends Node3D static var _scene := preload("res://scenes/game_key.tscn") @export_group("Node references") @export var _mesh: MeshInstance3D @export var _upper_left_label: Label3D @export var _upper_right_label: Label3D @export var _lower_left_label: Label3D @export var _lower_right_label: Label3D @export var _center_label: Label3D @export var _press_light: OmniLight3D @export var _sfx_player: AudioStreamPlayer3D @export_group("Light") @export var _light_energy: float = 3 @export var _light_fade_time: float = 0.25 @export_group("Pressing") @export var _press_offset: float = 0.1 @export_group("SFX") @export var _press_sfx: AudioStream @export var _release_sfx: AudioStream var props: KeyProps var keyboard: GameKeyboard var input_event_init: InputEventKey var _is_pressed: bool var _input_event_main: InputEventKey var _input_event_shift: InputEventKey var _input_event_alt: InputEventKey var _input_event_alt_shift: InputEventKey var _light_timer: float @onready var _default_position: Vector3 = position @onready var _light_default_position: Vector3 = _press_light.position @onready var _polyphonic := _sfx_player.get_stream_playback() as AudioStreamPlaybackPolyphonic static func instantiate_with_props( _props: KeyProps, _keyboard: GameKeyboard ) -> GameKey: var input_event := InputEventKey.new() input_event.physical_keycode = _props.physical_keycode input_event.location = _props.location var node := _scene.instantiate() as GameKey node.keyboard = _keyboard node.props = _props node.input_event_init = input_event node.name = node.name + " " + node.input_event_init.as_text_physical_keycode() if _props.location != KEY_LOCATION_UNSPECIFIED: node.name += " " + node.input_event_init.as_text_location() return node func _ready() -> void: _mesh.scale.x = props.width_ratio _set_labels() func _process(delta: float) -> void: _animate_light(delta) transform = keyboard.animator.animate_key(delta, _default_position) position.y -= _press_offset if _is_pressed else 0.0 func _unhandled_input(event: InputEvent) -> void: if event is not InputEventKey: return var event_key := event as InputEventKey if ( event_key.physical_keycode != props.physical_keycode or event_key.location != props.location or event_key.echo ): return _is_pressed = event_key.is_pressed() if _is_pressed: _play_sfx(_press_sfx) else: _play_sfx(_release_sfx) keyboard.pressed_positions[_default_position] = _is_pressed if LayoutConfig.is_configuring and _is_pressed: if not keyboard.alt_layout: if not event_key.shift_pressed: _input_event_main = event_key else: _input_event_shift = event_key else: if not event_key.shift_pressed: _input_event_alt = event_key else: _input_event_alt_shift = event_key _set_labels() func _clear_labels() -> void: _upper_left_label.text = "" _upper_right_label.text = "" _lower_left_label.text = "" _lower_right_label.text = "" _center_label.text = "" func _set_labels() -> void: _clear_labels() props.main_char = ( char(_input_event_main.unicode).to_upper() if _input_event_main else props.main_char ) props.shift_char = ( char(_input_event_shift.unicode).to_upper() if _input_event_shift else props.shift_char ) props.alt_char = ( char(_input_event_alt.unicode).to_upper() if _input_event_alt else props.alt_char ) props.alt_shift_char = ( char(_input_event_alt_shift.unicode).to_upper() if _input_event_alt_shift else props.alt_shift_char ) if not props.is_char(): _center_label.text = input_event_init.as_text_physical_keycode() return if ( (props.main_char and not props.shift_char) or props.main_char == props.shift_char ): _upper_left_label.text = props.main_char if (props.main_char and props.shift_char) and props.main_char != props.shift_char: _upper_left_label.text = props.shift_char _lower_left_label.text = props.main_char if ( (not props.main_char and props.shift_char) and props.main_char != props.shift_char ): _upper_left_label.text = props.shift_char if props.alt_char and props.alt_char != props.main_char: _lower_right_label.text = props.alt_char if ( props.alt_shift_char and props.alt_shift_char != props.alt_char and props.alt_shift_char != props.shift_char ): _upper_right_label.text = props.alt_shift_char func _animate_light(delta: float) -> void: if _is_pressed: _light_timer = _light_fade_time if _light_timer <= 0: _press_light.visible = false return _press_light.visible = true _press_light.light_energy = (_light_timer / _light_fade_time) * _light_energy _light_timer -= delta _press_light.position = _light_default_position _press_light.position.y += _press_offset if _is_pressed else 0.0 func _play_sfx(stream: AudioStream) -> void: _polyphonic.play_stream(stream)