384 lines
22 KiB
GDScript
384 lines
22 KiB
GDScript
class_name P92BattlePresenter
|
||
extends RefCounted
|
||
|
||
const FIELD_BACKGROUND := preload("res://assets/backgrounds/first_expedition_field.png")
|
||
const Rules := preload("res://domain/p9_2/battle_rules.gd")
|
||
|
||
var host: Control
|
||
var root: Control
|
||
|
||
func _init(runtime_host: Control) -> void:
|
||
host = runtime_host
|
||
|
||
func render(snapshot: Dictionary, events: Array[Dictionary]) -> void:
|
||
_ensure_root()
|
||
_clear_root()
|
||
root.set_meta("domain_tick", int(snapshot.get("tick", 0)))
|
||
root.set_meta("state_hash", host.authoritative_battle_service.state.state_hash())
|
||
root.set_meta("event_hash", host.authoritative_battle_service.state.event_hash())
|
||
root.set_meta("outcome", String(snapshot.get("outcome", "")))
|
||
root.set_meta("speed", host.authoritative_speed_multiplier)
|
||
root.set_meta("inspect_open", host.authoritative_inspect_open)
|
||
_add_background()
|
||
_add_wash()
|
||
_add_heading(snapshot)
|
||
_add_status_panels(snapshot)
|
||
_add_world_line(snapshot, events)
|
||
_add_event_ribbon(snapshot, events)
|
||
if bool(snapshot.terminal):
|
||
_add_result_panel(snapshot)
|
||
else:
|
||
_add_control_rail(snapshot)
|
||
if host.authoritative_inspect_open:
|
||
_add_inspect_panel(snapshot)
|
||
|
||
func cleanup() -> void:
|
||
if root != null and is_instance_valid(root):
|
||
root.queue_free()
|
||
root = null
|
||
|
||
func _ensure_root() -> void:
|
||
if root != null and is_instance_valid(root):
|
||
return
|
||
root = Control.new()
|
||
root.name = "P92BattleRoot"
|
||
root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
host.add_child(root)
|
||
|
||
func _clear_root() -> void:
|
||
for child in root.get_children():
|
||
root.remove_child(child)
|
||
child.queue_free()
|
||
|
||
func _portrait() -> bool:
|
||
return host.size.y > host.size.x
|
||
|
||
func _add_background() -> void:
|
||
var background := TextureRect.new()
|
||
background.name = "P92WorldBackground"
|
||
background.texture = FIELD_BACKGROUND
|
||
background.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
background.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
|
||
background.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
background.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
root.add_child(background)
|
||
|
||
func _add_wash() -> void:
|
||
var wash := ColorRect.new()
|
||
wash.name = "P92BattleWash"
|
||
wash.color = Color(0.01, 0.025, 0.055, 0.34)
|
||
wash.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
wash.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
root.add_child(wash)
|
||
|
||
func _add_heading(snapshot: Dictionary) -> void:
|
||
var title_size := 14 if _portrait() else 18
|
||
var title: Label = host.facade.game_label(host.facade.tr_text("裂隙守望 · 自动战斗", "Breach Watch · Auto Battle"), title_size, host.facade.COLOR_WHITE)
|
||
title.name = "P92BattleTitle"
|
||
title.position = Vector2(14, 8)
|
||
title.size = Vector2(host.size.x - 28, 34) if _portrait() else Vector2(host.size.x * 0.60, 34)
|
||
root.add_child(title)
|
||
var status_text: String
|
||
if bool(snapshot.terminal):
|
||
status_text = host.facade.tr_text("结果:%s" % _outcome_name(String(snapshot.outcome)), "Result: %s" % _outcome_name(String(snapshot.outcome)))
|
||
else:
|
||
status_text = host.facade.tr_text("%d 步 · %.1f 秒 · %dx" % [int(snapshot.tick), float(snapshot.tick) * Rules.TICK_SECONDS, host.authoritative_speed_multiplier], "Tick %d · %.1fs · %dx" % [int(snapshot.tick), float(snapshot.tick) * Rules.TICK_SECONDS, host.authoritative_speed_multiplier])
|
||
if bool(snapshot.paused):
|
||
status_text += host.facade.tr_text(" · 已暂停", " · Paused")
|
||
var status: Label = host.facade.centered_label(status_text, 10, host.facade.COLOR_GOLD_LIGHT)
|
||
status.name = "P92TickStatus"
|
||
status.position = Vector2(14, 43) if _portrait() else Vector2(host.size.x * 0.65, 8)
|
||
status.size = Vector2(host.size.x - 28, 38) if _portrait() else Vector2(host.size.x * 0.33, 36)
|
||
status.add_theme_stylebox_override("normal", host.facade.lacquer_style(Color(0.03, 0.05, 0.10, 0.92), host.facade.COLOR_GOLD, 2))
|
||
root.add_child(status)
|
||
|
||
func _add_status_panels(snapshot: Dictionary) -> void:
|
||
var members: Dictionary = snapshot.members
|
||
var enemies: Dictionary = snapshot.enemies
|
||
if _portrait():
|
||
var panel_width := host.size.x - 28.0
|
||
_add_health_column("P92PartyStatus", Vector2(14, 88), Vector2(panel_width, 92), members.ids, members.hp, true)
|
||
_add_health_column("P92EnemyStatus", Vector2(14, 186), Vector2(panel_width, 72), enemies.ids, enemies.hp, false)
|
||
else:
|
||
_add_health_column("P92PartyStatus", Vector2(14, 50), Vector2(226, 88), members.ids, members.hp, true)
|
||
_add_health_column("P92EnemyStatus", Vector2(host.size.x - 240, 50), Vector2(226, 68), enemies.ids, enemies.hp, false)
|
||
|
||
func _add_health_column(node_name: String, panel_position: Vector2, panel_size: Vector2, ids: Array, hp: Dictionary, members: bool) -> void:
|
||
var panel := PanelContainer.new()
|
||
panel.name = node_name
|
||
panel.position = panel_position
|
||
panel.size = panel_size
|
||
panel.add_theme_stylebox_override("panel", host.facade.lacquer_style(Color(0.025, 0.045, 0.09, 0.92), host.facade.COLOR_TEAL if members else host.facade.COLOR_CRIMSON, 2))
|
||
root.add_child(panel)
|
||
var box := VBoxContainer.new()
|
||
box.add_theme_constant_override("separation", 1)
|
||
panel.add_child(box)
|
||
for actor_id in ids:
|
||
var maximum: int = int(Rules.member_definition(actor_id).get("max_hp", 0)) if members else int(Rules.enemy_definition(actor_id).get("max_hp", 0))
|
||
var row := HBoxContainer.new()
|
||
row.name = ("MemberHP_" if members else "EnemyHP_") + String(actor_id)
|
||
row.set_meta("actor_id", actor_id)
|
||
row.set_meta("hp", int(hp.get(actor_id, 0)))
|
||
box.add_child(row)
|
||
var label: Label = host.facade.game_label((_member_name(actor_id) if members else _enemy_name(actor_id)), 8, host.facade.COLOR_WHITE)
|
||
label.custom_minimum_size.x = 80
|
||
row.add_child(label)
|
||
var bar := ProgressBar.new()
|
||
bar.name = "Bar"
|
||
bar.max_value = maximum
|
||
bar.value = int(hp.get(actor_id, 0))
|
||
bar.show_percentage = false
|
||
bar.custom_minimum_size = Vector2(112, 10)
|
||
bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
bar.add_theme_stylebox_override("background", host.facade.lacquer_style(Color("#14192a"), Color("#667085"), 1))
|
||
bar.add_theme_stylebox_override("fill", host.facade.lacquer_style(host.facade.COLOR_TEAL if members else host.facade.COLOR_CRIMSON, host.facade.COLOR_WHITE, 1))
|
||
row.add_child(bar)
|
||
|
||
func _add_world_line(snapshot: Dictionary, events: Array[Dictionary]) -> void:
|
||
var world_top := 294.0 if _portrait() else maxf(136.0, host.size.y * 0.35)
|
||
var control_height := 126.0 if _portrait() else 72.0
|
||
var world_bottom := host.size.y - control_height
|
||
var ground := ColorRect.new()
|
||
ground.name = "P92ContactGround"
|
||
ground.color = Color(0.05, 0.09, 0.13, 0.88)
|
||
ground.position = Vector2(12, world_bottom - 14)
|
||
ground.size = Vector2(host.size.x - 24, 4)
|
||
root.add_child(ground)
|
||
var members: Dictionary = snapshot.members
|
||
var enemies: Dictionary = snapshot.enemies
|
||
for member_id in members.ids:
|
||
_add_actor(String(member_id), int(members.positions.get(member_id, 0)), int(members.hp.get(member_id, 0)), true, world_top, world_bottom, events)
|
||
for enemy_id in enemies.ids:
|
||
_add_actor(String(enemy_id), int(enemies.positions.get(enemy_id, 100)), int(enemies.hp.get(enemy_id, 0)), false, world_top, world_bottom, events)
|
||
|
||
func _add_actor(actor_id: String, logical_position: int, hp: int, member: bool, world_top: float, world_bottom: float, events: Array[Dictionary]) -> void:
|
||
var available_height := maxf(80.0, world_bottom - world_top)
|
||
var actor_height := clampf(available_height * (0.46 if _portrait() else 0.68), 62.0, 132.0)
|
||
var actor_width := actor_height
|
||
var edge := actor_width * 0.48 + 12.0
|
||
var center_x := lerpf(edge, host.size.x - edge, inverse_lerp(-36.0, 112.0, float(logical_position)))
|
||
var event := _latest_actor_event(actor_id, member, events)
|
||
var animation_state := _member_animation(actor_id, event, logical_position) if member else _enemy_animation(event, hp)
|
||
var texture: Texture2D = host.facade.animation_texture(actor_id if member else "Riftblade", animation_state, float(host.authoritative_battle_service.state.tick) * Rules.TICK_SECONDS)
|
||
var actor := TextureRect.new()
|
||
actor.name = ("P92Member_" if member else "P92Enemy_") + actor_id
|
||
actor.texture = texture
|
||
actor.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
actor.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
actor.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||
actor.position = Vector2(center_x - actor_width * 0.5, world_bottom - actor_height - 8)
|
||
actor.size = Vector2(actor_width, actor_height)
|
||
actor.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
actor.modulate = Color(0.75, 0.65, 1.0) if actor_id == "RiftSpitter" else Color.WHITE
|
||
actor.set_meta("actor_id", actor_id)
|
||
actor.set_meta("logical_position", logical_position)
|
||
actor.set_meta("hp", hp)
|
||
actor.set_meta("event_index", int(event.get("index", -1)))
|
||
actor.set_meta("animation_state", animation_state)
|
||
root.add_child(actor)
|
||
var plaque: Label = host.facade.centered_label((_member_name(actor_id) if member else _enemy_name(actor_id)) + " %d" % hp, 7, host.facade.COLOR_WHITE)
|
||
plaque.name = actor.name + "_Plaque"
|
||
plaque.position = Vector2(clampf(actor.position.x - 4, 2, host.size.x - actor_width - 6), world_top)
|
||
plaque.size = Vector2(actor_width + 8, 22)
|
||
root.add_child(plaque)
|
||
|
||
func _add_event_ribbon(snapshot: Dictionary, events: Array[Dictionary]) -> void:
|
||
var event: Dictionary = events[-1] if not events.is_empty() else (snapshot.events[-1] if not snapshot.events.is_empty() else {})
|
||
var ribbon: Label = host.facade.centered_label(_event_text(event), 9, host.facade.COLOR_WHITE)
|
||
ribbon.name = "P92EventRibbon"
|
||
if _portrait():
|
||
ribbon.position = Vector2(14, 264)
|
||
ribbon.size = Vector2(host.size.x - 28, 42)
|
||
else:
|
||
ribbon.position = Vector2(host.size.x * 0.29, 50)
|
||
ribbon.size = Vector2(host.size.x * 0.42, 44)
|
||
ribbon.add_theme_stylebox_override("normal", host.facade.lacquer_style(Color(0.04, 0.07, 0.13, 0.95), host.facade.COLOR_GOLD, 2))
|
||
ribbon.set_meta("event_index", int(event.get("index", -1)))
|
||
ribbon.set_meta("event_kind", String(event.get("kind", "")))
|
||
ribbon.set_meta("source_member_id", String(event.get("source_member_id", "")))
|
||
ribbon.set_meta("target_member_id", String(event.get("target_member_id", "")))
|
||
root.add_child(ribbon)
|
||
|
||
func _add_control_rail(snapshot: Dictionary) -> void:
|
||
var panel := PanelContainer.new()
|
||
panel.name = "P92ControlRail"
|
||
panel.position = Vector2(10, host.size.y - (118 if _portrait() else 68))
|
||
panel.size = Vector2(host.size.x - 20, 110 if _portrait() else 60)
|
||
panel.add_theme_stylebox_override("panel", host.facade.lacquer_style(Color(0.02, 0.035, 0.075, 0.97), host.facade.COLOR_GOLD, 2))
|
||
root.add_child(panel)
|
||
var controls := GridContainer.new()
|
||
controls.columns = 3 if _portrait() else 5
|
||
controls.add_theme_constant_override("h_separation", 5)
|
||
controls.add_theme_constant_override("v_separation", 4)
|
||
panel.add_child(controls)
|
||
var pause_text: String = host.facade.tr_text("继续", "Resume") if bool(snapshot.paused) else host.facade.tr_text("暂停", "Pause")
|
||
controls.add_child(_control_button("P92PauseButton", pause_text, host.toggle_authoritative_pause, false, 72))
|
||
controls.add_child(_control_button("P92Speed1Button", ("● " if host.authoritative_speed_multiplier == 1 else "") + "1×", func(): host.set_authoritative_speed(1), host.authoritative_speed_multiplier == 1, 60))
|
||
controls.add_child(_control_button("P92Speed2Button", ("● " if host.authoritative_speed_multiplier == 2 else "") + "2×", func(): host.set_authoritative_speed(2), host.authoritative_speed_multiplier == 2, 60))
|
||
var inspect_text: String = host.facade.tr_text("关闭战报", "Close log") if host.authoritative_inspect_open else host.facade.tr_text("查看战报", "Inspect")
|
||
controls.add_child(_control_button("P92InspectButton", inspect_text, host.toggle_authoritative_inspect, host.authoritative_inspect_open, 92))
|
||
var retreat_available := int(snapshot.tick) >= Rules.RETREAT_MIN_TICK
|
||
var retreat_pending := int(snapshot.retreat_requested_tick) >= 0
|
||
var retreat_text: String = host.facade.tr_text("撤退 %d步" % maxi(0, int(snapshot.retreat_resolution_tick) - int(snapshot.tick)), "Retreat %dt" % maxi(0, int(snapshot.retreat_resolution_tick) - int(snapshot.tick))) if retreat_pending else (host.facade.tr_text("撤退", "Retreat") if retreat_available else host.facade.tr_text("撤退 · %d步解锁" % Rules.RETREAT_MIN_TICK, "Retreat · unlock %d" % Rules.RETREAT_MIN_TICK))
|
||
var retreat := _control_button("P92RetreatButton", retreat_text, host.request_authoritative_retreat, retreat_pending, 138)
|
||
retreat.disabled = not retreat_available
|
||
retreat.tooltip_text = host.facade.tr_text("第 50 步后可撤退", "Retreat unlocks after tick 50") if not retreat_available else host.facade.tr_text("撤退需继续结算 20 步;重复点击不会重复发起", "Withdrawal resolves over 20 ticks; repeated taps keep the first receipt")
|
||
retreat.set_meta("unlock_tick", Rules.RETREAT_MIN_TICK)
|
||
retreat.set_meta("pending", retreat_pending)
|
||
controls.add_child(retreat)
|
||
|
||
func _control_button(node_name: String, text: String, callback: Callable, selected: bool, minimum_width: float) -> Button:
|
||
var button: Button = host.facade.game_button(node_name, text, callback, false)
|
||
button.custom_minimum_size = Vector2(minimum_width if not _portrait() else 102, 44)
|
||
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
button.add_theme_font_size_override("font_size", host.facade.scaled(10))
|
||
if selected:
|
||
button.add_theme_color_override("font_color", host.facade.COLOR_GOLD_LIGHT)
|
||
button.add_theme_stylebox_override("normal", host.facade.lacquer_style(Color(0.06, 0.13, 0.18, 0.98), host.facade.COLOR_TEAL, 3))
|
||
return button
|
||
|
||
func _add_inspect_panel(snapshot: Dictionary) -> void:
|
||
var panel := PanelContainer.new()
|
||
panel.name = "P92InspectPanel"
|
||
panel.position = Vector2(14, 314) if _portrait() else Vector2(host.size.x * 0.29, 100)
|
||
panel.size = Vector2(host.size.x - 28, minf(360, host.size.y - 440)) if _portrait() else Vector2(host.size.x * 0.42, host.size.y - 176)
|
||
panel.add_theme_stylebox_override("panel", host.facade.lacquer_style(Color(0.018, 0.03, 0.065, 0.98), host.facade.COLOR_TEAL, 3))
|
||
panel.set_meta("snapshot_hash", JSON.stringify(snapshot).hash())
|
||
root.add_child(panel)
|
||
var box := VBoxContainer.new()
|
||
box.add_theme_constant_override("separation", 2)
|
||
panel.add_child(box)
|
||
box.add_child(host.facade.game_label(host.facade.tr_text("最近权威战报 · 只读", "Recent authoritative events · read only"), 12, host.facade.COLOR_GOLD_LIGHT))
|
||
box.add_child(host.facade.game_label(_inspect_actor_line(snapshot), 8, host.facade.COLOR_PARCHMENT))
|
||
var events: Array = host.authoritative_inspect_events
|
||
if events.is_empty():
|
||
box.add_child(host.facade.game_label(host.facade.tr_text("尚无事件", "No events yet"), 9, host.facade.COLOR_WHITE))
|
||
else:
|
||
for event in events.slice(maxi(0, events.size() - (4 if _portrait() else 5))):
|
||
var line := "#%d · %d · %s" % [int(event.get("index", -1)), int(event.get("tick", 0)), _event_text(event)]
|
||
box.add_child(host.facade.game_label(line, 8, host.facade.COLOR_WHITE))
|
||
|
||
func _inspect_actor_line(snapshot: Dictionary) -> String:
|
||
var living_members: Array[String] = []
|
||
for member_id in snapshot.members.ids:
|
||
living_members.append("%s:%d" % [String(member_id), int(snapshot.members.hp.get(member_id, 0))])
|
||
var living_enemies: Array[String] = []
|
||
for enemy_id in snapshot.enemies.ids:
|
||
living_enemies.append("%s:%d" % [String(enemy_id), int(snapshot.enemies.hp.get(enemy_id, 0))])
|
||
return host.facade.tr_text("队伍 %s 敌人 %s" % [" / ".join(living_members), " / ".join(living_enemies)], "Party %s · Enemies %s" % [" / ".join(living_members), " / ".join(living_enemies)])
|
||
|
||
func _add_result_panel(snapshot: Dictionary) -> void:
|
||
var panel := Panel.new()
|
||
panel.name = "P92ResultPanel"
|
||
panel.position = Vector2(12, 86) if _portrait() else Vector2(host.size.x * 0.14, 52)
|
||
panel.size = Vector2(host.size.x - 24, host.size.y - 110) if _portrait() else Vector2(host.size.x * 0.72, host.size.y - 68)
|
||
panel.add_theme_stylebox_override("panel", host.facade.lacquer_style(Color(0.018, 0.03, 0.065, 0.98), host.facade.COLOR_GOLD, 3))
|
||
root.add_child(panel)
|
||
var lines := _result_lines(snapshot)
|
||
var line_height := clampf((panel.size.y - 78.0) / 5.0, 36.0, 64.0)
|
||
for index in lines.size():
|
||
var line: Label = host.facade.game_label(lines[index], 10 if index == 0 else 9, host.facade.COLOR_GOLD_LIGHT if index == 0 else host.facade.COLOR_WHITE)
|
||
line.name = "P92ResultLine%d" % (index + 1)
|
||
line.position = Vector2(14, 10 + float(index) * line_height)
|
||
line.size = Vector2(panel.size.x - 28, line_height - 2)
|
||
line.set_meta("grammar_index", index + 1)
|
||
panel.add_child(line)
|
||
var actions := Control.new()
|
||
actions.name = "P92ResultActions"
|
||
actions.position = Vector2(14, panel.size.y - 60)
|
||
actions.size = Vector2(panel.size.x - 28, 50)
|
||
panel.add_child(actions)
|
||
var home: Button = host.facade.game_button("P92HomeButton", host.facade.tr_text("返回首页", "Home"), host.exit_authoritative_battle_to_home, false)
|
||
var retry: Button = host.facade.game_button("P92RetryButton", host.facade.tr_text("重新挑战", "Retry"), host.retry_authoritative_battle, true)
|
||
home.custom_minimum_size = Vector2(120, 44)
|
||
retry.custom_minimum_size = Vector2(120, 44)
|
||
var button_width := (actions.size.x - 8) * 0.5
|
||
home.position = Vector2.ZERO
|
||
home.size = Vector2(button_width, 48)
|
||
retry.position = Vector2(button_width + 8, 0)
|
||
retry.size = Vector2(button_width, 48)
|
||
actions.add_child(home)
|
||
actions.add_child(retry)
|
||
|
||
func _result_lines(snapshot: Dictionary) -> Array[String]:
|
||
var outcome := String(snapshot.outcome)
|
||
return [
|
||
host.facade.tr_text("1. 结果:%s" % _outcome_name(outcome), "1. Outcome: %s" % _outcome_name(outcome)),
|
||
host.facade.tr_text("2. 原因:%s" % _cause_name(String(snapshot.outcome_cause)), "2. Cause: %s" % _cause_name(String(snapshot.outcome_cause))),
|
||
host.facade.tr_text("3. 生还者:%s" % _survivor_text(snapshot), "3. Survivors: %s" % _survivor_text(snapshot)),
|
||
host.facade.tr_text("4. 收益/代价:裂隙守望凭证 BW-01(本阶段不写入存档)" if outcome == "victory" else "4. 收益/代价:0 金币", "4. Reward/cost: Breach Watch receipt BW-01 (not persisted yet)" if outcome == "victory" else "4. Reward/cost: 0 Coin"),
|
||
host.facade.tr_text("5. 下一步:返回首页或重试;P9.2 不执行永久伤亡/恢复", "5. Next: Home or Retry; P9.2 applies no permanent casualty/recovery"),
|
||
]
|
||
|
||
func _survivor_text(snapshot: Dictionary) -> String:
|
||
var survivors: Array[String] = []
|
||
for member_id in snapshot.members.ids:
|
||
var hp := int(snapshot.members.hp.get(member_id, 0))
|
||
if hp > 0:
|
||
survivors.append("%s %d HP" % [String(member_id), hp])
|
||
return host.facade.tr_text("无", "none") if survivors.is_empty() else ", ".join(survivors)
|
||
|
||
func _latest_actor_event(actor_id: String, member: bool, events: Array[Dictionary]) -> Dictionary:
|
||
for index in range(events.size() - 1, -1, -1):
|
||
var event: Dictionary = events[index]
|
||
if member and String(event.get("source_member_id", "")) == actor_id:
|
||
return event
|
||
if not member and String(event.get("source_enemy_id", "")) == actor_id:
|
||
return event
|
||
return {}
|
||
|
||
func _member_animation(actor_id: String, event: Dictionary, logical_position: int) -> String:
|
||
if not event.is_empty():
|
||
if String(event.get("cue", "")) == "heal":
|
||
return "lantern_heal"
|
||
return host.facade.profession_action_state(actor_id)
|
||
if logical_position < 40:
|
||
return "advance"
|
||
return "idle"
|
||
|
||
func _enemy_animation(event: Dictionary, hp: int) -> String:
|
||
if hp <= 0:
|
||
return "death"
|
||
if not event.is_empty():
|
||
return "attack"
|
||
return "advance" if host.authoritative_battle_service.state.tick < 24 else "idle"
|
||
|
||
func _event_text(event: Dictionary) -> String:
|
||
if event.is_empty():
|
||
return host.facade.tr_text("双方正在接近…", "Both lines are advancing…")
|
||
match String(event.get("kind", "")):
|
||
"battle_started":
|
||
return host.facade.tr_text("队伍进入裂隙守望,战斗自动执行", "The Company enters Breach Watch; combat resolves automatically")
|
||
"contact":
|
||
return host.facade.tr_text("前线接触:近战停止推进并开始交战", "Contact: melee stops and engages")
|
||
"member_action":
|
||
var source := _member_name(String(event.get("source_member_id", "")))
|
||
return host.facade.tr_text("%s 执行职业行动 %+d" % [source, int(event.get("delta", 0))], "%s used a profession action %+d" % [source, int(event.get("delta", 0))])
|
||
"enemy_action":
|
||
return host.facade.tr_text("敌方施压 %+d", "Enemy pressure %+d") % int(event.get("delta", 0))
|
||
"paused_changed":
|
||
return host.facade.tr_text("战斗已暂停" if bool(event.get("paused", false)) else "战斗继续", "Battle paused" if bool(event.get("paused", false)) else "Battle resumed")
|
||
"retreat_started":
|
||
return host.facade.tr_text("撤退已发起:第 %d 步结算" % int(event.get("resolves_at", 0)), "Withdrawal started: resolves at tick %d" % int(event.get("resolves_at", 0)))
|
||
"outcome_locked":
|
||
return host.facade.tr_text("结果锁定:%s" % _outcome_name(String(event.get("outcome", ""))), "Outcome locked: %s" % _outcome_name(String(event.get("outcome", ""))))
|
||
return host.facade.tr_text("自动战斗正在解析", "Automatic battle is resolving")
|
||
|
||
func _member_name(member_id: String) -> String:
|
||
return host.facade.candidate_name(member_id)
|
||
|
||
func _enemy_name(enemy_id: String) -> String:
|
||
return {"RiftBulwark": host.facade.tr_text("裂隙壁垒", "Rift Bulwark"), "RiftSpitter": host.facade.tr_text("裂隙喷吐者", "Rift Spitter")}.get(enemy_id, enemy_id)
|
||
|
||
func _outcome_name(outcome: String) -> String:
|
||
return {"victory": host.facade.tr_text("胜利", "Victory"), "defeat": host.facade.tr_text("失败", "Defeat"), "timeout": host.facade.tr_text("超时", "Timeout"), "retreat": host.facade.tr_text("撤退", "Retreat")}.get(outcome, host.facade.tr_text("进行中", "In progress"))
|
||
|
||
func _cause_name(cause: String) -> String:
|
||
return {
|
||
"all_enemies_defeated": host.facade.tr_text("全部敌人被击败", "all enemies defeated"),
|
||
"all_members_fallen": host.facade.tr_text("本次遭遇中全队倒下", "all Guild members fell in this encounter"),
|
||
"encounter_timeout": host.facade.tr_text("90 秒内未完成目标", "the 90-second objective expired"),
|
||
"withdrawal_complete": host.facade.tr_text("撤退指令完成 20 步结算", "withdrawal completed after 20 ticks"),
|
||
}.get(cause, cause)
|