class_name GameBall extends RigidBody3D ## Base class for all gfolf balls signal entered_water const TERRAIN_DAMPING_EPSILON := 1e-6 const IRON_DAMPING := 9999.0 ## Angular damping while in air @export var air_damping := 0.0 ## Angular damping while in collision with rough terrain @export var rough_damping := 8.0 ## Angular damping for iron balls @export var iron_damping := 9999.0 ## Causes the ball to act more like a brick @export var iron_ball := false: set(value): if value: physics_material_override = iron_physics else: physics_material_override = normal_physics iron_ball = value ## Base damage inflicted on impact with a player @export var base_damage := 15.0 var _last_contact_normal: Vector3 = Vector3.UP var _position_on_last_wake: Vector3 var _awake := false var _zones: Array[BallZone] = [] @onready var normal_physics: PhysicsMaterial = preload( "res://src/equipment/balls/physics_ball/normal_physics.tres" ) @onready var iron_physics: PhysicsMaterial = preload( "res://src/equipment/balls/physics_ball/iron_physics.tres" ) @onready var _debug_draw: Control = %DebugDraw ## Called by a water area when this ball enters it func enter_water() -> void: entered_water.emit() func _total_terrain_angular_damping() -> float: return _zones.reduce( func(a: float, b: BallZone) -> float: return a + b.terrain_angular_damping, 0.0 ) func _integrate_forces(state: PhysicsDirectBodyState3D) -> void: if not _awake: # Triggered on first frame after waking _awake = true _position_on_last_wake = global_position _last_contact_normal = Vector3.UP var damping := air_damping if state.get_contact_count(): _last_contact_normal = state.get_contact_local_normal(0) damping = _total_terrain_angular_damping() if damping <= TERRAIN_DAMPING_EPSILON: damping = rough_damping if iron_ball: damping = iron_damping angular_damp = damping func enter_zone(zone: BallZone) -> void: _zones.push_back(zone) if zone.water_hazard: entered_water.emit() func exit_zone(zone: BallZone) -> void: _zones.erase(zone) func get_reoriented_basis() -> Basis: var up := _last_contact_normal.normalized() var forward := (_position_on_last_wake - global_position).normalized() var right := up.cross(forward).normalized() forward = right.cross(up) # orthonormalize return Basis(right, up, forward) func _on_sleeping_state_changed() -> void: if sleeping: # Trigger to reassign on wake _awake = false