extends Node3D ## Original Half-Life fireball, 15 frames at 15 FPS, additive camera-facing quad. ## A PNG atlas avoids the native GoldSrc importer in Web exports. const ATLAS = preload("res://assets/effects/explosion/zerogxplode_atlas.png") const FRAME_COUNT := 15 const FPS := 15.0 const DURATION := FRAME_COUNT / FPS const SIZE := 3.2 var elapsed := 0.0 var frame_index := 0 var fireball: MeshInstance3D var material: StandardMaterial3D func _ready() -> void: name = "GrenadeExplosion" material = StandardMaterial3D.new() material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA material.blend_mode = BaseMaterial3D.BLEND_MODE_ADD material.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED material.cull_mode = BaseMaterial3D.CULL_DISABLED material.albedo_texture = ATLAS material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR material.texture_repeat = false # Two-pixel black gutters prevent adjacent frames bleeding under filtering. material.uv1_scale = Vector3(64.0 / 340.0, 64.0 / 204.0, 1) fireball = MeshInstance3D.new() fireball.name = "Fireball" fireball.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF var quad := QuadMesh.new() quad.size = Vector2(SIZE, SIZE) quad.material = material fireball.mesh = quad # Raise the fireball, but leave the damage origin at the physical impact. fireball.position.y = 0.8 add_child(fireball) _set_frame(0) var light := OmniLight3D.new() light.name = "Flash" light.light_color = Color(1, 0.45, 0.08) light.light_energy = 8 light.omni_range = 10 light.shadow_enabled = false add_child(light) create_tween().tween_property(light, "light_energy", 0.0, 0.3) # Keep the node alive for the original explosion sound to finish. get_tree().create_timer(4).timeout.connect(queue_free) func _process(delta: float) -> void: elapsed += delta if elapsed >= DURATION: fireball.hide() set_process(false) return _set_frame(mini(int(elapsed * FPS), FRAME_COUNT - 1)) func _set_frame(index: int) -> void: frame_index = index material.uv1_offset = Vector3( (float(index % 5) * 68 + 2) / 340.0, (float(index / 5) * 68 + 2) / 204.0, 0)