214 lines
9.3 KiB
GDScript
214 lines
9.3 KiB
GDScript
class_name LoadoutOverview
|
||||
|
|
extends Control
|
|||
|
|
|
|||
|
|
const CYAN := Color("86bfd0")
|
|||
|
|
var editor: ClassEditor
|
|||
|
|
var layout := Control.new()
|
|||
|
|
var classes := VBoxContainer.new()
|
|||
|
|
var primary: Button
|
|||
|
|
var pistol: Button
|
|||
|
|
var bench: WeaponShowcase
|
|||
|
|
var thumbnails: Array[WeaponShowcase] = []
|
|||
|
|
var weapon_names: Array[Label] = []
|
|||
|
|
var attachment_labels: Array[Label] = []
|
|||
|
|
var feedback: Label
|
|||
|
|
var sidearm: Node3D
|
|||
|
|
|
|||
|
|
func text(value: String, at: Vector2, font_size: int, color: Color = Color("a7a8a4")) -> Label:
|
|||
|
|
var label := Label.new()
|
|||
|
|
label.text = value
|
|||
|
|
label.position = at
|
|||
|
|
label.add_theme_font_size_override("font_size", font_size)
|
|||
|
|
label.add_theme_color_override("font_color", color)
|
|||
|
|
layout.add_child(label)
|
|||
|
|
return label
|
|||
|
|
|
|||
|
|
func tile(value: String, at: Vector2, dimensions: Vector2, callback: Callable) -> Button:
|
|||
|
|
var button := Button.new()
|
|||
|
|
button.text = value
|
|||
|
|
button.position = at
|
|||
|
|
button.size = dimensions
|
|||
|
|
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
|||
|
|
button.add_theme_font_size_override("font_size", 19)
|
|||
|
|
button.add_theme_color_override("font_color", Color("969b9b"))
|
|||
|
|
button.add_theme_color_override("font_hover_color", CYAN)
|
|||
|
|
button.add_theme_color_override("font_focus_color", CYAN)
|
|||
|
|
for state: String in ["normal", "hover", "pressed", "focus"]:
|
|||
|
|
var style := StyleBoxFlat.new()
|
|||
|
|
style.bg_color = Color(0.065, 0.066, 0.068, 0.92) if state == "normal" else Color(0.09, 0.13, 0.145, 0.97)
|
|||
|
|
style.border_width_top = 0 if state == "normal" else 1
|
|||
|
|
style.border_color = CYAN
|
|||
|
|
style.content_margin_left = 18
|
|||
|
|
button.add_theme_stylebox_override(state, style)
|
|||
|
|
button.pressed.connect(callback)
|
|||
|
|
layout.add_child(button)
|
|||
|
|
return button
|
|||
|
|
|
|||
|
|
func _ready() -> void:
|
|||
|
|
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|||
|
|
add_child(layout)
|
|||
|
|
layout.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|||
|
|
resized.connect(func(): layout.scale = size / Vector2(1600, 900))
|
|||
|
|
layout.scale = size / Vector2(1600, 900)
|
|||
|
|
bench = WeaponShowcase.new()
|
|||
|
|
layout.add_child(bench)
|
|||
|
|
bench.position = Vector2(680, 85)
|
|||
|
|
bench.size = Vector2(920, 750)
|
|||
|
|
bench.stretch = true
|
|||
|
|
bench.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|||
|
|
var environment := bench.stage.get_child(0) as WorldEnvironment
|
|||
|
|
environment.environment.background_color = Color("050707")
|
|||
|
|
environment.environment.ambient_light_energy = 0.18
|
|||
|
|
for child in bench.stage.get_children():
|
|||
|
|
if child is OmniLight3D:
|
|||
|
|
child.light_energy = 1.1
|
|||
|
|
child.shadow_enabled = true
|
|||
|
|
make_bench()
|
|||
|
|
text("EDIT LOADOUTS", Vector2(77, 33), 38, Color("6e959f"))
|
|||
|
|
text(editor.ui.game.nickname + " / LOCAL OPERATOR", Vector2(1150, 47), 16)
|
|||
|
|
var scroll := ScrollContainer.new()
|
|||
|
|
scroll.position = Vector2(80, 142)
|
|||
|
|
scroll.size = Vector2(308, 280)
|
|||
|
|
layout.add_child(scroll)
|
|||
|
|
classes.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|||
|
|
classes.add_theme_constant_override("separation", 7)
|
|||
|
|
scroll.add_child(classes)
|
|||
|
|
primary = tile("", Vector2(445, 142), Vector2(337, 158), func(): open_weapon(false))
|
|||
|
|
pistol = tile("", Vector2(445, 310), Vector2(337, 158), func(): open_weapon(true))
|
|||
|
|
for index in 2:
|
|||
|
|
var card: Button = primary if index == 0 else pistol
|
|||
|
|
var heading := text("Primary Weapon" if index == 0 else "Secondary Weapon", Vector2.ZERO, 18)
|
|||
|
|
heading.reparent(card, false)
|
|||
|
|
heading.position = Vector2(18, 8)
|
|||
|
|
heading.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|||
|
|
var weapon_name := text("", Vector2.ZERO, 13)
|
|||
|
|
weapon_name.reparent(card, false)
|
|||
|
|
weapon_name.position = Vector2(18, 33)
|
|||
|
|
weapon_names.append(weapon_name)
|
|||
|
|
var dots := text("", Vector2.ZERO, 16, Color("9f7639"))
|
|||
|
|
dots.reparent(card, false)
|
|||
|
|
dots.position = Vector2(235, 10)
|
|||
|
|
attachment_labels.append(dots)
|
|||
|
|
var thumbnail := WeaponShowcase.new()
|
|||
|
|
card.add_child(thumbnail)
|
|||
|
|
thumbnail.position = Vector2(62, 52)
|
|||
|
|
thumbnail.size = Vector2(220, 77)
|
|||
|
|
thumbnail.stretch = true
|
|||
|
|
thumbnail.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|||
|
|
var env := thumbnail.stage.get_child(0) as WorldEnvironment
|
|||
|
|
env.environment.background_color = Color("101112")
|
|||
|
|
thumbnails.append(thumbnail)
|
|||
|
|
var caption := text("ANPASSEN / WAFFENSCHMIED", Vector2.ZERO, 11, Color("626969"))
|
|||
|
|
caption.reparent(card, false)
|
|||
|
|
caption.position = Vector2(18, 139)
|
|||
|
|
tile("Ausrüstung\n\n◈ ENERGIESCHILD ✦ CLAYMORE", Vector2(445, 480), Vector2(337, 100), func(): editor.notice.text = "Ausrüstung ist für alle Klassen festgelegt.")
|
|||
|
|
tile("Lethal\n\nFrag-Granate × 2", Vector2(445, 590), Vector2(337, 73), func(): editor.notice.text = "Zwei Frag-Granaten pro Leben.")
|
|||
|
|
tile("Tactical\n\nFlashbang × 1", Vector2(445, 675), Vector2(337, 73), func(): editor.notice.text = "Eine Flashbang pro Leben.")
|
|||
|
|
tile("KLASSENOPTIONEN / VORLAGEN", Vector2(80, 690), Vector2(308, 40), func(): editor.show_workshop())
|
|||
|
|
tile("SPEICHERN & AUSRÜSTEN", Vector2(80, 741), Vector2(308, 40), editor.save_class)
|
|||
|
|
tile("← BACK", Vector2(80, 843), Vector2(160, 36), editor.ui.main_menu)
|
|||
|
|
text("AUSWÄHLEN / WAFFE ANPASSEN", Vector2(270, 853), 14)
|
|||
|
|
feedback = text("Änderungen gelten ab dem nächsten Spawn.", Vector2(820, 853), 13)
|
|||
|
|
feedback.size = Vector2(700, 35)
|
|||
|
|
feedback.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|||
|
|
refresh()
|
|||
|
|
|
|||
|
|
func box(at: Vector3, dimensions: Vector3, color: Color) -> MeshInstance3D:
|
|||
|
|
var node := MeshInstance3D.new()
|
|||
|
|
var mesh := BoxMesh.new()
|
|||
|
|
mesh.size = dimensions
|
|||
|
|
node.mesh = mesh
|
|||
|
|
var material := StandardMaterial3D.new()
|
|||
|
|
material.albedo_color = color
|
|||
|
|
material.roughness = 0.84
|
|||
|
|
node.material_override = material
|
|||
|
|
node.position = at
|
|||
|
|
bench.stage.add_child(node)
|
|||
|
|
return node
|
|||
|
|
|
|||
|
|
func make_bench() -> void:
|
|||
|
|
var tabletop := box(Vector3(0, -0.19, -0.1), Vector3(1.3, 0.09, 1.7), Color("242a2b"))
|
|||
|
|
var surface := tabletop.material_override as StandardMaterial3D
|
|||
|
|
surface.albedo_texture = load("res://assets/materials/concrete_color.jpg")
|
|||
|
|
surface.normal_enabled = true
|
|||
|
|
surface.normal_texture = load("res://assets/materials/concrete_normal.jpg")
|
|||
|
|
surface.uv1_scale = Vector3(2, 2, 2)
|
|||
|
|
var pad := box(Vector3(0, -0.13, -0.15), Vector3(0.54, 0.06, 0.91), Color("121616"))
|
|||
|
|
pad.rotation.y = -0.2
|
|||
|
|
for index in 10:
|
|||
|
|
var seam := box(Vector3(-0.23 + index * 0.05, -0.097, -0.15), Vector3(0.003, 0.005, 0.84), Color("252a28"))
|
|||
|
|
seam.rotation.y = -0.2
|
|||
|
|
sidearm = WeaponModels.build(4)
|
|||
|
|
bench.stage.add_child(sidearm)
|
|||
|
|
sidearm.position = Vector3(0.39, -0.06, 0.43)
|
|||
|
|
sidearm.rotation = Vector3(0, -0.65, PI / 2)
|
|||
|
|
for index in 18:
|
|||
|
|
var shell := box(Vector3(sin(index * 4.7) * 0.5, -0.125, cos(index * 2.8) * 0.7), Vector3(0.009, 0.009, 0.035), Color("9e8c55"))
|
|||
|
|
shell.rotation.y = index * 1.7
|
|||
|
|
for index in 3:
|
|||
|
|
var bottle := MeshInstance3D.new()
|
|||
|
|
var cylinder := CylinderMesh.new()
|
|||
|
|
cylinder.top_radius = 0.033
|
|||
|
|
cylinder.bottom_radius = 0.038
|
|||
|
|
cylinder.height = 0.19
|
|||
|
|
bottle.mesh = cylinder
|
|||
|
|
bottle.position = Vector3(0.44, -0.025, -0.6 + index * 0.17)
|
|||
|
|
var mat := StandardMaterial3D.new()
|
|||
|
|
mat.albedo_color = Color("303d30")
|
|||
|
|
mat.metallic = 0.45
|
|||
|
|
mat.roughness = 0.45
|
|||
|
|
bottle.material_override = mat
|
|||
|
|
bench.stage.add_child(bottle)
|
|||
|
|
box(bottle.position + Vector3(0, 0.1, 0), Vector3(0.025, 0.02, 0.025), Color("8e918c"))
|
|||
|
|
box(Vector3(-0.4, -0.075, 0.3), Vector3(0.2, 0.15, 0.24), Color("625741"))
|
|||
|
|
box(Vector3(-0.4, 0.003, 0.3), Vector3(0.08, 0.005, 0.035), Color("bfbeb2"))
|
|||
|
|
box(Vector3(-0.4, 0.004, 0.3), Vector3(0.035, 0.005, 0.08), Color("bfbeb2"))
|
|||
|
|
|
|||
|
|
func refresh() -> void:
|
|||
|
|
if not is_instance_valid(primary): return
|
|||
|
|
for child in classes.get_children():
|
|||
|
|
classes.remove_child(child)
|
|||
|
|
child.queue_free()
|
|||
|
|
for index in ClassLoadouts.entries.size():
|
|||
|
|
var button := tile(str(ClassLoadouts.entries[index].name) + (" ★" if index == ClassLoadouts.selected else ""), Vector2.ZERO, Vector2(308, 50), func():
|
|||
|
|
ClassLoadouts.entries[editor.slot] = ClassLoadouts.clean(editor.draft)
|
|||
|
|
editor.slot = index
|
|||
|
|
editor.draft = ClassLoadouts.entries[index].duplicate(true)
|
|||
|
|
editor.refresh())
|
|||
|
|
button.reparent(classes)
|
|||
|
|
button.custom_minimum_size = Vector2(300, 50)
|
|||
|
|
button.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|||
|
|
button.add_theme_color_override("font_color", CYAN if index == editor.slot else Color("55747e"))
|
|||
|
|
if index == editor.slot: button.grab_focus()
|
|||
|
|
var id := int(editor.draft.primary)
|
|||
|
|
for index in 2:
|
|||
|
|
var weapon_id := id if index == 0 else 4
|
|||
|
|
weapon_names[index].text = Arsenal.DATA[weapon_id].name
|
|||
|
|
var design: Dictionary = editor.draft.skins.get(str(weapon_id), {})
|
|||
|
|
var attachments: Dictionary = design.get("attachments", {})
|
|||
|
|
var count := 0
|
|||
|
|
for value: Variant in attachments.values():
|
|||
|
|
if int(value) != 0: count += 1
|
|||
|
|
attachment_labels[index].text = "●".repeat(count) + "○".repeat(maxi(0, 5 - count))
|
|||
|
|
thumbnails[index].select_weapon(weapon_id)
|
|||
|
|
WeaponSkins.apply(thumbnails[index].model, design)
|
|||
|
|
thumbnails[index].camera.position = Vector3(1.4, 0, -0.15)
|
|||
|
|
thumbnails[index].camera.look_at(Vector3(0, -0.025, -0.15))
|
|||
|
|
thumbnails[index].camera.size = 0.25 if weapon_id == 4 else 0.42
|
|||
|
|
WeaponSkins.apply(sidearm, editor.draft.skins.get("4", {}))
|
|||
|
|
bench.select_weapon(id)
|
|||
|
|
WeaponSkins.apply(bench.model, editor.draft.skins.get(str(id), {}))
|
|||
|
|
bench.model.rotation = Vector3(0, -0.2, PI / 2)
|
|||
|
|
bench.model.position = Vector3(0, -0.025, 0.1)
|
|||
|
|
bench.camera.position = Vector3(1.15, 1.7, 1.1)
|
|||
|
|
bench.camera.look_at(Vector3(0, -0.1, -0.1))
|
|||
|
|
bench.camera.size = 1.75
|
|||
|
|
|
|||
|
|
func open_weapon(is_secondary: bool) -> void:
|
|||
|
|
editor.secondary = is_secondary
|
|||
|
|
editor.refresh()
|
|||
|
|
editor.show_workshop()
|
|||
|
|
|
|||
|
|
func _process(_dt: float) -> void:
|
|||
|
|
if is_instance_valid(feedback): feedback.text = editor.notice.text
|