Reorganize the codebase into role-based folders (snake_case)
Move the 40-odd root-level scripts and scenes into a tree that groups by role,
per the approved roadmap (WS2/WS3): actors/{player,matador,bear}, systems/{combat,
fx,input,debug,render,crowd,config}, ui/, props/ (was tscn_s/). Rename the shell
scene.tscn -> levels/game_shell.tscn and every PascalCase scene to snake_case
(Player.tscn -> player.tscn, MainMenu.tscn -> main_menu.tscn, etc.) so the
case-insensitive web/macOS filesystems can't alias two files together.
Done as a scripted move: each file travels with its .uid; every quoted res:// path
across scenes, scripts, .tres, and project.godot's 9 autoloads is rewritten; the
.godot uid/class cache is rebuilt from scratch. Scenes still resolve by uid, so the
rewrite is belt-and-suspenders. Lint globs (run_tests.sh + both CI workflows) now
walk the new dirs; an orphan animation_tree.gd.uid and 7 pre-existing trailing-
whitespace lints (barrel/torch, never covered by the old root-only glob) are cleaned.
Full suite green on both sides. Asset consolidation (Assets/Animations/fonts ->
art/) is deferred — moving imported .glb/.png/.ttf is safer done in the editor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KoYBYQgRyY1zDHXMsXY3V
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
class_name IsoCamera
|
||||
extends Camera3D
|
||||
|
||||
@export var height: float = 25.0
|
||||
@export var horizontal_distance: float = 25.0
|
||||
@export var horizontal_angle_deg: float = 45.0
|
||||
|
||||
# Perspective zoom (FOV)
|
||||
@export var fov_min: float = 20.0
|
||||
@export var fov_max: float = 75.0
|
||||
@export var default_fov: float = 30.0
|
||||
|
||||
@export var focus_slice_thickness: float = 6.0
|
||||
@export var blur_transition: float = 15.0
|
||||
|
||||
var _shake_amount: float = 0.0
|
||||
var _shake_decay: float = 0.0
|
||||
var _user_fov: float = 0.0
|
||||
|
||||
# Per-level framing multiplier: 1.0 = the designer's base shot, >1 dollies the camera
|
||||
# back for a wider view (the lobby uses this). Set by game_shell when a level loads;
|
||||
# kept separate from the scroll-wheel FOV zoom so the two don't fight.
|
||||
var _framing_scale: float = 1.0
|
||||
|
||||
# Trailing-camera state: _focus is the smoothed point the camera frames (it lags
|
||||
# the bull), _drag_offset is the velocity-driven trail that slides it behind.
|
||||
var _focus: Vector3 = Vector3.ZERO
|
||||
var _drag_offset: Vector3 = Vector3.ZERO
|
||||
var _focus_init: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_as_top_level(true)
|
||||
|
||||
projection = Camera3D.PROJECTION_PERSPECTIVE
|
||||
fov = default_fov
|
||||
_user_fov = default_fov
|
||||
|
||||
near = 0.1
|
||||
far = 150.0
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("zoom_in"):
|
||||
_user_fov = clamp(_user_fov / 1.1, fov_min, fov_max)
|
||||
|
||||
if event.is_action_pressed("zoom_out"):
|
||||
_user_fov = clamp(_user_fov * 1.1, fov_min, fov_max)
|
||||
|
||||
|
||||
# Pinch-to-zoom hook for the touch UI. `ratio` is the two-finger spread this frame over
|
||||
# last frame: >1 (fingers parting) narrows the FOV to zoom in, <1 widens it to pull back,
|
||||
# mirroring the scroll-wheel zoom_in/zoom_out feel.
|
||||
func adjust_zoom(ratio: float) -> void:
|
||||
if ratio <= 0.0:
|
||||
return
|
||||
_user_fov = clampf(_user_fov / ratio, fov_min, fov_max)
|
||||
|
||||
|
||||
## Dolly the camera in/out for the level being loaded. 1.0 is the base framing; >1 pulls
|
||||
## back for a wider shot (the lobby hub uses this). Independent of the scroll-wheel zoom.
|
||||
func set_framing_scale(scale: float) -> void:
|
||||
_framing_scale = maxf(scale, 0.1)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
fov = lerpf(fov, _user_fov, delta * 8.0)
|
||||
|
||||
var angle_rad := deg_to_rad(horizontal_angle_deg)
|
||||
|
||||
var offset := Vector3(
|
||||
sin(angle_rad) * horizontal_distance * _framing_scale,
|
||||
height * _framing_scale,
|
||||
cos(angle_rad) * horizontal_distance * _framing_scale
|
||||
)
|
||||
|
||||
var target := get_parent() as Node3D
|
||||
var focus := _drag_focus(target, delta)
|
||||
|
||||
global_position = focus + offset
|
||||
look_at(focus, Vector3.UP)
|
||||
|
||||
_update_dof(focus)
|
||||
|
||||
if _shake_amount > 0.005:
|
||||
global_position += Vector3(
|
||||
randf_range(-_shake_amount, _shake_amount),
|
||||
randf_range(-_shake_amount * 0.25, _shake_amount * 0.25),
|
||||
randf_range(-_shake_amount, _shake_amount)
|
||||
)
|
||||
|
||||
_shake_amount = move_toward(_shake_amount, 0.0, _shake_decay * delta)
|
||||
|
||||
|
||||
# Smoothed follow point that drags behind the bull. The target trail sits opposite
|
||||
# the bull's flat velocity (cam_drag metres per m/s); the offset and the focus both
|
||||
# ease toward their targets so the camera lags and settles rather than snapping.
|
||||
func _drag_focus(target: Node3D, delta: float) -> Vector3:
|
||||
var pos := target.global_position
|
||||
if not _focus_init:
|
||||
_focus = pos
|
||||
_focus_init = true
|
||||
|
||||
var vel := Vector3.ZERO
|
||||
if target is CharacterBody3D:
|
||||
vel = (target as CharacterBody3D).velocity
|
||||
vel.y = 0.0
|
||||
|
||||
var drag_target := -vel * DP.f("cam_drag")
|
||||
_drag_offset = _drag_offset.lerp(drag_target, clampf(DP.f("cam_drag_speed") * delta, 0.0, 1.0))
|
||||
_focus = _focus.lerp(pos + _drag_offset, clampf(DP.f("cam_follow_lerp") * delta, 0.0, 1.0))
|
||||
return _focus
|
||||
|
||||
|
||||
func trigger_hit(strength: float) -> void:
|
||||
if not Settings.screen_shake:
|
||||
return
|
||||
_shake_amount = strength * 0.5
|
||||
_shake_decay = _shake_amount / 0.4
|
||||
|
||||
|
||||
func _update_dof(target_pos: Vector3) -> void:
|
||||
var cam_attr: CameraAttributesPractical = null
|
||||
|
||||
if attributes is CameraAttributesPractical:
|
||||
cam_attr = attributes
|
||||
|
||||
if cam_attr == null:
|
||||
return
|
||||
|
||||
var distance := global_position.distance_to(target_pos)
|
||||
|
||||
cam_attr.dof_blur_near_distance = max(distance - focus_slice_thickness * 0.5, near + 0.1)
|
||||
cam_attr.dof_blur_far_distance = distance + focus_slice_thickness * 0.5
|
||||
|
||||
cam_attr.dof_blur_near_transition = blur_transition
|
||||
cam_attr.dof_blur_far_transition = blur_transition
|
||||
|
||||
cam_attr.dof_blur_near_enabled = false
|
||||
cam_attr.dof_blur_far_enabled = false
|
||||
Reference in New Issue
Block a user