neon-sprawl/client/scripts/npc_combat_hud_helpers.gd

134 lines
5.0 KiB
GDScript

extends RefCounted
## NEO-97: shared combat-active gate + NPC/telegraph HUD copy for [code]main.gd[/code] and tests.
const PrototypeTargetConstants := preload("res://scripts/prototype_target_constants.gd")
const NpcRuntimeClient := preload("res://scripts/npc_runtime_client.gd")
static func locked_target_id_from_state(state: Dictionary) -> String:
var locked_variant: Variant = state.get("lockedTargetId", null)
if locked_variant is String:
return (locked_variant as String).strip_edges()
return ""
static func is_known_npc_id(target_id: String) -> bool:
return PrototypeTargetConstants.ANCHORS.has(target_id.strip_edges())
static func incoming_aggro_row(dev_player_id: String, npc_runtime_snapshot: Dictionary) -> Dictionary:
if npc_runtime_snapshot.is_empty():
return {}
return NpcRuntimeClient.row_for_aggro_holder(dev_player_id, npc_runtime_snapshot)
static func is_combat_active(
last_target_state: Dictionary, npc_runtime_snapshot: Dictionary, dev_player_id: String
) -> bool:
var lock_id := locked_target_id_from_state(last_target_state)
if is_known_npc_id(lock_id):
return true
return not incoming_aggro_row(dev_player_id, npc_runtime_snapshot).is_empty()
static func resolve_hud_npc_rows(
last_target_state: Dictionary, npc_runtime_snapshot: Dictionary, dev_player_id: String
) -> Dictionary:
var lock_id := locked_target_id_from_state(last_target_state)
var locked_row: Dictionary = {}
if is_known_npc_id(lock_id):
locked_row = NpcRuntimeClient.npc_row_in_snapshot(lock_id, npc_runtime_snapshot)
var incoming_row: Dictionary = {}
var aggro_row: Dictionary = incoming_aggro_row(dev_player_id, npc_runtime_snapshot)
if not aggro_row.is_empty():
var aggro_id: String = str(aggro_row.get("npcInstanceId", "")).strip_edges()
if aggro_id != lock_id:
incoming_row = aggro_row
return {"locked": locked_row, "incoming": incoming_row}
static func build_npc_state_label(
rows: Dictionary, runtime_sync_error: String = ""
) -> String:
var lines: PackedStringArray = PackedStringArray()
var locked_row: Dictionary = rows.get("locked", {}) as Dictionary
if not locked_row.is_empty():
var locked_id: String = str(locked_row.get("npcInstanceId", ""))
lines.append(
"NPC state: %s%s"
% [
PrototypeTargetConstants.display_short_name(locked_id),
str(locked_row.get("state", "unknown")),
]
)
var incoming_row: Dictionary = rows.get("incoming", {}) as Dictionary
if not incoming_row.is_empty():
var incoming_id: String = str(incoming_row.get("npcInstanceId", ""))
lines.append(
"Incoming: %s%s"
% [
PrototypeTargetConstants.display_short_name(incoming_id),
str(incoming_row.get("state", "unknown")),
]
)
if lines.is_empty():
if not runtime_sync_error.is_empty():
return "NPC state: — (%s)" % runtime_sync_error
return "NPC state: —"
if not runtime_sync_error.is_empty():
lines.append("(runtime sync: %s)" % runtime_sync_error)
return "\n".join(lines)
static func format_telegraph_line(row: Dictionary, hud_state: RefCounted = null) -> String:
var instance_id: String = str(row.get("npcInstanceId", "")).strip_edges()
var telegraph: Variant = row.get("activeTelegraph", null)
if telegraph == null or not telegraph is Dictionary:
return ""
var telegraph_dict: Dictionary = telegraph as Dictionary
var remaining: float = float(telegraph_dict.get("windupRemainingSeconds", 0.0))
if hud_state != null and hud_state.has_method("display_windup_remaining"):
remaining = float(hud_state.call("display_windup_remaining", instance_id))
if remaining <= 0.0:
return ""
var short_name: String = PrototypeTargetConstants.display_short_name(instance_id)
var telegraph_id: String = str(telegraph_dict.get("telegraphId", ""))
var archetype: String = str(telegraph_dict.get("archetypeKind", ""))
return "Telegraph: %s %s · %.1fs (%s)" % [short_name, telegraph_id, remaining, archetype]
static func build_telegraph_label(
rows: Dictionary, hud_state: RefCounted = null, runtime_sync_error: String = ""
) -> String:
var locked_row: Dictionary = rows.get("locked", {}) as Dictionary
var line: String = format_telegraph_line(locked_row, hud_state)
if line.is_empty():
line = format_telegraph_line(rows.get("incoming", {}) as Dictionary, hud_state)
if line.is_empty():
if not runtime_sync_error.is_empty():
return "Telegraph: — (%s)" % runtime_sync_error
return "Telegraph: —"
return line
static func has_interpolated_telegraph_display(
rows: Dictionary, hud_state: RefCounted
) -> bool:
if hud_state == null:
return false
for key: String in ["locked", "incoming"]:
var row: Dictionary = rows.get(key, {}) as Dictionary
if row.is_empty():
continue
var instance_id: String = str(row.get("npcInstanceId", "")).strip_edges()
if instance_id.is_empty():
continue
var telegraph: Variant = row.get("activeTelegraph", null)
if telegraph == null or not telegraph is Dictionary:
continue
if hud_state.has_method("has_active_windup"):
if bool(hud_state.call("has_active_windup", instance_id)):
return true
return false