Files
Blockline/scripts/weapon_stickers.gd
T

113 lines
4.5 KiB
GDScript

class_name WeaponStickers
extends RefCounted
static var directory := "user://stickers/"
static var cache := {}
static var error := ""
static func valid(id: String) -> bool:
if id.length() != 64: return false
for c in id:
if not c in "0123456789abcdef": return false
return true
static func import_file(path: String) -> String:
error = ""
if not path.get_extension().to_lower() in ["png", "jpg", "jpeg", "webp"]:
error = "Bitte PNG, JPG oder WebP auswählen."; return ""
var source := FileAccess.open(path, FileAccess.READ)
if source == null: error = "Datei konnte nicht geöffnet werden."; return ""
if source.get_length() > 8 * 1024 * 1024:
error = "Das Bild darf höchstens 8 MB groß sein."; return ""
var img := Image.load_from_file(path)
if img == null or img.is_empty(): error = "Bild konnte nicht gelesen werden."; return ""
img.convert(Image.FORMAT_RGBA8)
var factor := minf(1, 256.0 / maxf(img.get_width(), img.get_height()))
img.resize(maxi(1, int(img.get_width() * factor)), maxi(1, int(img.get_height() * factor)), Image.INTERPOLATE_LANCZOS)
var bytes := img.save_png_to_buffer()
var id := bytes.hex_encode().sha256_text()
DirAccess.make_dir_recursive_absolute(directory)
var file := FileAccess.open(directory + id + ".png", FileAccess.WRITE)
if file == null: error = "Sticker konnte nicht gespeichert werden."; return ""
file.store_buffer(bytes)
return id
static func texture(id: String) -> Texture2D:
if not valid(id): return null
if not cache.has(id):
if not FileAccess.file_exists(directory + id + ".png"): return null
var img := Image.load_from_file(directory + id + ".png")
if img == null: return null
img.generate_mipmaps()
cache[id] = ImageTexture.create_from_image(img)
return cache[id]
static func clean(value: Variant) -> Array:
var result := []
if value is Array:
for i in mini(value.size(), 3):
var id := str(value[i])
result.append(id if valid(id) else "")
while result.size() < 3: result.append("")
return result
static func apply(model: Node3D, value: Variant) -> void:
var old := model.get_node_or_null("Stickers")
if old == null: old = model.get_node_or_null("Bolt/Stickers")
if old: old.get_parent().remove_child(old); old.queue_free()
var group := Node3D.new()
group.name = "Stickers"
var index := int(model.get_meta("weapon_index", 0))
var pistol := index == 4
if pistol: model.get_node("Bolt").add_child(group)
else: model.add_child(group)
var ids := clean(value)
for i in 3:
var tex := texture(ids[i])
if tex == null: continue
for side in [-1, 1]:
var sticker := MeshInstance3D.new()
var mesh := QuadMesh.new()
var width := 0.028 if pistol else 0.043
mesh.size = Vector2(width, width * tex.get_height() / tex.get_width())
sticker.mesh = mesh
var surface_x := 0.0192 if pistol else (0.0465 if index == 7 else 0.0358)
sticker.position = Vector3(side * surface_x, 0.036 if pistol else 0.0, 0.015 - i * (0.065 if pistol else 0.075))
sticker.rotation.y = side * PI / 2
var mat := StandardMaterial3D.new()
mat.albedo_texture = tex
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
mat.roughness = 0.75
mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC
sticker.material_override = mat
group.add_child(sticker)
static func bundle(designs: Dictionary) -> Dictionary:
var result := {}
for design in designs.values():
for id in clean(design.get("stickers", [])):
if valid(id) and FileAccess.file_exists(directory + id + ".png"):
result[id] = FileAccess.get_file_as_bytes(directory + id + ".png")
return result
static func accept(payload: Dictionary) -> Dictionary:
var accepted := {}
if payload.size() > 6: return accepted
for key in payload:
var id := str(key)
var bytes: Variant = payload[key]
if not valid(id) or not bytes is PackedByteArray or bytes.size() < 24 or bytes.size() > 270000: continue
if bytes.slice(0, 8) != PackedByteArray([137, 80, 78, 71, 13, 10, 26, 10]): continue
var width: int = (int(bytes[16]) << 24) | (int(bytes[17]) << 16) | (int(bytes[18]) << 8) | bytes[19]
var height: int = (int(bytes[20]) << 24) | (int(bytes[21]) << 16) | (int(bytes[22]) << 8) | bytes[23]
if width < 1 or height < 1 or width > 256 or height > 256: continue
if bytes.hex_encode().sha256_text() != id: continue
var img := Image.new()
if img.load_png_from_buffer(bytes) != OK: continue
DirAccess.make_dir_recursive_absolute(directory)
var file := FileAccess.open(directory + id + ".png", FileAccess.WRITE)
if file == null: continue
file.store_buffer(bytes); file.close()
accepted[id] = bytes
return accepted