140 lines
3.7 KiB
GDScript
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)
|