109 lines
4.6 KiB
GDScript
109 lines
4.6 KiB
GDScript
class_name WeaponSkins
|
|
extends RefCounted
|
|
|
|
const PARTS := ["receiver", "handguard", "stock", "barrel", "grip", "optic", "magazine", "bolt"]
|
|
const LABELS := ["Gehäuse", "Handschutz", "Schaft", "Lauf", "Griff", "Visierung", "Magazin", "Verschluss"]
|
|
static var designs: Dictionary = {}
|
|
static var revision := 0
|
|
|
|
static func clean(value: Variant) -> Dictionary:
|
|
if not value is Dictionary: return {}
|
|
var result := {}
|
|
result.soviet_variant = 1 if value.get("soviet_variant",0) == 1 else 0
|
|
for key in PARTS + ["text_color"]:
|
|
if value.get(key) is Color:
|
|
var c: Color = value[key]
|
|
if is_finite(c.r) and is_finite(c.g) and is_finite(c.b): result[key] = Color(clampf(c.r, 0, 1), clampf(c.g, 0, 1), clampf(c.b, 0, 1), 1)
|
|
var words := str(value.get("text", "")).replace("\n", " ").replace("\r", " ").replace("\t", " ").left(32)
|
|
result.text = words
|
|
result.attachments = WeaponAttachments.clean(value.get("attachments", {}))
|
|
result.stickers = WeaponStickers.clean(value.get("stickers", []))
|
|
return result
|
|
|
|
static func set_design(weapon: int, design: Dictionary) -> void:
|
|
if weapon < 0 or weapon >= Arsenal.DATA.size(): return
|
|
designs[str(weapon)] = clean(design)
|
|
revision += 1
|
|
|
|
static func get_design(weapon: int) -> Dictionary:
|
|
return clean(designs.get(str(weapon), {}))
|
|
|
|
static func split_frame(frame: Node3D) -> void:
|
|
# Preserve semantic parts before batching so customization never recolors an entire weapon.
|
|
var nodes := {}
|
|
for part in PARTS.slice(0, 6):
|
|
var group := Node3D.new()
|
|
group.name = part
|
|
frame.add_child(group)
|
|
nodes[part] = group
|
|
for child in frame.get_children():
|
|
if not child is MeshInstance3D: continue
|
|
var at: Vector3 = child.transform * child.mesh.get_aabb().get_center()
|
|
var part := "receiver"
|
|
if at.z > 0.10: part = "stock"
|
|
elif at.z < -0.53: part = "barrel"
|
|
elif at.z < -0.23: part = "handguard"
|
|
elif at.y > 0.065: part = "optic"
|
|
elif at.y < -0.055: part = "grip"
|
|
frame.remove_child(child)
|
|
nodes[part].add_child(child)
|
|
for part in nodes:
|
|
WeaponGeometry.batch(nodes[part])
|
|
nodes[part].set_meta("skin_part", part)
|
|
|
|
static func apply(model: Node3D, design: Dictionary) -> void:
|
|
design = clean(design)
|
|
if model.has_meta("soviet_model"): SovietWeapons.variant(model,design)
|
|
WeaponAttachments.apply(model, design)
|
|
WeaponStickers.apply(model, design.get("stickers", []))
|
|
for part in PARTS:
|
|
var group := model.get_node_or_null("Frame/" + part)
|
|
if part == "magazine": group = model.get_node_or_null("Magazine")
|
|
if part == "bolt": group = model.get_node_or_null("Bolt")
|
|
if group == null: continue
|
|
var meshes := group.get_children()
|
|
if part == "optic" and model.has_node("Frame/FactoryOptic"):
|
|
meshes.append_array(model.get_node("Frame/FactoryOptic").get_children())
|
|
for child in meshes:
|
|
if not child is MeshInstance3D: continue
|
|
if not child.has_meta("factory_material"): child.set_meta("factory_material", child.material_override)
|
|
var factory: StandardMaterial3D = child.get_meta("factory_material")
|
|
child.material_override = factory
|
|
if factory and design.has(part) and not factory.emission_enabled:
|
|
var finish := factory.duplicate() as StandardMaterial3D
|
|
finish.albedo_color = design[part]
|
|
child.material_override = finish
|
|
var lettering := model.get_node_or_null("CustomLettering")
|
|
var pistol := int(model.get_meta("weapon_index", 0)) == 4
|
|
if lettering == null: lettering = model.get_node_or_null("Bolt/CustomLettering")
|
|
if lettering == null:
|
|
lettering = Node3D.new()
|
|
lettering.name = "CustomLettering"
|
|
if pistol: model.get_node("Bolt").add_child(lettering)
|
|
else: model.add_child(lettering)
|
|
for side in [-1, 1]:
|
|
var label := Label3D.new()
|
|
label.name = "Text" + str(side)
|
|
var surface_x := 0.0193 if pistol else (0.0466 if int(model.get_meta("weapon_index", 0)) == 7 else 0.036)
|
|
label.position = Vector3(side * surface_x, 0.045 if pistol else 0.018, -0.07)
|
|
label.rotation.y = side * PI / 2
|
|
label.pixel_size = 0.00035
|
|
label.font_size = 48
|
|
label.outline_size = 0
|
|
label.no_depth_test = false
|
|
lettering.add_child(label)
|
|
for label: Label3D in lettering.get_children():
|
|
label.text = design.get("text", "")
|
|
label.pixel_size = minf(0.00018 if pistol else 0.00035, (0.09 if pistol else 0.15) / maxf(1, label.text.length() * 28))
|
|
label.modulate = design.get("text_color", Color.WHITE)
|
|
|
|
static func loadout_designs(primary: int) -> Dictionary:
|
|
return clean_loadout(designs, primary)
|
|
|
|
static func clean_loadout(value: Variant, primary: int) -> Dictionary:
|
|
var result := {}
|
|
if value is Dictionary:
|
|
for index in [Arsenal.primary_id(primary), 4]:
|
|
result[str(index)] = clean(value.get(str(index), {}))
|
|
return result
|