216 lines
5.3 KiB
GDScript
216 lines
5.3 KiB
GDScript
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("Animation")
|
|
@export var _pos_sod_fzr: Vector3 = Vector3(5, 0.15, 4)
|
|
|
|
@export_subgroup("Idle")
|
|
@export var _idle_frequency: float = 0.5
|
|
@export var _idle_amplitude: float = 0.1
|
|
@export var _idle_offset_x: float = 1
|
|
@export var _idle_offset_z: float = -1
|
|
|
|
@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 _idle_sin_time: float
|
|
var _light_timer: float
|
|
|
|
@onready var _default_position: Vector3 = position
|
|
@onready
|
|
var _polyphonic := _sfx_player.get_stream_playback() as AudioStreamPlaybackPolyphonic
|
|
@onready var _pos_sod := SecondOrderDynamics.new(_pos_sod_fzr, _default_position)
|
|
|
|
|
|
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(delta)
|
|
_animate_light(delta)
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if event.is_action_pressed("reset_animations"):
|
|
_pos_sod = SecondOrderDynamics.new(_pos_sod_fzr, _default_position)
|
|
return
|
|
|
|
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(delta: float) -> void:
|
|
_idle_sin_time += delta * _idle_frequency
|
|
|
|
var new_position := _default_position
|
|
new_position.y += (
|
|
sin(_idle_sin_time + position.x * _idle_offset_x + position.z * _idle_offset_z)
|
|
* _idle_amplitude
|
|
)
|
|
|
|
new_position.y -= _press_offset if _is_pressed else 0.0
|
|
|
|
position = _pos_sod.process(delta, new_position)
|
|
|
|
|
|
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
|
|
|
|
|
|
func _play_sfx(stream: AudioStream) -> void:
|
|
_polyphonic.play_stream(stream)
|