class_name AnalyticalRegionalUpdates extends RefCounted static func linear_value( current_value: float, rate_per_tick: float, elapsed_ticks: int, minimum: float = -INF, maximum: float = INF ) -> float: if elapsed_ticks <= 0 or not is_finite(current_value) or not is_finite(rate_per_tick): return clampf(current_value, minimum, maximum) return clampf(current_value + rate_per_tick * elapsed_ticks, minimum, maximum) static func exponential_value( current_value: float, multiplier_per_tick: float, elapsed_ticks: int, minimum: float = -INF, maximum: float = INF ) -> float: if ( elapsed_ticks <= 0 or not is_finite(current_value) or not is_finite(multiplier_per_tick) or multiplier_per_tick < 0.0 ): return clampf(current_value, minimum, maximum) return clampf(current_value * pow(multiplier_per_tick, elapsed_ticks), minimum, maximum) static func periodic_occurrences( first_due_tick: int, interval_ticks: int, through_tick: int ) -> int: if first_due_tick < 0 or interval_ticks <= 0 or through_tick < first_due_tick: return 0 return (through_tick - first_due_tick) / interval_ticks + 1 static func transfer_amount( available: float, demand: float, rate_per_tick: float, elapsed_ticks: int, loss_fraction: float = 0.0 ) -> Dictionary: var sent := minf( maxf(available, 0.0), minf(maxf(demand, 0.0), maxf(rate_per_tick, 0.0) * maxi(elapsed_ticks, 0)) ) var received := sent * (1.0 - clampf(loss_fraction, 0.0, 1.0)) return { "sent": sent, "received": received, "remaining_available": maxf(available - sent, 0.0), "remaining_demand": maxf(demand - received, 0.0), }