neon-sprawl/client/scripts/cooldown_state.gd

88 lines
2.7 KiB
GDScript

extends RefCounted
## Local mirror for GET [code]/game/players/{id}/cooldown-snapshot[/code] (NEO-32).
## Uses [code]serverTimeUtc[/code] + receive tick to approximate server "now" between polls.
const SLOT_COUNT: int = 8
## Reject bogus [method Time.get_unix_time_from_datetime_string] results (failed parse → 0).
const MIN_VALID_UNIX_ANCHOR: float = 1_000_000_000.0
var _slot_end_unix: PackedFloat64Array = PackedFloat64Array()
var _server_unix_at_receive: float = 0.0
var _ticks_msec_at_receive: int = 0
func _init() -> void:
_slot_end_unix.resize(SLOT_COUNT)
for i in range(SLOT_COUNT):
_slot_end_unix[i] = 0.0
func apply_snapshot(snapshot: Dictionary) -> void:
for i in range(SLOT_COUNT):
_slot_end_unix[i] = 0.0
var st: Variant = snapshot.get("serverTimeUtc", null)
var anchor: float = 0.0
if st is String:
var su: String = (st as String).strip_edges()
if not su.is_empty():
anchor = Time.get_unix_time_from_datetime_string(su)
elif st is float:
anchor = st as float
elif st is int:
anchor = float(st as int)
if anchor < MIN_VALID_UNIX_ANCHOR:
anchor = Time.get_unix_time_from_system()
_server_unix_at_receive = anchor
_ticks_msec_at_receive = Time.get_ticks_msec()
var slots: Variant = snapshot.get("slots", [])
if slots is Array:
for item in slots as Array:
if not item is Dictionary:
continue
var d: Dictionary = item
var idx: int = int(d.get("slotIndex", -1))
if idx < 0 or idx >= SLOT_COUNT:
continue
var end_v: Variant = d.get("cooldownEndsAtUtc", null)
if end_v is String and not (end_v as String).is_empty():
var end_s: String = (end_v as String).strip_edges()
var end_unix: float = Time.get_unix_time_from_datetime_string(end_s)
if end_unix >= MIN_VALID_UNIX_ANCHOR:
_slot_end_unix[idx] = end_unix
func _effective_server_unix() -> float:
return _server_unix_at_receive + (Time.get_ticks_msec() - _ticks_msec_at_receive) * 0.001
func remaining_for_slot(slot_index: int) -> float:
if slot_index < 0 or slot_index >= _slot_end_unix.size():
return 0.0
var end_u: float = _slot_end_unix[slot_index]
if end_u <= 0.0:
return 0.0
return maxf(0.0, end_u - _effective_server_unix())
func is_slot_cooling(slot_index: int) -> bool:
return remaining_for_slot(slot_index) > 0.0
func any_slot_cooling() -> bool:
for i in range(_slot_end_unix.size()):
if remaining_for_slot(i) > 0.0:
return true
return false
func build_hud_line() -> String:
var parts: PackedStringArray = PackedStringArray()
for i in range(_slot_end_unix.size()):
var rem: float = remaining_for_slot(i)
if rem > 0.0:
parts.append("slot %d: %.1fs" % [i, rem])
if parts.is_empty():
return "Cooldowns: (ready)"
return "Cooldowns: " + ", ".join(parts)