Files
Blockline/scripts/operator_gallery.gd
T

149 lines
5.4 KiB
GDScript

class_name OperatorGallery
extends PanelContainer
var ui: GameUI
var viewport := SubViewport.new()
var world := Node3D.new()
var model: ImportedOperator
var camera := Camera3D.new()
var clips := OptionButton.new()
var scrub := HSlider.new()
var status := Label.new()
var playing := true
var elapsed := 0.0
var selected := "steve"
var yaw := 0.0
var distance := 2.5
func _ready() -> void:
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var background := StyleBoxFlat.new()
background.bg_color = Color("101820")
add_theme_stylebox_override("panel", background)
var margin := MarginContainer.new()
for side: String in ["left", "right", "top", "bottom"]: margin.add_theme_constant_override("margin_" + side, 24)
add_child(margin)
var column := VBoxContainer.new()
column.add_theme_constant_override("separation", 12)
margin.add_child(column)
var title := Label.new()
title.text = "OPERATOR / ANIMATIONSSTUDIO"
title.add_theme_font_size_override("font_size", 28)
column.add_child(title)
var row := HBoxContainer.new()
column.add_child(row)
var select := OptionButton.new()
select.add_item("STEVE / SOLDAT")
select.add_item("GAS MASK / SWAT")
select.selected = 1 if ui.game.operator_id == "gas_mask" else 0
row.add_child(select)
select.item_selected.connect(func(index: int): select_model("gas_mask" if index == 1 else "steve"))
var equip := Button.new()
equip.text = "OPERATOR AUSRÜSTEN"
row.add_child(equip)
equip.pressed.connect(func():
ui.game.operator_id = selected
ui.preferences_changed.emit()
status.text = "Gespeichert. Dein Operator ist im nächsten Match aktiv.")
var preview := SubViewportContainer.new()
preview.size_flags_vertical = Control.SIZE_EXPAND_FILL
preview.size_flags_horizontal = Control.SIZE_EXPAND_FILL
preview.stretch = true
preview.custom_minimum_size = Vector2(300, 220)
column.add_child(preview)
viewport.own_world_3d = true
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
viewport.msaa_3d = get_viewport().msaa_3d
preview.add_child(viewport)
viewport.add_child(world)
preview.gui_input.connect(inspect_input)
world.add_child(camera)
camera.fov = 45
camera.make_current()
var environment := WorldEnvironment.new()
environment.environment = Environment.new()
environment.environment.background_mode = Environment.BG_COLOR
environment.environment.background_color = Color("151e29")
environment.environment.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
environment.environment.ambient_light_color = Color("b4cae2")
environment.environment.ambient_light_energy = 0.75
world.add_child(environment)
var key := DirectionalLight3D.new()
key.rotation_degrees = Vector3(-35, -30, 0)
key.light_energy = 1.6
key.shadow_enabled = true
world.add_child(key)
var fill := DirectionalLight3D.new()
fill.rotation_degrees = Vector3(-20, 145, 0)
fill.light_energy = 0.8
fill.light_color = Color("8cb8ed")
world.add_child(fill)
var floor_mesh := MeshInstance3D.new()
var plane := PlaneMesh.new()
plane.size = Vector2(8, 8)
floor_mesh.mesh = plane
var mat := StandardMaterial3D.new()
mat.albedo_color = Color("283440")
mat.roughness = 0.9
floor_mesh.material_override = mat
world.add_child(floor_mesh)
select_model("gas_mask" if select.selected == 1 else "steve")
for clip: String in OperatorPose.library.get_animation_list():
clips.add_item(clip.replace("__", " / ").replace("_", " "))
clips.set_item_metadata(clips.item_count - 1, clip)
if clip == "pro_rifle__idle_aiming": clips.selected = clips.item_count - 1
clips.item_selected.connect(func(_index: int): elapsed = 0)
column.add_child(clips)
var playback := HBoxContainer.new()
column.add_child(playback)
var pause := Button.new()
pause.text = "PAUSE / PLAY"
pause.pressed.connect(func(): playing = not playing)
playback.add_child(pause)
scrub.min_value = 0
scrub.max_value = 1
scrub.step = 0.001
scrub.size_flags_horizontal = Control.SIZE_EXPAND_FILL
scrub.value_changed.connect(func(value: float):
playing = false
elapsed = value * model.pose.clip(clip_key()).length)
playback.add_child(scrub)
status.text = "%d Clips · Ziehen: drehen · Mausrad: zoomen · Zeitleiste: Einzelpose" % clips.item_count
column.add_child(status)
var back := Button.new()
back.text = "← ZURÜCK"
back.pressed.connect(ui.main_menu)
column.add_child(back)
func select_model(variant: String) -> void:
if model != null:
world.remove_child(model)
model.queue_free()
selected = variant
model = ImportedOperator.new()
world.add_child(model)
model.setup(variant)
model.weapon_mount.add_child(WeaponModels.build(ui.game.loadout))
elapsed = 0
func clip_key() -> String:
return str(clips.get_item_metadata(clips.selected)) if clips.item_count > 0 else "pro_rifle__idle_aiming"
func _process(delta: float) -> void:
if model == null: return
var key := clip_key()
var length := model.pose.clip(key).length
if playing: elapsed = fposmod(elapsed + delta, length + 0.5)
model.preview(key, minf(elapsed, length))
scrub.set_value_no_signal(minf(1, elapsed / length))
model.rotation.y = yaw
camera.position = Vector3(0.4, 1.25, -distance)
camera.look_at(Vector3(0, 0.9, 0))
func inspect_input(event: InputEvent) -> void:
if event is InputEventMouseMotion and event.button_mask & MOUSE_BUTTON_MASK_LEFT:
yaw += event.relative.x * 0.012
if event is InputEventMouseButton and event.pressed:
if event.button_index == MOUSE_BUTTON_WHEEL_UP: distance = maxf(1.2, distance - 0.2)
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN: distance = minf(5, distance + 0.2)