saumwacht-godot/scripts/presentation/enemy_visuals.gd
Martin Stickelbroeck c2b447610c Baseline: Saumwacht 0.15.0 (Phase 20A) vor dem Umbau zu Champions of Luneraria
Unveränderter Ausgangsstand inklusive Masterprompt und Konzeptbildern
unter assets/. .godot/ und docs/screenshots/ bleiben laut .gitignore
unversioniert; letztere sind unter ../backups gesichert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:00:36 +02:00

128 lines
5.7 KiB
GDScript

extends RefCounted
## Original rift creatures for the six enemy types used in the eight-wave slice.
## Generated once, then batched by material. They have no Phase 3 icon reference.
static var _cache: Dictionary = {}
static func create(data: Dictionary) -> Node3D:
var id: String = data.get("id", "")
if not id in ["e01", "e02", "e03", "e04", "e07", "e08"]:
return null
if not _cache.has(id):
var root := Node3D.new()
_build(root, id)
_cache[id] = AssetLibrary.bake_mesh(root)
root.free()
var result := MeshInstance3D.new()
result.name = "Rift_" + id
result.mesh = _cache[id]
return result
static func _mat(hex: String, glow: bool = false) -> StandardMaterial3D:
var mat := StandardMaterial3D.new()
mat.albedo_color = Color(hex)
mat.roughness = 0.85
mat.emission_enabled = glow
mat.emission = Color(hex)
mat.emission_energy_multiplier = 0.35
return mat
static func _piece(root: Node3D, at: Vector3, size: Vector3, mat: Material, box: bool = false) -> MeshInstance3D:
var instance := MeshInstance3D.new()
if box:
var mesh := BoxMesh.new()
mesh.size = size
instance.mesh = mesh
else:
var mesh := SphereMesh.new()
mesh.radius = 0.5
mesh.height = 1.0
mesh.radial_segments = 6
mesh.rings = 2
instance.mesh = mesh
instance.scale = size
instance.material_override = mat
instance.position = at
root.add_child(instance)
return instance
static func _limb(root: Node3D, start: Vector3, end: Vector3, radius: float, mat: Material, tapered: bool = false) -> void:
var mesh := CylinderMesh.new()
mesh.height = start.distance_to(end)
mesh.bottom_radius = radius
mesh.top_radius = 0.0 if tapered else radius * 0.7
mesh.radial_segments = 5
var instance := MeshInstance3D.new()
instance.mesh = mesh
instance.material_override = mat
instance.position = (start + end) * 0.5
instance.quaternion = Quaternion(Vector3.UP, (end - start).normalized())
root.add_child(instance)
static func _build(root: Node3D, id: String) -> void:
var dark := _mat("323e50")
var shell := _mat("869aa6")
var pale := _mat("d6dedc")
var rift := _mat("d27b94", true)
if id in ["e01", "e02"]:
var heavy: bool = id == "e02"
_piece(root, Vector3(0, .62, -.10), Vector3(1.12, .75 if heavy else .48, 1.15), dark)
_piece(root, Vector3(0, .91 if heavy else .74, -.18), Vector3(1.08, .68 if heavy else .35, 1.1), shell)
_piece(root, Vector3(0, .60, .54), Vector3(.60, .42, .48), dark)
for side: float in [-1.0, 1.0]:
for index: int in range(3):
var z: float = -.50 + index * .40
var knee := Vector3(side * .73, .47, z + .10)
_limb(root, Vector3(side * .30, .62, z), knee, .12 if heavy else .07, shell)
_limb(root, knee, Vector3(side * .83, .04, z + .22), .075, dark, true)
_piece(root, Vector3(side * .16, .69, .74), Vector3(.11, .10, .08), rift)
_limb(root, Vector3(side * .22, .43, .65), Vector3(side * .40, .55, .92), .07, pale, true)
if heavy:
for z: float in [-.45, -.10, .25]:
_limb(root, Vector3(0, 1.00, z), Vector3(0, 1.42, z -.10), .14, pale, true)
elif id == "e03":
_piece(root, Vector3(0, 1.14, 0), Vector3(.60, .75, .48), dark)
_piece(root, Vector3(0, 1.58, .09), Vector3(.42, .59, .24), pale)
for side: float in [-1.0, 1.0]:
_piece(root, Vector3(side * .11, 1.61, .21), Vector3(.08, .15, .07), rift)
_limb(root, Vector3(side*.23,1.25,0), Vector3(side*.57,.95,.17), .12,shell)
_limb(root, Vector3(side*.57,.95,.17), Vector3(side*.68,1.29,.27), .09,pale,true)
for index: int in range(3):
var x: float = (index-1)*.22
_limb(root,Vector3(x,1.0,0),Vector3(x-.12,.43,.04),.15,dark)
_limb(root,Vector3(x-.12,.43,.04),Vector3(x+.13,.05,.14),.09,shell,true)
elif id == "e04":
_piece(root, Vector3(0, 1.02, 0), Vector3(.42, .65, .35), dark)
_piece(root, Vector3(0, 1.54, .05), Vector3(.38, .43, .31), pale)
_piece(root, Vector3(0, 1.55, .23), Vector3(.23, .055, .05), rift, true)
for side: float in [-1.0,1.0]:
_limb(root,Vector3(side*.17,.88,0),Vector3(side*.28,.46,-.06),.10,shell)
_limb(root,Vector3(side*.28,.46,-.06),Vector3(side*.32,.03,.22),.07,dark)
_limb(root,Vector3(side*.20,1.23,0),Vector3(side*.46,1.07,.34),.07,shell)
_limb(root,Vector3(0,1.12,.45),Vector3(side*.61,1.15,.65),.06,pale)
_limb(root,Vector3(0,1.12,.20),Vector3(0,1.12,.94),.065,rift,true)
elif id == "e07":
_piece(root, Vector3(0, 1.03, -.22), Vector3(.90, .88, 1.37), dark)
_piece(root, Vector3(0, 1.61, .39), Vector3(.66, .85, .71), shell)
_piece(root, Vector3(0, 1.62, .82), Vector3(.38, .31, .51), pale)
for side: float in [-1.0,1.0]:
for z: float in [-.65,.35]:
_limb(root,Vector3(side*.29,.91,z),Vector3(side*.42,.09,z+.08),.14,dark)
_piece(root,Vector3(side*.42,.09,z+.13),Vector3(.27,.17,.31),pale,true)
_piece(root,Vector3(side*.25,1.78,.67),Vector3(.10,.10,.14),rift)
_limb(root,Vector3(side*.22,1.89,.40),Vector3(side*.64,2.46,.10),.11,pale,true)
_limb(root,Vector3(side*.45,2.17,.24),Vector3(side*.89,2.39,.40),.08,pale,true)
_limb(root,Vector3(side*.54,2.29,.17),Vector3(side*.52,2.61,.40),.06,pale,true)
else:
_piece(root,Vector3(0,1.07,0),Vector3(1.03,1.32,.66),dark,true)
_piece(root,Vector3(0,1.23,.37),Vector3(.69,.91,.17),shell,true)
_piece(root,Vector3(0,1.26,.48),Vector3(.11,.70,.06),rift,true)
_piece(root,Vector3(0,1.98,.08),Vector3(.52,.44,.46),pale,true)
_piece(root,Vector3(0,1.99,.32),Vector3(.35,.08,.05),rift,true)
for side: float in [-1.0,1.0]:
_piece(root,Vector3(side*.38,.29,0),Vector3(.40,.59,.49),shell,true)
_piece(root,Vector3(side*.39,.09,.20),Vector3(.48,.17,.64),dark,true)
_piece(root,Vector3(side*.78,1.22,.03),Vector3(.42,1.13,.55),shell,true)
for index: int in range(2):
_piece(root,Vector3(side*.78,1.89,-.14+index*.34),Vector3(.42,.24,.16),pale,true)
_limb(root,Vector3(side*.76,.99,.34),Vector3(side*.80,.90,.97),.13,dark)