c0a022fed4
An Fx autoload + one_shot_effect base so every effect is an inspector-editable .tscn (the systems/fx/ authoring dir): blood/slam/spark/flash primitives and the bull's kick/dash/slam/roll + movement effects (bull_*.tscn), fired via Fx.spawn() / Fx.attach(). Plus a preview gallery + smoke test, and bull_impact_fx.gd for the coded, non-particle slam rings + kick flash-pop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CCyaJPTHCH9EDthpf4A5Sj
201 lines
5.7 KiB
GDScript
201 lines
5.7 KiB
GDScript
extends SceneTree
|
|
## Seeds the placeholder effect .tscn (and the gallery scene) once, headless. Those .tscn are the
|
|
## source of truth Siim edits in the inspector — this script only builds their first-draft defaults
|
|
## (ported from the old code-built particles). Re-run only to reset a placeholder from scratch:
|
|
## godot --headless --script tools/seed_fx_placeholders.gd
|
|
|
|
const _EFFECT_SCRIPT := "res://systems/fx/one_shot_effect.gd"
|
|
const _GALLERY_SCRIPT := "res://systems/fx/effect_gallery.gd"
|
|
|
|
|
|
func _init() -> void:
|
|
_seed_blood_burst()
|
|
_seed_slam_dust()
|
|
_seed_spark()
|
|
_seed_hit_flash()
|
|
_seed_gallery()
|
|
print("Seeded FX placeholder scenes into res://systems/fx/")
|
|
quit()
|
|
|
|
|
|
# ── shared builders ─────────────────────────────────────────────────────────
|
|
func _unshaded() -> StandardMaterial3D:
|
|
var m := StandardMaterial3D.new()
|
|
m.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
m.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
m.vertex_color_use_as_albedo = true
|
|
m.cull_mode = BaseMaterial3D.CULL_DISABLED
|
|
return m
|
|
|
|
|
|
func _sphere(radius: float) -> SphereMesh:
|
|
var s := SphereMesh.new()
|
|
s.radius = radius
|
|
s.height = radius * 2.0
|
|
s.radial_segments = 4
|
|
s.rings = 2
|
|
s.material = _unshaded()
|
|
return s
|
|
|
|
|
|
func _ramp(c0: Color, c1: Color) -> Gradient:
|
|
var g := Gradient.new()
|
|
g.set_color(0, c0)
|
|
g.set_color(1, c1)
|
|
return g
|
|
|
|
|
|
func _curve(points: Array) -> Curve:
|
|
var c := Curve.new()
|
|
for pt: Vector2 in points:
|
|
c.add_point(pt)
|
|
return c
|
|
|
|
|
|
func _save(p: CPUParticles3D, path: String) -> void:
|
|
p.set_script(load(_EFFECT_SCRIPT))
|
|
var packed := PackedScene.new()
|
|
packed.pack(p)
|
|
ResourceSaver.save(packed, path)
|
|
p.free()
|
|
|
|
|
|
# ── the placeholders (defaults ported from the old code-built particles) ─────
|
|
func _seed_blood_burst() -> void:
|
|
var p := CPUParticles3D.new()
|
|
p.name = "blood_burst"
|
|
p.mesh = _sphere(0.07)
|
|
p.color_ramp = _ramp(Color(1.0, 0.02, 0.02, 1.0), Color(0.25, 0.0, 0.0, 0.0))
|
|
p.scale_amount_curve = _curve([Vector2(0.0, 1.0), Vector2(0.6, 0.5), Vector2(1.0, 0.0)])
|
|
p.emitting = false
|
|
p.one_shot = true
|
|
p.explosiveness = 0.9
|
|
p.amount = 25
|
|
p.lifetime = 1.1
|
|
p.randomness = 0.4
|
|
p.local_coords = false
|
|
p.direction = Vector3(0.0, 1.0, 0.0)
|
|
p.spread = 80.0
|
|
p.gravity = Vector3(0.0, -7.0, 0.0)
|
|
p.initial_velocity_min = 4.0
|
|
p.initial_velocity_max = 11.0
|
|
p.scale_amount_min = 0.5
|
|
p.scale_amount_max = 1.6
|
|
_save(p, "res://systems/fx/blood_burst.tscn")
|
|
|
|
|
|
func _seed_slam_dust() -> void:
|
|
var p := CPUParticles3D.new()
|
|
p.name = "slam_dust"
|
|
p.mesh = _sphere(0.16)
|
|
p.color_ramp = _ramp(Color(0.72, 0.60, 0.40, 0.9), Color(0.55, 0.45, 0.30, 0.0))
|
|
p.scale_amount_curve = _curve([Vector2(0.0, 0.4), Vector2(0.35, 1.0), Vector2(1.0, 0.0)])
|
|
p.emitting = false
|
|
p.one_shot = true
|
|
p.explosiveness = 1.0
|
|
p.amount = 36
|
|
p.lifetime = 0.9
|
|
p.randomness = 0.5
|
|
p.local_coords = false
|
|
p.emission_shape = CPUParticles3D.EMISSION_SHAPE_SPHERE
|
|
p.emission_sphere_radius = 0.5
|
|
p.direction = Vector3(0.0, 1.0, 0.0)
|
|
p.spread = 75.0
|
|
p.gravity = Vector3(0.0, -6.0, 0.0)
|
|
p.initial_velocity_min = 2.5
|
|
p.initial_velocity_max = 6.5
|
|
p.scale_amount_min = 0.6
|
|
p.scale_amount_max = 1.4
|
|
_save(p, "res://systems/fx/slam_dust.tscn")
|
|
|
|
|
|
func _seed_spark() -> void:
|
|
var p := CPUParticles3D.new()
|
|
p.name = "spark"
|
|
p.mesh = _sphere(0.03)
|
|
p.color_ramp = _ramp(Color(1.0, 0.95, 0.6, 1.0), Color(1.0, 0.55, 0.1, 0.0))
|
|
p.scale_amount_curve = _curve([Vector2(0.0, 1.0), Vector2(1.0, 0.0)])
|
|
p.emitting = false
|
|
p.one_shot = true
|
|
p.explosiveness = 1.0
|
|
p.amount = 14
|
|
p.lifetime = 0.35
|
|
p.randomness = 0.6
|
|
p.local_coords = false
|
|
p.direction = Vector3(0.0, 1.0, 0.0)
|
|
p.spread = 45.0
|
|
p.gravity = Vector3(0.0, -9.0, 0.0)
|
|
p.initial_velocity_min = 3.0
|
|
p.initial_velocity_max = 7.0
|
|
p.scale_amount_min = 0.4
|
|
p.scale_amount_max = 1.0
|
|
_save(p, "res://systems/fx/spark.tscn")
|
|
|
|
|
|
func _seed_hit_flash() -> void:
|
|
var p := CPUParticles3D.new()
|
|
p.name = "hit_flash"
|
|
p.mesh = _sphere(0.05)
|
|
p.color_ramp = _ramp(Color(1.0, 1.0, 1.0, 1.0), Color(1.0, 1.0, 1.0, 0.0))
|
|
p.scale_amount_curve = _curve([Vector2(0.0, 0.2), Vector2(0.3, 1.2), Vector2(1.0, 0.0)])
|
|
p.emitting = false
|
|
p.one_shot = true
|
|
p.explosiveness = 1.0
|
|
p.amount = 6
|
|
p.lifetime = 0.18
|
|
p.randomness = 0.2
|
|
p.local_coords = false
|
|
p.spread = 180.0
|
|
p.gravity = Vector3.ZERO
|
|
p.initial_velocity_min = 0.5
|
|
p.initial_velocity_max = 1.5
|
|
p.scale_amount_min = 0.8
|
|
p.scale_amount_max = 1.4
|
|
_save(p, "res://systems/fx/hit_flash.tscn")
|
|
|
|
|
|
func _seed_gallery() -> void:
|
|
var root_node := Node3D.new()
|
|
root_node.name = "EffectGallery"
|
|
|
|
var cam := Camera3D.new()
|
|
cam.name = "Camera3D"
|
|
cam.transform = Transform3D(Basis.from_euler(Vector3(deg_to_rad(-18.0), 0.0, 0.0)), Vector3(0.0, 2.0, 4.5))
|
|
root_node.add_child(cam)
|
|
cam.owner = root_node
|
|
|
|
var light := DirectionalLight3D.new()
|
|
light.name = "Sun"
|
|
light.rotation_degrees = Vector3(-50.0, -30.0, 0.0)
|
|
root_node.add_child(light)
|
|
light.owner = root_node
|
|
|
|
var ground := MeshInstance3D.new()
|
|
ground.name = "Ground"
|
|
var plane := PlaneMesh.new()
|
|
plane.size = Vector2(12.0, 12.0)
|
|
var gmat := StandardMaterial3D.new()
|
|
gmat.albedo_color = Color(0.62, 0.53, 0.36)
|
|
plane.material = gmat
|
|
ground.mesh = plane
|
|
root_node.add_child(ground)
|
|
ground.owner = root_node
|
|
|
|
var ui := CanvasLayer.new()
|
|
ui.name = "UI"
|
|
root_node.add_child(ui)
|
|
ui.owner = root_node
|
|
var label := Label.new()
|
|
label.name = "Label"
|
|
label.position = Vector2(24.0, 20.0)
|
|
label.text = "FX gallery"
|
|
label.add_theme_font_size_override("font_size", 28)
|
|
ui.add_child(label)
|
|
label.owner = root_node
|
|
|
|
root_node.set_script(load(_GALLERY_SCRIPT))
|
|
var packed := PackedScene.new()
|
|
packed.pack(root_node)
|
|
ResourceSaver.save(packed, "res://systems/fx/effect_gallery.tscn")
|
|
root_node.free()
|