113 lines
4.5 KiB
GDScript
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
|