Files
Ransom-s-wake/src/survival_model.gd
T

140 lines
3.7 KiB
GDScript

class_name SurvivalModel
extends RefCounted
enum AlertLevel {
NORMAL,
CAUTION,
RETURN,
CRITICAL,
EMPTY,
}
const DEFAULT_OXYGEN_SECONDS := 600.0
const DEFAULT_BATTERY := 100.0
const DEFAULT_HULL := 100.0
const DEFAULT_NOMINAL_DEPTH := 60.0
const DEFAULT_CRITICAL_DEPTH := 72.0
const DEFAULT_CRUSH_DEPTH := 78.0
var oxygen_max := DEFAULT_OXYGEN_SECONDS
var battery_max := DEFAULT_BATTERY
var hull_max := DEFAULT_HULL
var oxygen := DEFAULT_OXYGEN_SECONDS
var battery := DEFAULT_BATTERY
var hull := DEFAULT_HULL
var nominal_depth := DEFAULT_NOMINAL_DEPTH
var critical_depth := DEFAULT_CRITICAL_DEPTH
var crush_depth := DEFAULT_CRUSH_DEPTH
var oxygen_drain_per_second := 1.0
var battery_drain_per_second_at_full_thrust := 0.60
func reset() -> void:
oxygen = oxygen_max
battery = battery_max
hull = hull_max
func tick(delta: float, thrust_ratio: float, depth: float, collision_damage: float = 0.0) -> Dictionary:
if delta <= 0.0:
return snapshot(depth)
oxygen = maxf(0.0, oxygen - oxygen_drain_per_second * delta)
battery = maxf(
0.0,
battery - battery_drain_per_second_at_full_thrust * clampf(thrust_ratio, 0.0, 1.0) * delta
)
if depth > nominal_depth:
if depth >= crush_depth:
hull -= 25.0 * delta
else:
var pressure_range := maxf(critical_depth - nominal_depth, 0.001)
var pressure_ratio := clampf((depth - nominal_depth) / pressure_range, 0.0, 1.0)
var damage_rate := lerpf(0.15, 5.0, pressure_ratio * pressure_ratio)
if depth > critical_depth:
var critical_ratio := (depth - critical_depth) / maxf(crush_depth - critical_depth, 0.001)
damage_rate = lerpf(5.0, 18.0, clampf(critical_ratio, 0.0, 1.0))
hull -= damage_rate * delta
hull = clampf(hull - maxf(collision_damage, 0.0), 0.0, hull_max)
return snapshot(depth)
func snapshot(depth: float) -> Dictionary:
return {
"oxygen": oxygen,
"oxygen_max": oxygen_max,
"oxygen_ratio": _ratio(oxygen, oxygen_max),
"battery": battery,
"battery_max": battery_max,
"battery_ratio": _ratio(battery, battery_max),
"hull": hull,
"hull_max": hull_max,
"hull_ratio": _ratio(hull, hull_max),
"depth": maxf(depth, 0.0),
"nominal_depth": nominal_depth,
"critical_depth": critical_depth,
"crush_depth": crush_depth,
"oxygen_alert": alert_level(oxygen, oxygen_max),
"battery_alert": alert_level(battery, battery_max),
"hull_alert": alert_level(hull, hull_max),
"depth_alert": depth_alert_level(depth),
"loss_reason": loss_reason(),
}
func loss_reason() -> StringName:
if hull <= 0.0:
return &"hull_failure"
if oxygen <= 0.0:
return &"oxygen_depleted"
return &""
func alert_level(value: float, maximum: float) -> AlertLevel:
var ratio := _ratio(value, maximum)
if ratio <= 0.0:
return AlertLevel.EMPTY
if ratio <= 0.05:
return AlertLevel.CRITICAL
if ratio <= 0.15:
return AlertLevel.RETURN
if ratio <= 0.30:
return AlertLevel.CAUTION
return AlertLevel.NORMAL
func depth_alert_level(depth: float) -> AlertLevel:
if depth >= crush_depth:
return AlertLevel.EMPTY
if depth >= critical_depth:
return AlertLevel.CRITICAL
if depth > nominal_depth:
return AlertLevel.RETURN
if depth >= nominal_depth * 0.85:
return AlertLevel.CAUTION
return AlertLevel.NORMAL
func estimated_return_energy(distance: float, vertical_gain: float = 0.0) -> float:
var seconds_at_cruise := maxf(distance, 0.0) / 6.0
var base_cost := seconds_at_cruise * battery_drain_per_second_at_full_thrust
var vertical_cost := maxf(vertical_gain, 0.0) * 0.035
return base_cost + vertical_cost
func force_critical_state() -> void:
oxygen = minf(oxygen, 30.0)
battery = minf(battery, 12.0)
hull = minf(hull, 28.0)
func _ratio(value: float, maximum: float) -> float:
if maximum <= 0.0:
return 0.0
return clampf(value / maximum, 0.0, 1.0)