2 Commits

Author SHA1 Message Date
richard aac0099faa Merge remote-tracking branch 'origin/main' 2026-06-10 22:36:53 +03:00
richard 4fc64dedae dash speed 3x, slam visual/damage fix 2026-06-10 22:36:47 +03:00
3 changed files with 60 additions and 91 deletions
+27 -63
View File
@@ -4,58 +4,39 @@ extends Camera3D
@export var horizontal_distance: float = 28.0 @export var horizontal_distance: float = 28.0
@export var horizontal_angle_deg: float = 45.0 @export var horizontal_angle_deg: float = 45.0
# --- ORTHOGRAPHIC ZOOM SETTINGS --- @export var size_min: float = 5.0
@export var size_min: float = 5.0 # Maximum zoom in (small frame size) @export var size_max: float = 30.0
@export var size_max: float = 30.0 # Maximum zoom out (large frame size)
@export var default_size: float = 15.0 @export var default_size: float = 15.0
# --- TILT-SHIFT DOF SETTINGS --- @export var focus_slice_thickness: float = 6.0
@export var focus_slice_thickness: float = 6.0 @export var blur_transition: float = 15.0
@export var blur_transition: float = 15.0
const _FLASH_DURATION: float = 0.30
var _shake_amount: float = 0.0 var _shake_amount: float = 0.0
var _shake_decay: float = 0.0 var _shake_decay: float = 0.0
var _flash_timer: float = 0.0 var _user_size: float = 0.0
var _flash_rect: ColorRect = null var _charge_active: bool = false
var _target_size: float = 0.0
func _ready() -> void: func _ready() -> void:
set_as_top_level(true) set_as_top_level(true)
projection = Camera3D.PROJECTION_ORTHOGONAL # Switched to orthographic projection = Camera3D.PROJECTION_ORTHOGONAL
_target_size = default_size _user_size = default_size
size = default_size size = default_size
near = 0.1
near = 0.1 far = 150.0
far = 150.0
_setup_flash()
func _setup_flash() -> void:
var canvas := CanvasLayer.new()
canvas.layer = 20
add_child(canvas)
_flash_rect = ColorRect.new()
_flash_rect.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_flash_rect.color = Color(1.0, 0.25, 0.2, 0.0)
_flash_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
canvas.add_child(_flash_rect)
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
# Zoom inputs now scale the Orthographic Size property
if event.is_action_pressed("zoom_in"): if event.is_action_pressed("zoom_in"):
_target_size = clamp(_target_size / 1.2, size_min, size_max) _user_size = clamp(_user_size / 1.2, size_min, size_max)
if event.is_action_pressed("zoom_out"): if event.is_action_pressed("zoom_out"):
_target_size = clamp(_target_size * 1.2, size_min, size_max) _user_size = clamp(_user_size * 1.2, size_min, size_max)
func _process(delta: float) -> void: func _process(delta: float) -> void:
size = lerpf(size, _target_size, delta * 10.0) var zoom_target := _user_size * DP.f("charge_zoom_factor") if _charge_active else _user_size
size = lerpf(size, zoom_target, delta * 8.0)
var angle_rad := deg_to_rad(horizontal_angle_deg) var angle_rad := deg_to_rad(horizontal_angle_deg)
var offset := Vector3( var offset := Vector3(
sin(angle_rad) * horizontal_distance, sin(angle_rad) * horizontal_distance,
@@ -66,7 +47,6 @@ func _process(delta: float) -> void:
global_position = target.global_position + offset global_position = target.global_position + offset
look_at(target.global_position, Vector3.UP) look_at(target.global_position, Vector3.UP)
# --- DYNAMIC TILT SHIFT CALCULATION ---
_update_ortho_tilt_shift_dof(target.global_position) _update_ortho_tilt_shift_dof(target.global_position)
if _shake_amount > 0.005: if _shake_amount > 0.005:
@@ -77,54 +57,38 @@ func _process(delta: float) -> void:
) )
_shake_amount = move_toward(_shake_amount, 0.0, _shake_decay * delta) _shake_amount = move_toward(_shake_amount, 0.0, _shake_decay * delta)
if _flash_timer > 0.0:
_flash_timer = maxf(0.0, _flash_timer - delta)
_flash_rect.color.a = (_flash_timer / _FLASH_DURATION) * 0.6
else:
_flash_rect.color.a = 0.0
func trigger_hit(strength: float) -> void: func trigger_hit(strength: float) -> void:
_shake_amount = strength * 0.5 _shake_amount = strength * 0.5
_shake_decay = _shake_amount / 0.4 _shake_decay = _shake_amount / 0.4
_flash_timer = _FLASH_DURATION
func set_charge_active(active: bool) -> void:
_charge_active = active
func _update_ortho_tilt_shift_dof(target_pos: Vector3) -> void: func _update_ortho_tilt_shift_dof(target_pos: Vector3) -> void:
var cam_attr: CameraAttributesPractical = null var cam_attr: CameraAttributesPractical = null
if "attributes" in self and self.attributes is CameraAttributesPractical: if "attributes" in self and self.attributes is CameraAttributesPractical:
cam_attr = self.attributes cam_attr = self.attributes
elif "camera_attributes" in self and self.camera_attributes is CameraAttributesPractical: elif "camera_attributes" in self and self.camera_attributes is CameraAttributesPractical:
cam_attr = self.camera_attributes cam_attr = self.camera_attributes
if cam_attr != null: if cam_attr != null:
# 1. Project distance onto the camera's true forward forward axis
var to_target := target_pos - global_position var to_target := target_pos - global_position
var forward_vector := -global_transform.basis.z var forward_vector := -global_transform.basis.z
var ortho_depth := to_target.dot(forward_vector) var ortho_depth := to_target.dot(forward_vector)
# 2. Extract the dynamic zoom multiplier
var zoom_factor := size / default_size var zoom_factor := size / default_size
var dynamic_slice := focus_slice_thickness * zoom_factor var dynamic_slice := focus_slice_thickness * zoom_factor
var half_slice := dynamic_slice * 0.5 var half_slice := dynamic_slice * 0.5
# Base transition modified by the zoom state
var base_transition := blur_transition * zoom_factor var base_transition := blur_transition * zoom_factor
# 3. Apply the focal zones cam_attr.dof_blur_far_distance = ortho_depth + half_slice
cam_attr.dof_blur_far_distance = ortho_depth + half_slice
cam_attr.dof_blur_near_distance = maxf(near + 0.1, ortho_depth - half_slice) cam_attr.dof_blur_near_distance = maxf(near + 0.1, ortho_depth - half_slice)
cam_attr.dof_blur_far_transition = maxf(0.1, base_transition * 0.5)
# 4. ASYMMETRICAL CORRECTION FOR GODOT 4.7 FORWARD+
# Far transition needs a massive punch to actually manifest on screen
cam_attr.dof_blur_far_transition = maxf(0.1, base_transition * 0.5)
# Near transition needs to be greatly expanded (larger number = gentler slope)
# to stop it from snapping into massive instant blur
cam_attr.dof_blur_near_transition = maxf(1.0, base_transition * 1.5) cam_attr.dof_blur_near_transition = maxf(1.0, base_transition * 1.5)
cam_attr.dof_blur_far_enabled = false
# 5. Keep both enabled
cam_attr.dof_blur_far_enabled = false
cam_attr.dof_blur_near_enabled = false cam_attr.dof_blur_near_enabled = false
+6 -4
View File
@@ -83,6 +83,7 @@ func _register_all() -> void:
_reg_f("Legs", "jump_tuck_fwd", -0.1, -0.5, 0.5) _reg_f("Legs", "jump_tuck_fwd", -0.1, -0.5, 0.5)
_reg_f("Legs", "jump_land_threshold", 1.5, 0.5, 5.0) _reg_f("Legs", "jump_land_threshold", 1.5, 0.5, 5.0)
# ── Camera ──────────────────────────────────────────────────────────────── # ── Camera ────────────────────────────────────────────────────────────────
_reg_f("Camera", "charge_zoom_factor", 0.65, 0.3, 1.0, 0.05)
_reg_f("Camera", "cam_lerp_power", 6.0, 0.5, 30.0) _reg_f("Camera", "cam_lerp_power", 6.0, 0.5, 30.0)
_reg_f("Camera", "cam_sensitivity", 0.005, 0.001, 0.05, 0.001) _reg_f("Camera", "cam_sensitivity", 0.005, 0.001, 0.05, 0.001)
_reg_f("Camera", "cam_min_zoom", 3.5, 0.5, 20.0) _reg_f("Camera", "cam_min_zoom", 3.5, 0.5, 20.0)
@@ -118,11 +119,12 @@ func _register_all() -> void:
_reg_f("Abilities", "kick_cooldown", 1.5, 0.2, 10.0, 0.1) _reg_f("Abilities", "kick_cooldown", 1.5, 0.2, 10.0, 0.1)
_reg_f("Abilities", "kick_range", 3.5, 0.5, 8.0, 0.1) _reg_f("Abilities", "kick_range", 3.5, 0.5, 8.0, 0.1)
_reg_f("Abilities", "kick_strength", 12.0, 1.0, 30.0) _reg_f("Abilities", "kick_strength", 12.0, 1.0, 30.0)
_reg_f("Abilities", "slam_cooldown", 4.0, 0.5, 15.0, 0.1) _reg_f("Abilities", "slam_cooldown", 4.0, 0.5, 15.0, 0.1)
_reg_f("Abilities", "slam_range", 4.0, 1.0, 10.0, 0.1) _reg_f("Abilities", "slam_jump_velocity", 7.0, 2.0, 20.0)
_reg_f("Abilities", "slam_strength", 14.0, 1.0, 30.0) _reg_f("Abilities", "slam_range", 4.0, 1.0, 10.0, 0.1)
_reg_f("Abilities", "slam_strength", 14.0, 1.0, 30.0)
_reg_f("Abilities", "dash_cooldown", 2.0, 0.5, 10.0, 0.1) _reg_f("Abilities", "dash_cooldown", 2.0, 0.5, 10.0, 0.1)
_reg_f("Abilities", "dash_speed", 22.0, 5.0, 60.0) _reg_f("Abilities", "dash_speed", 66.0, 5.0, 200.0)
# ── Debug ───────────────────────────────────────────────────────────────── # ── Debug ─────────────────────────────────────────────────────────────────
_reg_b("Debug", "show_collisions", false) _reg_b("Debug", "show_collisions", false)
+27 -24
View File
@@ -31,8 +31,8 @@ var _active_ability: int = -1
var _dash_dir: Vector3 = Vector3.ZERO var _dash_dir: Vector3 = Vector3.ZERO
var _bull_anim: AnimationPlayer = null var _bull_anim: AnimationPlayer = null
var _charge_overlay: ColorRect = null
var _charge_ready_emitter: CPUParticles3D = null var _charge_ready_emitter: CPUParticles3D = null
var _slam_pending: bool = false
var _huff_player: AudioStreamPlayer = null var _huff_player: AudioStreamPlayer = null
var _crowd_player: AudioStreamPlayer = null var _crowd_player: AudioStreamPlayer = null
@@ -121,15 +121,6 @@ func _setup_charge_indicators() -> void:
sphere.rings = 2 sphere.rings = 2
sphere.material = mat sphere.material = mat
# Screen overlay — red tint while charge-ahead (both buttons) is active
var ui := CanvasLayer.new()
add_child(ui)
_charge_overlay = ColorRect.new()
_charge_overlay.color = Color(0.9, 0.55, 0.0, 0.0)
_charge_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
_charge_overlay.set_anchors_preset(Control.PRESET_FULL_RECT)
ui.add_child(_charge_overlay)
# Orange spark burst when single-button charge hits max # Orange spark burst when single-button charge hits max
var burst_ramp := Gradient.new() var burst_ramp := Gradient.new()
burst_ramp.set_color(0, Color(1.0, 0.55, 0.0, 1.0)) burst_ramp.set_color(0, Color(1.0, 0.55, 0.0, 1.0))
@@ -262,16 +253,16 @@ func _activate_slam() -> void:
ability_cd[1] = DP.f("slam_cooldown") ability_cd[1] = DP.f("slam_cooldown")
_ability_active = true _ability_active = true
_active_ability = 1 _active_ability = 1
_ability_timer = 0.9
if _bull_anim and _bull_anim.has_animation(&"Armature|SLAM"):
_bull_anim.play(&"Armature|SLAM")
_ability_timer = _bull_anim.get_animation(&"Armature|SLAM").length
_legs.set(&"tail_ragdoll", true) _legs.set(&"tail_ragdoll", true)
_spawn_slam_fx() _slam_pending = true
# Delay hit slightly so it lands when the first ring reaches the targets var jump_vel := DP.f("slam_jump_velocity")
get_tree().create_timer(0.15).timeout.connect( velocity.y = jump_vel
func() -> void: _hit_matadors_radius(DP.f("slam_range"), DP.f("slam_strength")) var air_time := jump_vel / 9.8 * 2.0
) _ability_timer = air_time + 0.5
if _bull_anim and _bull_anim.has_animation(&"Armature|SLAM"):
var anim_len := _bull_anim.get_animation(&"Armature|SLAM").length
_bull_anim.speed_scale = anim_len / maxf(air_time, 0.01)
_bull_anim.play(&"Armature|SLAM")
func _activate_dash() -> void: func _activate_dash() -> void:
@@ -592,12 +583,16 @@ func _physics_process(delta: float) -> void:
_active_ability = -1 _active_ability = -1
_legs.set(&"charge_pitch_target", 0.0) _legs.set(&"charge_pitch_target", 0.0)
_legs.set(&"tail_ragdoll", false) _legs.set(&"tail_ragdoll", false)
_slam_pending = false
if _bull_anim:
_bull_anim.speed_scale = 1.0
# ── Mouse button single-thrust actions ─────────────────────────────────────── # ── Mouse button single-thrust actions ───────────────────────────────────────
# Left only → thrust NE (forward-right diagonal) # Left only → thrust NE (forward-right diagonal)
# Right only → thrust NW (forward-left diagonal) # Right only → thrust NW (forward-left diagonal)
var left_held := Input.is_action_pressed(&"thrust_left") var left_held := Input.is_action_pressed(&"thrust_left")
var right_held := Input.is_action_pressed(&"thrust_right") var right_held := Input.is_action_pressed(&"thrust_right")
camera_pivot.call(&"set_charge_active", left_held and right_held)
if left_held and right_held: if left_held and right_held:
_thrust_left_charge = 0.0 _thrust_left_charge = 0.0
@@ -628,11 +623,6 @@ func _physics_process(delta: float) -> void:
_charge_ready_emitter.restart() _charge_ready_emitter.restart()
_charge_was_full = either_full _charge_was_full = either_full
# Briefly flash overlay when dash is active
var target_alpha := 0.20 if (_active_ability == 2 and _ability_active) else 0.0
var oc := _charge_overlay.color
_charge_overlay.color = Color(oc.r, oc.g, oc.b, lerpf(oc.a, target_alpha, delta * 12.0))
# Reset kick timer on landing so the first kick after a bounce is immediate # Reset kick timer on landing so the first kick after a bounce is immediate
if on_floor and not _was_on_floor and direction: if on_floor and not _was_on_floor and direction:
_kick_timer = 0.0 _kick_timer = 0.0
@@ -681,6 +671,19 @@ func _physics_process(delta: float) -> void:
if impact > DP.f("bounce_min_impact"): if impact > DP.f("bounce_min_impact"):
velocity.y = impact * DP.f("bounce_restitution") velocity.y = impact * DP.f("bounce_restitution")
# ── Slam landing ──────────────────────────────────────────────────────────
if _slam_pending and is_on_floor() and not _was_on_floor:
_slam_pending = false
_ability_active = false
_active_ability = -1
_ability_timer = 0.0
_legs.set(&"charge_pitch_target", 0.0)
_legs.set(&"tail_ragdoll", false)
if _bull_anim:
_bull_anim.speed_scale = 1.0
_spawn_slam_fx()
_hit_matadors_radius(DP.f("slam_range"), DP.f("slam_strength"))
# ── Dust ────────────────────────────────────────────────────────────────── # ── Dust ──────────────────────────────────────────────────────────────────
if flat_speed > DP.f("dust_charge_spd") and on_floor: if flat_speed > DP.f("dust_charge_spd") and on_floor:
_set_dust_state(DustState.CHARGE) _set_dust_state(DustState.CHARGE)