Files

214 lines
9.3 KiB
GDScript
Raw Permalink Normal View History

2026-09-12 11:01:37 +02:00
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