Files

76 lines
3.0 KiB
GDScript
Raw Permalink Normal View History

2026-09-12 11:01:37 +02:00
class_name EnergyShield
extends Node3D
const DURATION := 45.0
var fighter: Fighter
var opening := 0.0
var surface: MeshInstance3D
static func activate(p: Fighter) -> void:
if not p.game.is_host or not p.game.active or p.game.match_over or p.hp <= 0 or p.shield_charges <= 0 or p.shield_left > 0: return
p.shield_charges -= 1
p.shield_left = DURATION
p.protection = 0
static func blocks(p: Fighter, origin: Vector3) -> bool:
if p.shield_left <= 0 or p.hp <= 0 or not origin.is_finite(): return false
var offset := origin - p.eye()
return offset.length_squared() > 0.001 and Arsenal.direction(p.yaw, p.pitch).dot(offset.normalized()) > 0.01
static func pose(p: Fighter) -> Transform3D:
var facing := Basis.from_euler(Vector3(p.pitch, p.yaw, 0))
return Transform3D(facing, p.eye() + facing * Vector3(0, -0.4, -0.95))
static func clip_ray(game: Node3D, source: Fighter, start: Vector3, end: Vector3, original: Dictionary) -> Dictionary:
var best := start.distance_to(original.position) if not original.is_empty() else start.distance_to(end)
var result := original
for target: Fighter in game.players.values():
if target == source or target.hp <= 0 or target.shield_left <= 0: continue
var transform := pose(target)
var a := transform.affine_inverse() * start
var b := transform.affine_inverse() * end
# Outward face is -Z. One-way shield: incoming rays only.
if a.z >= 0 or b.z <= a.z: continue
var t := -a.z / (b.z - a.z)
if t < 0 or t > 1: continue
var point := a.lerp(b, t)
if absf(point.x) > 1.2 or absf(point.y) > 1.35: continue
var world := transform * point
if start.distance_to(world) < best:
best = start.distance_to(world)
result = {"position": world, "collider": null, "shield": target}
return result
func _ready() -> void:
surface = MeshInstance3D.new()
var panel := QuadMesh.new()
panel.size = Vector2(2.4, 2.7)
surface.mesh = panel
surface.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
var shader := Shader.new()
shader.code = """shader_type spatial;
render_mode unshaded, cull_disabled, depth_draw_never;
void fragment() {
vec2 edge = abs(UV - vec2(0.5)) * 2.0;
float rim = smoothstep(0.94, 0.99, max(edge.x, edge.y));
vec2 cell = abs(fract(UV * vec2(18.0, 20.0)) - vec2(0.5));
float grid = smoothstep(0.465, 0.495, max(cell.x, cell.y));
float sweep = pow(max(0.0, sin(UV.y * 24.0 - TIME * 3.0)), 18.0);
ALBEDO = mix(vec3(0.10, 0.65, 1.0), vec3(0.90, 0.98, 1.0), rim);
EMISSION = ALBEDO * (0.7 + rim * 2.5);
ALPHA = 0.035 + rim * 0.78 + grid * 0.15 + sweep * 0.06;
}"""
var material := ShaderMaterial.new()
material.shader = shader
surface.material_override = material
add_child(surface)
visible = false
func _process(dt: float) -> void:
var enabled: bool = fighter.game.active and fighter.hp > 0 and fighter.shield_left > 0
opening = move_toward(opening, 1.0 if enabled else 0.0, dt * 6)
visible = opening > 0
if not visible: return
global_transform = pose(fighter)
surface.scale = Vector3(maxf(0.01, WeaponHandling.smooth(opening)), 1, 1)