Reorganize into role-based folders and consolidate art
Move every script and scene into role-based directories (actors/, systems/, ui/, levels/, props/) and all raw art under art/ (grouped by role, snake_case, Extract-Textures GLBs), dropping Blender version-suffix noise. Filenames are snake_case (PascalCase reserved for class_name) to avoid case-insensitive filesystem aliasing on the web/macOS targets. Routes the bull's hits through the typed DamageEvent pipeline and wires the bull's abilities to the Fx library. Updates CLAUDE.md's folder-layout overview to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CCyaJPTHCH9EDthpf4A5Sj
This commit is contained in:
@@ -67,11 +67,11 @@ func _make_floor() -> StaticBody3D:
|
||||
func _run() -> void:
|
||||
root.add_child(_make_floor())
|
||||
|
||||
_bull = (load("res://Player.tscn") as PackedScene).instantiate()
|
||||
_bull = (load("res://actors/player/player.tscn") as PackedScene).instantiate()
|
||||
root.add_child(_bull)
|
||||
_bull.global_position = Vector3(4, 1, 0)
|
||||
|
||||
_bear = (load("res://Bear.tscn") as PackedScene).instantiate()
|
||||
_bear = (load("res://actors/bear/bear.tscn") as PackedScene).instantiate()
|
||||
root.add_child(_bear)
|
||||
_bear.global_position = Vector3(0, 1, 0)
|
||||
await physics_frame
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends SceneTree
|
||||
## Bear attack-FX test. The four attack effects (crater, smash debris, left/right claw slash)
|
||||
## live in Assets/fx/Bear_FX.glb as meshes that rest at scale 0 at Siim's baked positions. The
|
||||
## live in art/fx/bear_fx.glb as meshes that rest at scale 0 at Siim's baked positions. The
|
||||
## GLB carries its own AnimationPlayer whose clips scale one mesh up at its strike frame and
|
||||
## back to 0 (self-hiding) while pinning the others at 0. bear.gd fires the matching clip on
|
||||
## each strike (see _play_fx). This asserts each clip pops its own mesh, self-hides by the end,
|
||||
@@ -27,7 +27,7 @@ func _ok(c: bool, m: String) -> void:
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var bear := (load("res://Bear.tscn") as PackedScene).instantiate()
|
||||
var bear := (load("res://actors/bear/bear.tscn") as PackedScene).instantiate()
|
||||
root.add_child(bear)
|
||||
await physics_frame
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ func _drive(bear: Node, fire: String) -> void:
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var bear := (load("res://Bear.tscn") as PackedScene).instantiate() as Node3D
|
||||
var bear := (load("res://actors/bear/bear.tscn") as PackedScene).instantiate() as Node3D
|
||||
root.add_child(bear)
|
||||
bear.set_physics_process(false) # freeze the AI; we fire the colliders ourselves
|
||||
await physics_frame
|
||||
|
||||
@@ -56,7 +56,7 @@ func _ram_pass(bear: Node, bull: CharacterBody3D, speed: float, from: Vector3, t
|
||||
|
||||
func _run() -> void:
|
||||
var bull := _make_bull()
|
||||
var bear := (load("res://Bear.tscn") as PackedScene).instantiate() as Node3D
|
||||
var bear := (load("res://actors/bear/bear.tscn") as PackedScene).instantiate() as Node3D
|
||||
root.add_child(bear)
|
||||
bear.global_position = Vector3.ZERO
|
||||
bear.set_physics_process(false) # freeze the AI; we drive the ram sweep ourselves
|
||||
|
||||
@@ -49,9 +49,9 @@ func _init() -> void:
|
||||
func _run() -> void:
|
||||
_dt = 1.0 / float(Engine.physics_ticks_per_second)
|
||||
_dp = root.get_node_or_null("/root/DP")
|
||||
_mat_scene = load("res://Matador.tscn") as PackedScene
|
||||
_mat_scene = load("res://actors/matador/matador.tscn") as PackedScene
|
||||
|
||||
_scene = (load("res://scene.tscn") as PackedScene).instantiate()
|
||||
_scene = (load("res://levels/game_shell.tscn") as PackedScene).instantiate()
|
||||
root.add_child(_scene)
|
||||
current_scene = _scene
|
||||
await create_timer(0.5).timeout
|
||||
|
||||
@@ -29,7 +29,7 @@ var _matador_scene: PackedScene = null
|
||||
# preload of a matador scene compiles matador.gd before the DP autoload binds.
|
||||
func _mat_scene() -> PackedScene:
|
||||
if _matador_scene == null:
|
||||
_matador_scene = load("res://Matador.tscn")
|
||||
_matador_scene = load("res://actors/matador/matador.tscn")
|
||||
return _matador_scene
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ func _init() -> void:
|
||||
func _run() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(OUTPUT_DIR)
|
||||
|
||||
var scene_res := load("res://scene.tscn")
|
||||
var scene_res := load("res://levels/game_shell.tscn")
|
||||
if not scene_res:
|
||||
push_error("gameplay_test: failed to load scene.tscn")
|
||||
quit(1)
|
||||
|
||||
@@ -44,7 +44,7 @@ func _run() -> void:
|
||||
# Force the shell to build the Bear's Den regardless of the generated map.
|
||||
run.debug_level_override = run.get_level(&"bear")
|
||||
|
||||
var scene: Node = load("res://scene.tscn").instantiate()
|
||||
var scene: Node = load("res://levels/game_shell.tscn").instantiate()
|
||||
root.add_child(scene)
|
||||
current_scene = scene
|
||||
# Let the shell instance the level and the spawner's deferred spawn run.
|
||||
|
||||
+3
-58
@@ -30,7 +30,6 @@ func _run() -> void:
|
||||
test_kick_spread()
|
||||
test_gait_phase_crossing()
|
||||
test_tail_verlet_constraint()
|
||||
test_run_map()
|
||||
|
||||
print("=" .repeat(60))
|
||||
print("Results: %d passed, %d failed" % [_passed, _failed])
|
||||
@@ -172,60 +171,6 @@ func test_tail_verlet_constraint() -> void:
|
||||
_assert_in_range(damping_factor, 0.85, 1.0, "tail damping factor is reasonable")
|
||||
|
||||
|
||||
# ── Run map (Slay-the-Spire progression) ──────────────────────────────────────
|
||||
|
||||
func test_run_map() -> void:
|
||||
print("\n-- test_run_map --")
|
||||
var run: Node = root.get_node_or_null("/root/Run")
|
||||
if run == null:
|
||||
_assert_true(false, "Run autoload should exist")
|
||||
return
|
||||
|
||||
run.start_new_run()
|
||||
_assert_true(run.has_run(), "start_new_run builds a map")
|
||||
_assert_true(run.map.size() >= 2, "map has multiple rows")
|
||||
|
||||
# Final row is a single boss node the map funnels toward.
|
||||
var last: Array = run.map[run.map.size() - 1]
|
||||
_assert_eq(last.size(), 1, "final row is a single boss node")
|
||||
_assert_true(last[0].level.is_boss, "final node is a boss level")
|
||||
|
||||
# Connectivity: every node links forward, and every next-row node has a parent.
|
||||
for r: int in run.map.size() - 1:
|
||||
var nxt: Array = run.map[r + 1]
|
||||
var reached := {}
|
||||
for from_node in run.map[r]:
|
||||
_assert_true(from_node.links.size() >= 1, "row %d node links forward" % r)
|
||||
for c: int in from_node.links:
|
||||
_assert_true(c >= 0 and c < nxt.size(), "link column in range")
|
||||
reached[c] = true
|
||||
for c: int in nxt.size():
|
||||
_assert_true(reached.has(c), "row %d col %d has a parent" % [r + 1, c])
|
||||
|
||||
# From the start gate the whole first row is the frontier.
|
||||
_assert_eq(run.reachable().size(), run.map[0].size(), "start frontier is row 0")
|
||||
|
||||
# Advancing follows the chosen node's links.
|
||||
var first = run.map[0][0]
|
||||
run.select_node(first)
|
||||
run.complete_current_level()
|
||||
_assert_eq(run.player_row, 0, "player advanced to row 0")
|
||||
_assert_true(first.cleared, "cleared node is flagged")
|
||||
_assert_eq(run.reachable().size(), first.links.size(),
|
||||
"frontier follows the chosen node's links")
|
||||
|
||||
# Walking to a boss node latches run completion.
|
||||
var guard := 0
|
||||
while not run.run_complete() and guard < 64:
|
||||
var opts: Array = run.reachable()
|
||||
if opts.is_empty():
|
||||
break
|
||||
run.select_node(opts[0])
|
||||
run.complete_current_level()
|
||||
guard += 1
|
||||
_assert_true(run.run_complete(), "reaching a boss node completes the run")
|
||||
|
||||
|
||||
# ── Shared helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
func _assert_in_range(val: float, lo: float, hi: float, desc: String) -> void:
|
||||
@@ -245,8 +190,8 @@ func test_debug_params_defaults() -> void:
|
||||
"mat_walk_speed default in valid range")
|
||||
_assert_in_range(dp.f("mat_wander_radius"), 5.0, 50.0,
|
||||
"mat_wander_radius default in valid range")
|
||||
_assert_in_range(dp.f("mat_spawn_count"), 1.0, 30.0,
|
||||
"mat_spawn_count default in valid range")
|
||||
_assert_in_range(dp.f("mat_wave_size"), 1.0, 50.0,
|
||||
"mat_wave_size default in valid range")
|
||||
_assert_in_range(dp.f("mat_hit_threshold"), 1.0, 30.0,
|
||||
"mat_hit_threshold default in valid range")
|
||||
_assert_in_range(dp.f("mat_ragdoll_impulse"), 1.0, 50.0,
|
||||
@@ -377,7 +322,7 @@ func test_matador_state_transitions() -> void:
|
||||
print("\n-- test_matador_state_transitions --")
|
||||
# Verify that the State enum values match expected constants
|
||||
# Load the matador scene to check its script
|
||||
var matador_scene := load("res://Matador.tscn")
|
||||
var matador_scene := load("res://actors/matador/matador.tscn")
|
||||
if matador_scene == null:
|
||||
_assert_true(false, "Matador.tscn should load")
|
||||
return
|
||||
|
||||
@@ -43,7 +43,7 @@ func _make_bull() -> CharacterBody3D:
|
||||
|
||||
|
||||
func _make_matador(_bull: CharacterBody3D) -> CharacterBody3D:
|
||||
var mat := (load("res://Matador.tscn") as PackedScene).instantiate() as CharacterBody3D
|
||||
var mat := (load("res://actors/matador/matador.tscn") as PackedScene).instantiate() as CharacterBody3D
|
||||
root.add_child(mat)
|
||||
mat.global_position = Vector3.ZERO
|
||||
mat.set_physics_process(false) # we drive the swept gore ourselves, isolated from the AI
|
||||
|
||||
@@ -7,7 +7,7 @@ extends SceneTree
|
||||
# physics_layers.gd is pure constants (safe to preload); spear_projectile.gd references the
|
||||
# Settings autoload, whose global identifier isn't registered when a --script entry compiles its
|
||||
# preloads — so it's loaded at runtime inside _run instead, once the autoloads exist.
|
||||
const PhysicsLayers = preload("res://physics_layers.gd")
|
||||
const PhysicsLayers = preload("res://systems/combat/physics_layers.gd")
|
||||
|
||||
var _pass := 0
|
||||
var _fail := 0
|
||||
@@ -44,8 +44,8 @@ func _run() -> void:
|
||||
await physics_frame
|
||||
|
||||
# Launch a spear flat, dead-on from 3 m out; low gravity so the short flight stays level.
|
||||
var spear_script := load("res://spear_projectile.gd")
|
||||
var mesh := (load("res://Spear.tscn") as PackedScene).instantiate() as Node3D
|
||||
var spear_script := load("res://actors/matador/spear_projectile.gd")
|
||||
var mesh := (load("res://actors/matador/spear.tscn") as PackedScene).instantiate() as Node3D
|
||||
root.add_child(mesh)
|
||||
var origin := Vector3(0.0, 0.0, -3.0)
|
||||
var spear := spear_script.launch(root, mesh, origin, Vector3(0.0, 0.0, 12.0), 0.05) as RigidBody3D
|
||||
|
||||
@@ -45,7 +45,7 @@ func _run() -> void:
|
||||
root.add_child(bull)
|
||||
bull.global_position = Vector3(0.0, 0.0, standoff + 3.0)
|
||||
|
||||
var mat := (load("res://Matador.tscn") as PackedScene).instantiate() as CharacterBody3D
|
||||
var mat := (load("res://actors/matador/matador.tscn") as PackedScene).instantiate() as CharacterBody3D
|
||||
root.add_child(mat) # _ready acquires the bull from the "player" group
|
||||
mat.global_position = Vector3.ZERO
|
||||
mat.set_physics_process(false) # we drive the attack tick ourselves, isolated from the AI
|
||||
|
||||
@@ -38,7 +38,7 @@ func _make_bull() -> CharacterBody3D:
|
||||
|
||||
|
||||
func _make_taunting_matador(_bull: CharacterBody3D) -> CharacterBody3D:
|
||||
var mat := (load("res://Matador.tscn") as PackedScene).instantiate() as CharacterBody3D
|
||||
var mat := (load("res://actors/matador/matador.tscn") as PackedScene).instantiate() as CharacterBody3D
|
||||
root.add_child(mat)
|
||||
mat.global_position = Vector3.ZERO
|
||||
mat.set_physics_process(false) # we drive _tick_wander ourselves, no brain
|
||||
|
||||
@@ -45,7 +45,7 @@ func _run() -> void:
|
||||
run.ensure_run()
|
||||
run.debug_level_override = run.get_level(&"arena")
|
||||
|
||||
var scene: Node = load("res://scene.tscn").instantiate()
|
||||
var scene: Node = load("res://levels/game_shell.tscn").instantiate()
|
||||
root.add_child(scene)
|
||||
current_scene = scene
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func _run() -> void:
|
||||
run.ensure_run() # build the run first — the shell's own ensure_run would wipe the override
|
||||
run.debug_level_override = run.get_level(&"arena")
|
||||
|
||||
var scene_res := load("res://scene.tscn")
|
||||
var scene_res := load("res://levels/game_shell.tscn")
|
||||
if not scene_res:
|
||||
push_error("performance_test: failed to load scene.tscn")
|
||||
quit(1)
|
||||
|
||||
@@ -65,7 +65,7 @@ func _spawn_one() -> Node3D:
|
||||
|
||||
func _run() -> void:
|
||||
# Warm up: first instance pays one-time import/JIT/shader costs that would skew sample 1.
|
||||
_matador = load("res://Matador.tscn") as PackedScene
|
||||
_matador = load("res://actors/matador/matador.tscn") as PackedScene
|
||||
var warm := _spawn_one()
|
||||
await process_frame
|
||||
warm.call(&"apply_ability_hit", Vector3(1.0, 0.0, 0.0), 12.0)
|
||||
|
||||
@@ -21,7 +21,7 @@ func _init() -> void:
|
||||
func _run() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(OUTPUT_DIR)
|
||||
|
||||
var scene_res := load("res://scene.tscn")
|
||||
var scene_res := load("res://levels/game_shell.tscn")
|
||||
if not scene_res:
|
||||
push_error("ragdoll_throw_test: failed to load scene.tscn")
|
||||
quit(1)
|
||||
|
||||
@@ -7,7 +7,7 @@ extends SceneTree
|
||||
## hides the original skinned meshes (so they never hit the invisible-on-Mali skinning path).
|
||||
## Run: godot --headless --script res://tests/rigid_skin_test.gd
|
||||
|
||||
const RigidSkin = preload("res://rigid_skin.gd")
|
||||
const RigidSkin = preload("res://systems/render/rigid_skin.gd")
|
||||
|
||||
var _pass := 0
|
||||
var _fail := 0
|
||||
@@ -43,7 +43,7 @@ func _surface_mats(mi: MeshInstance3D) -> Array:
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var inst: Node = (load("res://Assets/Matador.glb") as PackedScene).instantiate()
|
||||
var inst: Node = (load("res://art/characters/matador.glb") as PackedScene).instantiate()
|
||||
root.add_child(inst)
|
||||
|
||||
# Distinct materials on the skinned source meshes, before conversion.
|
||||
|
||||
@@ -18,7 +18,7 @@ func _run() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(OUTPUT_DIR)
|
||||
|
||||
# Load and switch to the main scene
|
||||
var scene_res := load("res://scene.tscn")
|
||||
var scene_res := load("res://levels/game_shell.tscn")
|
||||
if scene_res == null:
|
||||
push_error("screenshot_test: Failed to load scene.tscn")
|
||||
quit(1)
|
||||
|
||||
@@ -73,7 +73,7 @@ func _run() -> void:
|
||||
# Headless boots a 64x64 root viewport, which collapses every UI rect on top of each other.
|
||||
# Give it a real phone-ish landscape size so the joystick zone / buttons lay out sanely.
|
||||
root.size = Vector2i(1152, 648)
|
||||
_tc = (load("res://touch_controls.gd") as Script).new()
|
||||
_tc = (load("res://systems/input/touch_controls.gd") as Script).new()
|
||||
root.add_child(_tc)
|
||||
await process_frame # _ready builds _surface and runs the first _relayout
|
||||
_tc._relayout() # ensure the layout reflects the size set above
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ extends Node3D
|
||||
## skinned mesh draws under a given engine build.
|
||||
|
||||
func _ready() -> void:
|
||||
var bull := (load("res://Assets/bull.fbx") as PackedScene).instantiate() as Node3D
|
||||
var bull := (load("res://art/characters/bull.fbx") as PackedScene).instantiate() as Node3D
|
||||
bull.scale = Vector3(100.0, 100.0, 100.0)
|
||||
add_child(bull)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user