extends Node3D ## Rigid stone joints: meshes shared across units, transforms owned per instance. var asset_id: String = "" var parts: Dictionary = {} var rest_bounds: AABB var _walk: float = 0.0 var _hit_material: StandardMaterial3D var _core_health: float = 1.0 var _core_damage: float = 0.0 var _core_activate: float = 0.0 func setup(id: String, groups: Dictionary, bounds: AABB) -> void: asset_id = id rest_bounds = bounds name = "Animated_" + id _hit_material = StandardMaterial3D.new() _hit_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED _hit_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA _hit_material.albedo_color = Color(1.0, .83, .49, 0.0) for key: String in groups: var instance := MeshInstance3D.new() instance.name = key instance.mesh = groups[key].mesh instance.position = groups[key].pivot instance.set_meta("rest", instance.position) instance.material_overlay = _hit_material add_child(instance) parts[key] = instance func set_core_health(ratio: float) -> void: var next: float = clampf(ratio, 0.0, 1.0) if next < _core_health - .002: _core_damage = 1.0 _core_health = next func trigger_activate() -> void: _core_activate = 1.0 func pose(time: float, moving: bool, attack: float, ability: float, hit: float, delta: float = 0.0) -> void: _walk = move_toward(_walk, 1.0 if moving else 0.0, .12) var stride: float = sin(time * 8.0) * _walk var strike: float = sin(clampf(attack / .28, 0.0, 1.0) * PI) _core_damage = maxf(0.0, _core_damage - delta * 1.8) _core_activate = maxf(0.0, _core_activate - delta * .55) _hit_material.albedo_color.a = clampf(maxf(hit * 2.4, _core_damage * .45), 0.0, .5) for key: String in parts: var part: MeshInstance3D = parts[key] part.position = part.get_meta("rest") part.rotation = Vector3.ZERO part.scale = Vector3.ONE if key.begins_with("Leg"): var side: float = 1.0 if key.begins_with("LegL") else -1.0 if key.right(1).is_valid_int() and int(key.right(1)) % 2 == 1: side *= -1.0 part.rotation.x = stride * side * .30 part.position.y += maxf(0, stride * side) * .05 continue match key: "ArmL", "ArmR": var side: float = 1.0 if key == "ArmL" else -1.0 part.rotation.x = -stride * side * .16 - strike * (.52 if asset_id in ["g01","g02","g03","g04","e05","e08"] else .12) part.rotation.z = strike * side * .08 "Weapon": part.position.z -= strike * (.22 if asset_id == "g06" else .10) if asset_id == "g05": part.rotation.y = strike * .22 "Mortar": part.rotation.x = -strike * .16 part.position.y -= strike * .07 "Flame": part.scale.y = 1.0 + sin(time * 11.0) * .09 + ability * .45 "Crystal": # Activation flare, calm float, then a readable shudder when hurt. var distress: float = 1.0 - _core_health var pulse: float = sin(time * (1.5 + distress * 2.2)) part.position.y += pulse * (.045 + distress * .018) + _core_activate * .14 part.rotation.y = sin(time * .35) * .08 + _core_damage * .16 * sin(time * 36.0) part.scale = Vector3.ONE * (1.0 + _core_activate * .18 + _core_damage * .07) "Body": part.rotation.z = stride * .025 if asset_id in ["e03","e06","e09"]: part.position.y += sin(time*2.4)*.06 if asset_id == "e10": part.rotation.x = -strike*.15 part.position.z -= strike*.10 func get_aabb() -> AABB: return rest_bounds