245 lines
7.1 KiB
GDScript
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
|