- assets/audio/music/zombie_industrial_loop.ogg: 57.60 s Ogg Vorbis loop generated locally with YuE2-3B on the Tesla V100 (score written for this track; vocals removed with HTDemucs). The loop is spliced at the track's structural repeat with a 0.6 s crossfade: measured seam dip <= 1.6 dB. - scripts/main.gd: "Music" AudioStreamPlayer at -11 dB, started at boot on desktop and on the start click on Web (browser autoplay policy). It forces stream.loop at runtime because the Web staging step re-imports the OGG with loop=false. - tools/test_music.gd: soundtrack test (loop, length, volume, playback, still playing after player death). - docs/MUSIC.md: source, loop construction, verification, licence. - docs/WEB-DEPLOY.md: release ab6054111f5919a8 notes. Initial commit of the project.
67 lines
2.4 KiB
GDScript
67 lines
2.4 KiB
GDScript
extends RefCounted
|
|
const MAX_STAINS := 160
|
|
|
|
static func impact(world: Node3D, point: Vector3, direction: Vector3) -> void:
|
|
# CPU particles work in both Forward+ and WebGL Compatibility.
|
|
var particles := CPUParticles3D.new()
|
|
particles.name = "BloodSpray"
|
|
particles.amount = 28
|
|
particles.lifetime = 0.65
|
|
particles.one_shot = true
|
|
particles.explosiveness = 1.0
|
|
particles.direction = direction.normalized()
|
|
particles.spread = 65
|
|
particles.initial_velocity_min = 1.3
|
|
particles.initial_velocity_max = 3.5
|
|
particles.gravity = Vector3(0, -9.8, 0)
|
|
particles.scale_amount_min = 0.5
|
|
particles.scale_amount_max = 1.5
|
|
var mesh := SphereMesh.new()
|
|
mesh.radius = 0.018
|
|
mesh.height = 0.036
|
|
var material := StandardMaterial3D.new()
|
|
material.albedo_color = Color(0.45, 0.015, 0.025)
|
|
material.roughness = 0.35
|
|
mesh.material = material
|
|
particles.mesh = mesh
|
|
world.add_child(particles)
|
|
particles.global_position = point
|
|
particles.emitting = true
|
|
world.get_tree().create_timer(1.3).timeout.connect(particles.queue_free)
|
|
var query := PhysicsRayQueryParameters3D.create(point + Vector3.UP * 0.05, point + Vector3.DOWN * 8, 1)
|
|
var hit := world.get_world_3d().direct_space_state.intersect_ray(query)
|
|
if hit.is_empty() or hit.normal.dot(Vector3.UP) < 0.7:
|
|
return
|
|
var width := randf_range(0.55, 1.15)
|
|
var stain: Node3D
|
|
if RenderingServer.get_current_rendering_method() == "gl_compatibility":
|
|
# WebGL has no projected decals: textured horizontal patch on floor.
|
|
var patch := MeshInstance3D.new()
|
|
var plane := PlaneMesh.new()
|
|
plane.size = Vector2(width, width)
|
|
var mat := StandardMaterial3D.new()
|
|
mat.albedo_texture = load("res://assets/effects/blood.png")
|
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED
|
|
mat.roughness = 0.9
|
|
plane.material = mat
|
|
patch.mesh = plane
|
|
patch.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
|
stain = patch
|
|
else:
|
|
var decal := Decal.new()
|
|
decal.texture_albedo = load("res://assets/effects/blood.png")
|
|
decal.size = Vector3(width, 0.25, width)
|
|
decal.cull_mask = 1
|
|
decal.upper_fade = 0
|
|
decal.lower_fade = 0
|
|
stain = decal
|
|
stain.name = "BloodStain"
|
|
world.add_child(stain)
|
|
stain.global_position = hit.position + Vector3.UP * 0.012
|
|
stain.rotation.y = randf_range(0, TAU)
|
|
stain.add_to_group("blood_stains")
|
|
var stains := world.get_tree().get_nodes_in_group("blood_stains")
|
|
if stains.size() > MAX_STAINS:
|
|
stains[0].queue_free()
|