stick-the-quick/ui/MainMenu/CharacterSelect/MainMenu_CharacterSelect.gd

245 lines
7.1 KiB
GDScript

class_name MainMenu_CharacterSelect extends Control
const IDLE_ANIMS: Array[StringName] = [
&'run',
&'run',
&'walk',
&'walk'
]
const CHOICE_ANIMS_1: Array[StringName] = [
&'spin',
&'binky',
&'run',
&'attack'
]
const CHOICE_ANIMS_2: Array[StringName] = [
&'wave',
&'swim',
&'caw',
&'bow'
]
const CHOICE_AUDIO_1: Array[StringName] = [
"spin",
"hop",
"low_whoosh",
"violent_whoosh"
]
@onready var models: Array[Node3D] = [
$Node3D/Stick, $Node3D/Lorna, $Node3D/Blujai, $Node3D/Gibbo
]
@onready var animplayers: Array[AnimationPlayer] = [
$Node3D/Stick/AnimationPlayer,
$Node3D/Lorna/AnimationPlayer,
$Node3D/Blujai/AnimationPlayer,
$Node3D/Gibbo/AnimationPlayer
]
@onready var profiles: Array[CharacterProfile] = [
load("res://characters/playable/Stick/StickProfile.tres"),
load("res://characters/playable/Lorna/LornaProfile.tres"),
load("res://characters/playable/Blujai/BlujaiProfile.tres"),
load("res://characters/playable/Gibbo/GibboProfile.tres")
]
@onready var pivot := $Node3D/Pivot as Node3D
@onready var prev_button := (
$PanelContainer/HBoxContainer/Control/PrevButton
) as BaseButton
@onready var next_button := (
$PanelContainer/HBoxContainer/Control/NextButton
) as BaseButton
@onready var name_label := (
$PanelContainer/HBoxContainer/VBoxContainer/Name
) as Label
@onready var description_label := (
$PanelContainer/HBoxContainer/VBoxContainer/ScrollContainer/Description
) as Label
@onready var back_button := (
$PanelContainer/HBoxContainer/Control/HBoxContainer/BackButton
) as Button
@onready var go_button := (
$PanelContainer/HBoxContainer/Control/HBoxContainer/GoButton
) as Button
@onready var audio_player := $AudioStreamPlayer as AudioStreamPlayer
@onready var panelcontainer := $PanelContainer as PanelContainer
var selected: int = 0
var mode: StringName
func _ready() -> void:
mode = UI.Args(self).get(&'mode', &'story')
for i in animplayers.size():
animplayers[i].play(IDLE_ANIMS[i])
_update_info()
_adjust_description_width.call_deferred()
prev_button.pressed.connect(_select_prev)
next_button.pressed.connect(_select_next)
back_button.pressed.connect(_back)
go_button.pressed.connect(_go)
match mode:
&'story': panelcontainer.add_theme_stylebox_override(&'panel', load(
"res://ui/MainMenu/StoryMenuStyleBox.tres"
))
&'mission': panelcontainer.add_theme_stylebox_override(&'panel', load(
"res://ui/MainMenu/MissionMenuStyleBox.tres"
))
&'freerun': panelcontainer.add_theme_stylebox_override(&'panel', load(
"res://ui/MainMenu/FreerunMenuStyleBox.tres"
))
func _process(delta: float) -> void:
for model in models:
model.rotation.y += delta
pivot.basis = pivot.basis.slerp(
Basis.looking_at((
models[selected].global_position - pivot.global_position
).slide(Vector3.UP)),
3.0*delta
)
func _select_next() -> void:
selected = (selected + 1)%profiles.size()
_update_info()
func _select_prev() -> void:
selected = (selected + profiles.size() - 1)%profiles.size()
_update_info()
func _update_info() -> void:
var profile := profiles[selected]
name_label.text = profile.nickname
name_label.modulate = profile.talk_color.lerp(Color.WHITE, 0.125)
var pronouns := profile.pronouns.split("/")
if Storyboard.character_unlocked(profile.id):
go_button.disabled = false
go_button.text = "Go!"
description_label.text = ((
"%s (%s)\n" +
"\n" +
"SPEED: %s\n" +
"TRACTION: %s\n" +
"AIR: %s\n" +
"COMBAT: %s\n" +
"ABILITY: %s\n" +
"\n" +
"%s\n" +
"\n" +
"BIO: %s\n"
) % [
profile.full_name, profile.pronouns,
_rating_string(profile.speed_rating),
_rating_string(profile.traction_rating),
_rating_string(profile.air_rating),
_rating_string(profile.combat_rating),
profile.ability,
profile.playstyle,
profile.story
])
else:
go_button.disabled = true
go_button.text = "Not unlocked"
description_label.text = ((
"This character is not unlocked yet. " +
"Meet %s in another character's story to unlock %s."
) % [pronouns[1], pronouns[1]])
func _rating_string(rating: CharacterProfile.StatRating) -> String:
match rating:
CharacterProfile.StatRating.LOW: return "Low"
CharacterProfile.StatRating.MEDIUM: return "Medium"
CharacterProfile.StatRating.HIGH: return "High"
CharacterProfile.StatRating.STELLAR: return "STELLAR!"
_: return "I don't know"
func _adjust_description_width() -> void:
description_label.custom_minimum_size.x = (
description_label.get_parent().size.x
)
func _input(event: InputEvent) -> void:
if (
event.is_action_pressed(&'move_left') or
event.is_action_pressed(&'look_left') or
event.is_action_pressed(&'ui_left')
):
get_viewport().set_input_as_handled()
UI._play_sound_confirm()
_select_prev()
elif (
event.is_action_pressed(&'move_right') or
event.is_action_pressed(&'look_right') or
event.is_action_pressed(&'ui_right')
):
get_viewport().set_input_as_handled()
UI._play_sound_confirm()
_select_next()
elif event.is_action_pressed(&'confirm'):
get_viewport().set_input_as_handled()
if not go_button.disabled:
_go()
func _back() -> void:
UI.Return(self)
func _go() -> void:
process_mode = PROCESS_MODE_DISABLED
UI._audio_player.stop()
audio_player.stream = load("res://audio/choice.ogg")
audio_player.play()
var model := models[selected]
var animplayer := animplayers[selected]
var profile := profiles[selected]
var snd1 := load("res://audio/%s.ogg" % CHOICE_AUDIO_1[selected])
var snd2 := profile.talk_blip
var anim1 := CHOICE_ANIMS_1[selected]
var anim2 := CHOICE_ANIMS_2[selected]
var target_basis := Basis.looking_at((
FX.get_camera().global_position - model.global_position
).slide(Vector3.UP), Vector3.UP, true)
var target_pivot_basis := Basis.looking_at((
models[selected].global_position - pivot.global_position
).slide(Vector3.UP))
while (
(pivot.basis.z - target_pivot_basis.z).length() > 0.02 or
(model.basis.z - target_basis.z).length() > 0.02
):
var tick := await Wait.tick()
pivot.basis = pivot.basis.slerp(target_pivot_basis, 10.0*tick)
model.basis = model.basis.slerp(target_basis, 10.0*tick)
audio_player.stream = snd1
audio_player.play()
animplayer.play(anim1, Runner.ANIMATION_BLEND_TIME)
await Wait.seconds(0.5*(3.0 - profile.speed_rating))
audio_player.stream = snd2
audio_player.play()
animplayer.play(anim2, Runner.ANIMATION_BLEND_TIME)
await Wait.seconds(1.0)
FX.stop_bgm()
await FX.fade(Color.BLACK)
process_mode = PROCESS_MODE_ALWAYS
match mode:
&'story':
UI.Return(self, _prepare_game_start_params())
_:
var params = await UI.Call(load(
"res://ui/MainMenu/MapSelect/MainMenu_MapSelect.tscn"
), {
&'mode': mode,
&'character': profile.id
})
UI.Return(self, params)
func _prepare_game_start_params() -> GameStartParameters:
var params := GameStartParameters.new()
params.character = profiles[selected].id
var story_state := Storyboard.save_data.find_character_story_state(
params.character
) as CharacterStoryState
params.game_mode = story_state.checkpoint.game_mode
params.level = story_state.checkpoint.location.map.level
params.mission = story_state.checkpoint.location.map.mission
params.loading_zone = story_state.checkpoint.location.loading_zone
params.show_level_card = bool(
params.game_mode != LevelDescriptor.GameMode.HUB
)
return params