876 lines
39 KiB
GDScript
876 lines
39 KiB
GDScript
class_name P910RuntimeController
|
|
extends "res://p9_9/runtime_controller.gd"
|
|
|
|
const P910Content = preload("res://content/p9_10_content.gd")
|
|
const P910State = preload("res://p9_10/contract_state.gd")
|
|
const P910BossService = preload("res://p9_10/boss_service.gd")
|
|
const P910BattleService = preload("res://p9_10/battle_service.gd")
|
|
const P910BossBattleService = preload("res://p9_10/boss_battle_service.gd")
|
|
const P910SaveService = preload("res://p9_10/save_service.gd")
|
|
|
|
var boss_battle = P910BossBattleService.new()
|
|
var boss_battle_active := false
|
|
var focused_equipment_id := ""
|
|
var focused_equipment_target := ""
|
|
var focused_company_actor := ""
|
|
var focused_boss_plan := ""
|
|
var last_boss_direction := "front_to_rear"
|
|
var last_boss_rule := ""
|
|
|
|
|
|
func _bootstrap_market() -> void:
|
|
if entry_snapshot.is_empty():
|
|
last_code = "entry_snapshot_required"
|
|
_queue_render()
|
|
return
|
|
market = P910BossService.new()
|
|
var restored := false
|
|
if String(entry_snapshot.get("state_kind", "")) == "p9_10_contract":
|
|
restored = market.restore_snapshot(entry_snapshot)
|
|
elif String(entry_snapshot.get("state_kind", "")) == "p99_run":
|
|
market = P910BossService.new(entry_snapshot)
|
|
restored = market.state.invariants_hold()
|
|
if not restored:
|
|
market = null
|
|
last_code = "p910_snapshot_invalid"
|
|
_queue_render()
|
|
return
|
|
content = P910Content.snapshot(market.state.selected_charter_claim_id)
|
|
battle = P910BattleService.new(market.state.selected_charter_claim_id)
|
|
boss_battle = P910BossBattleService.new()
|
|
saves = P910SaveService.new()
|
|
pending_line_ids.assign(market.state.deployed_recruit_ids)
|
|
focused_hire_id = ""
|
|
focused_route_id = ""
|
|
focused_equipment_id = ""
|
|
focused_equipment_target = ""
|
|
focused_company_actor = ""
|
|
focused_boss_plan = ""
|
|
battle_active = false
|
|
boss_battle_active = false
|
|
battle_result_visible = false
|
|
last_code = "p910_ready"
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if not low_power:
|
|
world_clock += maxf(0.0, delta)
|
|
_update_ambient_actors()
|
|
if not battle_active or battle_result_visible or battle_paused or not battle_auto_advance:
|
|
return
|
|
battle_tick_accumulator += maxf(0.0, delta) * float(battle_speed)
|
|
var ticks := int(floor(battle_tick_accumulator / 0.08))
|
|
if ticks <= 0:
|
|
return
|
|
battle_tick_accumulator -= float(ticks) * 0.08
|
|
advance_battle_ticks(ticks)
|
|
|
|
|
|
func tap_hire_actor(recruit_id: String) -> void:
|
|
if not _consume_interaction() or not _market_ready():
|
|
return
|
|
if focused_hire_id != recruit_id:
|
|
focused_hire_id = recruit_id
|
|
last_code = "hire_inspected"
|
|
_queue_render()
|
|
return
|
|
var result: Dictionary = market.hire(_operation("hire", recruit_id), recruit_id, market.state.revision)
|
|
last_code = String(result.code)
|
|
if result.ok:
|
|
focused_hire_id = ""
|
|
pending_line_ids.assign(market.state.deployed_recruit_ids)
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func tap_equipment_offer(offer_id: String) -> void:
|
|
if not _consume_interaction() or not _market_ready():
|
|
return
|
|
if offer_id not in market.state.available_equipment_offer_ids:
|
|
return
|
|
if focused_equipment_id != offer_id:
|
|
focused_equipment_id = offer_id
|
|
focused_equipment_target = ""
|
|
last_code = "equipment_inspected"
|
|
else:
|
|
last_code = "equipment_target_required"
|
|
_queue_render()
|
|
|
|
|
|
func tap_company_actor(recruit_id: String) -> void:
|
|
if not _consume_interaction() or not _market_ready() or recruit_id not in market.state.company:
|
|
return
|
|
if not focused_equipment_id.is_empty():
|
|
if focused_equipment_target != recruit_id:
|
|
var preview: Dictionary = market.equipment_preview(focused_equipment_id, recruit_id)
|
|
last_code = String(preview.code)
|
|
if preview.ok:
|
|
focused_equipment_target = recruit_id
|
|
_queue_render()
|
|
return
|
|
var result: Dictionary = market.buy_and_equip(_operation("equip", recruit_id), focused_equipment_id, recruit_id, market.state.revision)
|
|
last_code = String(result.code)
|
|
if result.ok:
|
|
focused_equipment_id = ""
|
|
focused_equipment_target = ""
|
|
_save_stable()
|
|
_queue_render()
|
|
return
|
|
focused_company_actor = recruit_id
|
|
last_code = "company_inspected"
|
|
_queue_render()
|
|
|
|
|
|
func commit_final_reorder() -> void:
|
|
if not _consume_interaction() or not _market_ready() or market.state.current_market != 12 or market.state.final_order_confirmed:
|
|
return
|
|
var result: Dictionary
|
|
if market.state.proposed_final_order.is_empty():
|
|
var proposed: Array[String] = market.state.deployed_recruit_ids.duplicate()
|
|
proposed.reverse()
|
|
result = market.propose_final_order(_operation("order", "propose"), proposed, market.state.revision)
|
|
else:
|
|
result = market.confirm_final_order(_operation("order", "confirm"), market.state.revision)
|
|
last_code = String(result.code)
|
|
if result.ok:
|
|
pending_line_ids.assign(market.state.deployed_recruit_ids)
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func tap_route(route_id: String) -> void:
|
|
if not _consume_interaction() or not _market_ready():
|
|
return
|
|
if focused_route_id != route_id:
|
|
focused_route_id = route_id
|
|
last_code = "route_preview"
|
|
_queue_render()
|
|
return
|
|
var result: Dictionary = market.commit_route(_operation("route", route_id), route_id, market.state.revision)
|
|
last_code = String(result.code)
|
|
if not result.ok:
|
|
_queue_render()
|
|
return
|
|
_save_stable()
|
|
battle = P910BattleService.new(market.state.selected_charter_claim_id)
|
|
start_committed_battle()
|
|
|
|
|
|
func recover_company() -> void:
|
|
if not _consume_interaction() or market == null or battle_active:
|
|
return
|
|
var result: Dictionary
|
|
if market.state.recovery_kind == "boss_defeat":
|
|
result = market.recover_boss_defeat(_operation("recover", "boss"))
|
|
else:
|
|
result = market.recover_defeat(_operation("recover", "company"))
|
|
last_code = String(result.code)
|
|
if result.ok:
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func tap_boss_plan(plan: String) -> void:
|
|
if not _consume_interaction() or market == null or market.state.phase != P910State.Phase.BOSS_MUSTER or market.state.recovery_required:
|
|
return
|
|
if focused_boss_plan != plan:
|
|
focused_boss_plan = plan
|
|
last_code = "boss_plan_inspected"
|
|
_queue_render()
|
|
return
|
|
var result: Dictionary = market.commit_boss_plan(_operation("boss-plan", plan), plan, market.state.revision)
|
|
last_code = String(result.code)
|
|
if not result.ok:
|
|
_queue_render()
|
|
return
|
|
_save_stable()
|
|
start_committed_boss()
|
|
|
|
|
|
func return_to_boss_muster() -> void:
|
|
if not _consume_interaction() or market == null or market.state.phase != P910State.Phase.BOSS_ENCOUNTER or battle_active:
|
|
return
|
|
var result: Dictionary = market.return_to_boss_muster(_operation("boss", "back"))
|
|
last_code = String(result.code)
|
|
if result.ok:
|
|
focused_boss_plan = ""
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func start_committed_boss() -> void:
|
|
boss_battle = P910BossBattleService.new()
|
|
var result: Dictionary = boss_battle.start_from_contract(market.state)
|
|
last_code = String(result.code)
|
|
if not result.ok:
|
|
_queue_render()
|
|
return
|
|
boss_battle_active = true
|
|
battle_active = true
|
|
battle_result_visible = false
|
|
battle_paused = false
|
|
battle_speed = 1
|
|
battle_tick_accumulator = 0.0
|
|
battle_actions.clear()
|
|
presented_event_counts.clear()
|
|
last_boss_direction = boss_battle.state.gust_direction
|
|
last_boss_rule = "boss_started"
|
|
_queue_render()
|
|
|
|
|
|
func advance_battle_ticks(ticks: int) -> Dictionary:
|
|
if not boss_battle_active:
|
|
return super.advance_battle_ticks(ticks)
|
|
if not battle_active or battle_result_visible:
|
|
return {"ok": false, "code": "battle_inactive", "events": []}
|
|
var result: Dictionary = boss_battle.advance_ticks(ticks)
|
|
last_code = String(result.code)
|
|
for event in result.events:
|
|
_handle_boss_event(event)
|
|
_update_boss_visuals()
|
|
if boss_battle.state.terminal:
|
|
battle_result_visible = true
|
|
_queue_render()
|
|
return result
|
|
|
|
|
|
func settle_and_return() -> void:
|
|
if not _consume_interaction() or not battle_active:
|
|
return
|
|
var result: Dictionary
|
|
if boss_battle_active:
|
|
if not boss_battle.state.terminal:
|
|
return
|
|
result = market.settle_boss(_operation("boss-settle", boss_battle.state.plan), boss_battle.state.snapshot())
|
|
else:
|
|
if not battle.state.terminal:
|
|
return
|
|
result = market.settle_battle(_operation("settle", battle.state.route_id), battle.state.snapshot())
|
|
last_code = String(result.code)
|
|
if not result.ok:
|
|
_queue_render()
|
|
return
|
|
battle_active = false
|
|
boss_battle_active = false
|
|
battle_result_visible = false
|
|
focused_route_id = ""
|
|
focused_boss_plan = ""
|
|
pending_line_ids.assign(market.state.deployed_recruit_ids)
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func close_contract() -> void:
|
|
if not _consume_interaction() or market == null or market.state.phase != P910State.Phase.CONTRACT_RESULT:
|
|
return
|
|
var result: Dictionary = market.close_contract(_operation("close", "rg01-c02"))
|
|
last_code = String(result.code)
|
|
if result.ok:
|
|
_save_stable()
|
|
_queue_render()
|
|
|
|
|
|
func _market_ready() -> bool:
|
|
return market != null and not battle_active and market.state.phase == P910State.Phase.MARKET
|
|
|
|
|
|
func _save_stable() -> bool:
|
|
if not persistence_enabled or market == null:
|
|
return false
|
|
return bool(saves.save_snapshot(market.state.snapshot()).ok)
|
|
|
|
|
|
func _operation(kind: String, target: String) -> String:
|
|
operation_index += 1
|
|
return "p910-ui-%s-%d-%d-%s" % [kind, market.state.revision, operation_index, target]
|
|
|
|
|
|
func _render_current() -> void:
|
|
if display_root == null:
|
|
return
|
|
_clear_display()
|
|
if size.y > size.x:
|
|
_render_rotate_notice()
|
|
elif market == null:
|
|
_render_missing_entry()
|
|
elif battle_active and boss_battle_active:
|
|
_render_boss_battle()
|
|
elif battle_active:
|
|
_render_battle()
|
|
elif market.state.phase == P910State.Phase.BOSS_MUSTER:
|
|
_render_boss_muster()
|
|
elif market.state.phase == P910State.Phase.CONTRACT_RESULT:
|
|
_render_contract_result()
|
|
elif market.state.phase == P910State.Phase.RUN_CLOSED:
|
|
_render_persistent_result()
|
|
else:
|
|
_render_market()
|
|
|
|
|
|
func _render_market() -> void:
|
|
_add_background(WINDGLASS_BACKGROUND)
|
|
_add_market_header()
|
|
if market.state.recovery_required:
|
|
_render_recovery_ground()
|
|
_render_market_context()
|
|
return
|
|
_render_market_offers()
|
|
_render_company_line()
|
|
_render_route_gates()
|
|
_render_market_context()
|
|
|
|
|
|
func _render_battle() -> void:
|
|
super._render_battle()
|
|
_compact_battle_name_labels()
|
|
|
|
|
|
func _compact_battle_name_labels() -> void:
|
|
var compact := size.x < 900.0 or text_scale > 1.0
|
|
var width := 42.0 if compact else 104.0
|
|
var font_size := 7 if compact else 8
|
|
for node in display_root.find_children("P97BattleName_*", "Label", true, false):
|
|
var label := node as Label
|
|
if label == null:
|
|
continue
|
|
label.size.x = width
|
|
label.add_theme_font_size_override("font_size", _scaled(font_size))
|
|
label.clip_text = true
|
|
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
var actor_id := String(label.name).trim_prefix("P97BattleName_")
|
|
var actor: TextureRect = battle_actors.get(actor_id)
|
|
if actor != null:
|
|
label.position = Vector2(actor.position.x + (actor.size.x - label.size.x) * 0.5, actor.position.y + actor.size.y - 6)
|
|
label.position.y += -6.0 if bool(actor.get_meta("enemy", false)) else 4.0
|
|
|
|
|
|
func _add_market_header() -> void:
|
|
var shade := ColorRect.new()
|
|
shade.name = "P910MarketHeader"
|
|
shade.position = Vector2.ZERO
|
|
shade.size = Vector2(size.x, 50)
|
|
shade.color = Color(0.015, 0.035, 0.075, 0.88)
|
|
shade.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(shade)
|
|
var title := _outlined_label(_tr("第二契约 · 风廊 %d" % market.state.current_market, "CONTRACT 2 · GALEWAY %d" % market.state.current_market), 15, GOLD_LIGHT)
|
|
title.name = "P910MarketTitle"
|
|
title.position = Vector2(14, 3)
|
|
title.size = Vector2(size.x * 0.38, 43)
|
|
title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(title)
|
|
var values := _outlined_label(_tr("◆%d 队伍 %d/%d 胜利 %d/12" % [market.state.coin, market.state.company.size(), market.state.line_capacity, market.state.cleared_rounds], "◆%d COMPANY %d/%d CLEARS %d/12" % [market.state.coin, market.state.company.size(), market.state.line_capacity, market.state.cleared_rounds]), 12, WHITE)
|
|
values.name = "P910MarketValues"
|
|
values.position = Vector2(size.x * 0.38, 3)
|
|
values.size = Vector2(size.x * 0.60 - 12, 43)
|
|
values.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
values.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(values)
|
|
|
|
|
|
func _render_market_offers() -> void:
|
|
var right_edge := size.x - 224.0
|
|
var recruit_ids: Array = market.state.available_recruit_offer_ids
|
|
var slot_width := minf(116.0, maxf(82.0, (right_edge - 24.0) / maxf(1.0, float(recruit_ids.size() + 2))))
|
|
for index in recruit_ids.size():
|
|
var recruit_id := String(recruit_ids[index])
|
|
var row: Dictionary = content.recruit_offers[recruit_id]
|
|
var actor_size := clampf(slot_width * 0.68, 54.0, 76.0)
|
|
var x := 18.0 + float(index) * slot_width
|
|
var y := 62.0
|
|
var actor := _add_actor(String(content.professions[row.profession_id].visual_rig), Vector2(x, y), actor_size, "P910HireActor_%s" % recruit_id, true)
|
|
actor.self_modulate = _p910_profession_color(String(row.profession_id))
|
|
display_root.add_child(_actor_target("P910HireTarget_%s" % recruit_id, Vector2(x - 4, y - 4), Vector2(actor_size + 8, 102), tap_hire_actor.bind(recruit_id)))
|
|
var label := _outlined_label("%s\n◆%d" % [_tr(String(row.name_zh), String(row.name_en)), int(row.list_price)], 8, GOLD_LIGHT if focused_hire_id == recruit_id else WHITE)
|
|
label.position = Vector2(x - 18, y + actor_size - 2)
|
|
label.size = Vector2(actor_size + 36, 42)
|
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(label)
|
|
var equipment_ids: Array = market.state.available_equipment_offer_ids
|
|
for index in equipment_ids.size():
|
|
var offer_id := String(equipment_ids[index])
|
|
var row: Dictionary = content.equipment_offers[offer_id]
|
|
var x := 26.0 + float(recruit_ids.size()) * slot_width + float(index) * 80.0
|
|
var pedestal := TextureRect.new()
|
|
pedestal.name = "P910EquipmentObject_%s" % offer_id
|
|
pedestal.texture = _equipment_texture(String(row.catalog_id))
|
|
pedestal.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
pedestal.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
pedestal.position = Vector2(x, 70)
|
|
pedestal.size = Vector2(54, 54)
|
|
pedestal.self_modulate = GOLD_LIGHT if focused_equipment_id == offer_id else _equipment_color(String(row.catalog_id))
|
|
pedestal.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(pedestal)
|
|
display_root.add_child(_actor_target("P910EquipmentTarget_%s" % offer_id, Vector2(x - 4, 64), Vector2(64, 96), tap_equipment_offer.bind(offer_id)))
|
|
var label := _outlined_label("%s\n◆%d" % [_tr(String(row.name_zh), String(row.name_en)), int(row.list_price)], 8, WHITE)
|
|
label.position = Vector2(x - 18, 120)
|
|
label.size = Vector2(90, 40)
|
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(label)
|
|
|
|
|
|
func _render_company_line() -> void:
|
|
var world_bottom := size.y - 58.0
|
|
var lane_width := size.x - 228.0
|
|
var count: int = market.state.company.size()
|
|
var actor_size := clampf(minf(size.y * 0.18, (lane_width - 20.0) / maxf(1.0, float(count))), 48.0, 68.0)
|
|
var spacing := minf(actor_size + 2.0, (lane_width - actor_size - 18.0) / maxf(1.0, float(count - 1)))
|
|
var y := world_bottom - actor_size - 12.0
|
|
for index in count:
|
|
var recruit_id: String = market.state.company[index]
|
|
var x := 12.0 + float(index) * spacing
|
|
var highlighted := recruit_id == focused_company_actor or recruit_id == focused_equipment_target
|
|
_add_ground_ring(Vector2(x + actor_size * 0.5, y + actor_size - 3), actor_size * 0.72, GOLD_LIGHT if highlighted else CYAN, "P910CompanyRing_%s" % recruit_id)
|
|
var actor := _add_actor(_recruit_rig(recruit_id), Vector2(x, y), actor_size, "P910CompanyActor_%s" % recruit_id, true)
|
|
actor.self_modulate = _recruit_color(recruit_id)
|
|
display_root.add_child(_actor_target("P910CompanyTarget_%s" % recruit_id, Vector2(x - 3, y - 4), Vector2(actor_size + 6, actor_size + 20), tap_company_actor.bind(recruit_id)))
|
|
var hp := _outlined_label("%d" % int(market.state.member_condition[recruit_id]), 8, WHITE)
|
|
hp.position = Vector2(x, y + actor_size - 5)
|
|
hp.size = Vector2(actor_size, 22)
|
|
hp.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
display_root.add_child(hp)
|
|
var instance_id := String(market.state.equipped_by_recruit.get(recruit_id, ""))
|
|
if not instance_id.is_empty():
|
|
var dot := _outlined_label("◆", 10, GOLD_LIGHT)
|
|
dot.position = Vector2(x + actor_size - 14, y + 2)
|
|
dot.size = Vector2(18, 18)
|
|
display_root.add_child(dot)
|
|
if market.state.current_market == 12 and not market.state.final_order_confirmed:
|
|
var reorder_text := _tr("确认换序", "CONFIRM ORDER") if not market.state.proposed_final_order.is_empty() else _tr("预览换序", "PREVIEW ORDER")
|
|
var reorder := _secondary_button("P910FinalReorderButton", reorder_text, commit_final_reorder)
|
|
reorder.position = Vector2(maxf(12, lane_width - 140), 164)
|
|
reorder.size = Vector2(132, 48)
|
|
display_root.add_child(reorder)
|
|
|
|
|
|
func _render_route_gates() -> void:
|
|
var routes: Array = market.state.available_battle_offer_ids
|
|
for index in routes.size():
|
|
var route_id := String(routes[index])
|
|
var row: Dictionary = content.routes[route_id]
|
|
var gate := ColorRect.new()
|
|
gate.name = "P910RouteGate_%s" % route_id
|
|
gate.position = Vector2(size.x - 214, 58 + float(index) * 82)
|
|
gate.size = Vector2(198, 70)
|
|
gate.color = _risk_color(int(row.risk), focused_route_id == route_id)
|
|
gate.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(gate)
|
|
var label := _outlined_label("%s ◆+%d\n%s" % [_tr(["稳行", "急行", "险行"][int(row.risk)], ["SURVEY", "PRESS", "HAZARD"][int(row.risk)]), int(row.reward), _tr(String(row.pressure_zh), String(row.pressure_en))], 9, WHITE)
|
|
label.position = gate.position + Vector2(8, 2)
|
|
label.size = gate.size - Vector2(16, 4)
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(label)
|
|
display_root.add_child(_actor_target("P910RouteTarget_%s" % route_id, gate.position, gate.size, tap_route.bind(route_id)))
|
|
|
|
|
|
func _render_market_context() -> void:
|
|
var ribbon := ColorRect.new()
|
|
ribbon.name = "P910ContextRibbon"
|
|
ribbon.position = Vector2(0, size.y - 58)
|
|
ribbon.size = Vector2(size.x, 58)
|
|
ribbon.color = Color(0.015, 0.035, 0.075, 0.94)
|
|
ribbon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(ribbon)
|
|
var context := _market_context_text()
|
|
var label := _outlined_label(context, 10, WHITE)
|
|
label.name = "P910ContextText"
|
|
label.position = Vector2(14, size.y - 55)
|
|
label.size = Vector2(size.x - 28, 52)
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(label)
|
|
|
|
|
|
func _market_context_text() -> String:
|
|
if market.state.recovery_required:
|
|
return _tr("队伍战败。支付 ◆%d 恢复后再选择同一集市路线。" % market.state.recovery_cost, "COMPANY DEFEATED. Pay ◆%d to recover, then choose again from the same Market." % market.state.recovery_cost)
|
|
if not focused_equipment_id.is_empty():
|
|
var row: Dictionary = content.equipment_offers[focused_equipment_id]
|
|
var target_text := _tr("再次点击一名队员确认替换。", "Tap a Recruit again to confirm replacement.") if not focused_equipment_target.is_empty() else _tr("点击一名队员查看替换前后。", "Tap a Recruit to preview the replacement.")
|
|
return "%s · ◆%d · %s / %s %s" % [_tr(String(row.name_zh), String(row.name_en)), int(row.list_price), _tr(String(row.benefit_zh), String(row.benefit_en)), _tr(String(row.cost_zh), String(row.cost_en)), target_text]
|
|
if not focused_hire_id.is_empty():
|
|
var row: Dictionary = content.recruit_offers[focused_hire_id]
|
|
return "%s · ◆%d · %s / %s" % [_tr(String(row.name_zh), String(row.name_en)), int(row.list_price), _tr(String(row.benefit_zh), String(row.benefit_en)), _tr(String(row.cost_zh), String(row.cost_en))]
|
|
if not focused_route_id.is_empty():
|
|
var preview: Dictionary = market.route_preview(focused_route_id)
|
|
return _tr("再次点击进入:%s。奖励 ◆%d。" % [String(preview.pressure_zh), int(preview.reward)], "Tap again to enter: %s. Reward ◆%d." % [String(preview.pressure_en), int(preview.reward)])
|
|
if not focused_company_actor.is_empty():
|
|
var record: Dictionary = market.state.recruit_records[focused_company_actor]
|
|
return "%s · %s · %d HP" % [_tr(String(record.get("name_zh", focused_company_actor)), String(record.get("name_en", focused_company_actor))), _profession_name(focused_company_actor), int(market.state.member_condition[focused_company_actor])]
|
|
return _tr("点击人物、装备或路线查看;再次点击执行。战斗会自动进行。", "Tap a Recruit, gear object or route to inspect; tap again to act. Battles run automatically.")
|
|
|
|
|
|
func _render_recovery_ground() -> void:
|
|
var glow := ColorRect.new()
|
|
glow.name = "P910RecoveryGround"
|
|
glow.position = Vector2(size.x * 0.30, size.y * 0.25)
|
|
glow.size = Vector2(size.x * 0.40, size.y * 0.42)
|
|
glow.color = Color(0.18, 0.05, 0.09, 0.72)
|
|
display_root.add_child(glow)
|
|
var text := _outlined_label(_tr("整备倒下的队伍\n保持当前集市与所有报价\n◆%d" % market.state.recovery_cost, "RECOVER THE COMPANY\nKeep this Market and every offer\n◆%d" % market.state.recovery_cost), 16, WHITE)
|
|
text.position = glow.position + Vector2(14, 4)
|
|
text.size = glow.size - Vector2(28, 64)
|
|
text.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
text.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(text)
|
|
var recover := _action_button("P910RecoveryButton", _tr("支付并恢复", "PAY AND RECOVER"), recover_company)
|
|
recover.position = Vector2(glow.position.x + glow.size.x * 0.5 - 92, glow.position.y + glow.size.y - 54)
|
|
recover.size = Vector2(184, 50)
|
|
recover.disabled = market.state.coin < market.state.recovery_cost
|
|
display_root.add_child(recover)
|
|
|
|
|
|
func _render_boss_muster() -> void:
|
|
_add_background(WINDGLASS_BACKGROUND)
|
|
_add_contract_header(_tr("风暴织机 · 集结", "GALE LOOM · MUSTER"), _tr("先看风向与弱轴,再选择计划", "Read the gust and weak spindle before choosing"))
|
|
_render_gust_path(false)
|
|
var boss := _add_actor("Riftblade", Vector2(size.x * 0.64, size.y * 0.22), minf(176.0, size.y * 0.46), "P910BossMusterActor", true)
|
|
boss.self_modulate = Color("#b5e8ef")
|
|
_add_boss_footprints(Vector2(size.x * 0.69, size.y * 0.69))
|
|
if market.state.recovery_required:
|
|
_render_boss_recovery()
|
|
return
|
|
var plans: Array = market.state.boss_muster.plans
|
|
for index in plans.size():
|
|
var row: Dictionary = plans[index]
|
|
var plan_id := String(row.id)
|
|
var target := ColorRect.new()
|
|
target.name = "P910BossPlanGround_%s" % plan_id
|
|
target.position = Vector2(18 + float(index) * 236, 72)
|
|
target.size = Vector2(220, 102)
|
|
target.color = Color(0.08, 0.16, 0.22, 0.90) if focused_boss_plan != plan_id else Color(0.34, 0.23, 0.08, 0.94)
|
|
target.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(target)
|
|
var label := _outlined_label("%s\n%s" % [_tr(String(row.name_zh), String(row.name_en)), _tr(String(row.summary_zh), String(row.summary_en))], 10, GOLD_LIGHT if focused_boss_plan == plan_id else WHITE)
|
|
label.position = target.position + Vector2(8, 3)
|
|
label.size = target.size - Vector2(16, 6)
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(label)
|
|
display_root.add_child(_actor_target("P910BossPlanTarget_%s" % plan_id, target.position, target.size, tap_boss_plan.bind(plan_id)))
|
|
var context := _outlined_label(_boss_muster_context(), 10, WHITE)
|
|
context.name = "P910BossMusterContext"
|
|
context.position = Vector2(18, size.y - 66)
|
|
context.size = Vector2(size.x - 36, 58)
|
|
context.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(context)
|
|
|
|
|
|
func _boss_muster_context() -> String:
|
|
if focused_boss_plan == "break_spindle":
|
|
return _tr("再次点击:投射命中弱轴后逆转下一阵风,并打开短核心窗口;队序会移动。", "Tap again: projectile hits reverse the next gust and open a short core window; order will move.")
|
|
if focused_boss_plan == "hold_line":
|
|
return _tr("再次点击:保持队序并持续攻击核心;后端承受更强风压。", "Tap again: preserve order and race the core; the rear absorbs stronger pressure.")
|
|
return _tr("风从前端进入;弱轴只响应投射。半血后两端同时进风。战败恢复 ◆16,不重掷。", "Gust enters from the front; only projectile hits reverse the spindle. Both ends activate at half HP. Recovery costs ◆16 with no reroll.")
|
|
|
|
|
|
func _render_boss_recovery() -> void:
|
|
var band := ColorRect.new()
|
|
band.name = "P910BossRecoveryGround"
|
|
band.position = Vector2(22, size.y * 0.45)
|
|
band.size = Vector2(size.x * 0.52, 112)
|
|
band.color = Color(0.22, 0.05, 0.09, 0.88)
|
|
display_root.add_child(band)
|
|
var label := _outlined_label(_tr("织机仍保持同一风向与阶段\n支付 ◆16 恢复战前存活队员;此前阵亡者不会复活", "THE LOOM KEEPS THE SAME GUST AND PHASE\nPay ◆16 to restore pre-Boss survivors; prior casualties stay down"), 12, WHITE)
|
|
label.position = band.position + Vector2(10, 4)
|
|
label.size = Vector2(band.size.x - 20, 60)
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(label)
|
|
var recover := _action_button("P910BossRecoveryButton", _tr("支付 ◆16 · 重新计划", "PAY ◆16 · REPLAN"), recover_company)
|
|
recover.position = Vector2(band.position.x + 10, band.position.y + 62)
|
|
recover.size = Vector2(214, 48)
|
|
recover.disabled = market.state.coin < 16
|
|
display_root.add_child(recover)
|
|
|
|
|
|
func _render_gust_path(in_battle: bool) -> void:
|
|
var count: int = boss_battle.state.member_ids.size() if in_battle else market.state.deployed_recruit_ids.size()
|
|
var direction: String = String(boss_battle.state.gust_direction) if in_battle else String(market.state.boss_muster.initial_gust_direction)
|
|
var start_x := 20.0
|
|
var width := minf(size.x * 0.56, 470.0)
|
|
var spacing := width / maxf(1.0, float(count))
|
|
for index in count:
|
|
var number := index + 1 if direction == "front_to_rear" else count - index
|
|
var label := _outlined_label(str(number), 10, CYAN)
|
|
label.name = "P910GustFootprint_%02d" % index
|
|
label.position = Vector2(start_x + spacing * index, 184 if not in_battle else 54)
|
|
label.size = Vector2(maxf(24.0, spacing - 2), 24)
|
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
display_root.add_child(label)
|
|
var ends := _outlined_label(_tr("前端 → 后端", "FRONT → REAR") if direction == "front_to_rear" else _tr("后端 → 前端", "REAR → FRONT"), 10, GOLD_LIGHT)
|
|
ends.name = "P910GustDirection"
|
|
ends.position = Vector2(start_x, 205 if not in_battle else 75)
|
|
ends.size = Vector2(width, 24)
|
|
ends.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
display_root.add_child(ends)
|
|
|
|
|
|
func _add_boss_footprints(center: Vector2) -> void:
|
|
for index in range(2):
|
|
var footprint := ColorRect.new()
|
|
footprint.name = "P910BossFootprint_%d" % (index + 1)
|
|
footprint.position = center + Vector2(float(index) * 64 - 58, 0)
|
|
footprint.size = Vector2(54, 5)
|
|
footprint.color = VIOLET if index == 0 else CYAN
|
|
footprint.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(footprint)
|
|
|
|
|
|
func _render_boss_battle() -> void:
|
|
_add_background(WINDGLASS_BACKGROUND)
|
|
_render_gust_path(true)
|
|
var boss_size := clampf(size.y * 0.40, 126.0, 166.0)
|
|
var boss := _add_actor("Riftblade", Vector2(size.x * 0.70, size.y * 0.27), boss_size, "P910BossBattleActor", false)
|
|
boss.self_modulate = Color("#b5e8ef")
|
|
battle_actors[P910Content.BOSS_ID] = boss
|
|
_add_boss_footprints(Vector2(size.x * 0.75, size.y * 0.72))
|
|
var member_size := clampf(minf(64.0, (size.x * 0.60 - 24.0) / maxf(1.0, float(boss_battle.state.member_ids.size()))), 48.0, 64.0)
|
|
for recruit_id in boss_battle.state.member_ids:
|
|
var actor := _add_actor(_recruit_rig(recruit_id), Vector2.ZERO, member_size, "P910BossMember_%s" % recruit_id, false)
|
|
actor.self_modulate = _recruit_color(recruit_id)
|
|
battle_actors[recruit_id] = actor
|
|
_add_boss_battle_hud()
|
|
_update_boss_visuals()
|
|
if battle_result_visible:
|
|
_render_boss_battle_result()
|
|
|
|
|
|
func _add_boss_battle_hud() -> void:
|
|
var title := _outlined_label(_tr("风暴织机", "GALE LOOM"), 14, GOLD_LIGHT)
|
|
title.position = Vector2(14, 4)
|
|
title.size = Vector2(160, 42)
|
|
display_root.add_child(title)
|
|
var hp := _outlined_label("%d / %d" % [boss_battle.state.boss_hp, boss_battle.state.boss_max_hp], 12, WHITE)
|
|
hp.name = "P910BossHPText"
|
|
hp.position = Vector2(172, 4)
|
|
hp.size = Vector2(140, 42)
|
|
display_root.add_child(hp)
|
|
var status := _outlined_label(_boss_status_text(), 10, CYAN)
|
|
status.name = "P910BossRuleStatus"
|
|
status.position = Vector2(310, 4)
|
|
status.size = Vector2(maxf(120, size.x - 438), 42)
|
|
status.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(status)
|
|
var pause := _secondary_button("P910BossPauseButton", "▶" if battle_paused else "Ⅱ", toggle_battle_pause)
|
|
pause.position = Vector2(size.x - 112, 4)
|
|
pause.size = Vector2(48, 48)
|
|
display_root.add_child(pause)
|
|
var speed := _secondary_button("P910BossSpeedButton", "%dx" % battle_speed, toggle_battle_speed)
|
|
speed.position = Vector2(size.x - 58, 4)
|
|
speed.size = Vector2(50, 48)
|
|
display_root.add_child(speed)
|
|
|
|
|
|
func _boss_status_text() -> String:
|
|
var phase := _tr("双端风", "OPPOSED GUSTS") if boss_battle.state.phase_two else _tr("单向风", "SINGLE GUST")
|
|
var spindle := _tr("弱轴就绪", "SPINDLE READY") if boss_battle.state.spindle_ready else _tr("弱轴重整", "SPINDLE REARMING")
|
|
return "%s · %s · %s" % [_boss_plan_name(), phase, spindle]
|
|
|
|
|
|
func _update_boss_visuals() -> void:
|
|
if not boss_battle_active:
|
|
return
|
|
var count: int = boss_battle.state.member_ids.size()
|
|
var lane_width := size.x * 0.60
|
|
var spacing := (lane_width - 72.0) / maxf(1.0, float(count - 1))
|
|
for recruit_id in boss_battle.state.member_ids:
|
|
var actor: TextureRect = battle_actors.get(recruit_id)
|
|
if actor == null:
|
|
continue
|
|
var position := int(boss_battle.state.member_positions[recruit_id])
|
|
actor.position = Vector2(18 + spacing * position, size.y * 0.52)
|
|
var hp := int(boss_battle.state.member_hp[recruit_id])
|
|
var action: Dictionary = battle_actions.get(recruit_id, {})
|
|
actor.texture = animation.texture(_recruit_rig(recruit_id), String(action.get("state", "idle")), world_clock)
|
|
actor.modulate.a = 0.25 if hp <= 0 else 1.0
|
|
var boss: TextureRect = battle_actors.get(P910Content.BOSS_ID)
|
|
if boss != null:
|
|
var action: Dictionary = battle_actions.get(P910Content.BOSS_ID, {})
|
|
boss.texture = animation.texture("Riftblade", String(action.get("state", "idle")), world_clock)
|
|
boss.modulate.a = 0.35 if boss_battle.state.boss_hp <= 0 else 1.0
|
|
var hp_label := find_child("P910BossHPText", true, false) as Label
|
|
if hp_label != null:
|
|
hp_label.text = "%d / %d" % [boss_battle.state.boss_hp, boss_battle.state.boss_max_hp]
|
|
var status := find_child("P910BossRuleStatus", true, false) as Label
|
|
if status != null:
|
|
status.text = _boss_status_text()
|
|
|
|
|
|
func _handle_boss_event(event: Dictionary) -> void:
|
|
var kind := String(event.get("kind", ""))
|
|
presented_event_counts[kind] = int(presented_event_counts.get(kind, 0)) + 1
|
|
if kind == "member_action":
|
|
var source := String(event.get("source_member_id", ""))
|
|
var healing := String(event.get("action_family", "")) == "restore"
|
|
var target := String(event.get("target_member_id", P910Content.BOSS_ID))
|
|
battle_actions[source] = {"state": "lantern_heal" if healing else "draw_release" if String(event.get("action_family", "")) == "projectile" else "basic_attack"}
|
|
_spawn_number(int(event.get("delta", 0)), target, JADE if healing else CORAL)
|
|
if String(event.get("action_family", "")) == "projectile":
|
|
_spawn_projectile(source, P910Content.BOSS_ID)
|
|
elif kind == "boss_action":
|
|
var target := String(event.get("target_member_id", ""))
|
|
if not target.is_empty():
|
|
_spawn_number(int(event.get("delta", 0)), target, CORAL)
|
|
_spawn_vfx(target, HIT_VFX)
|
|
battle_actions[P910Content.BOSS_ID] = {"state": "attack"}
|
|
elif kind == "boss_rule":
|
|
last_boss_rule = String(event.get("rule", ""))
|
|
last_boss_direction = String(event.get("new_gust_direction", event.get("direction", boss_battle.state.gust_direction)))
|
|
var word: String = String({
|
|
"weak_spindle_hit": _tr("逆风", "REVERSE"),
|
|
"gust_through_enemy_footprints": _tr("反穿", "BACKFLOW"),
|
|
"half_vitality_opposed_gusts": _tr("双端风", "OPPOSED"),
|
|
"gale_path_advanced": _tr("阵风", "GUST"),
|
|
"opposed_gusts_resolved": _tr("夹风", "CROSSFLOW"),
|
|
}.get(last_boss_rule, last_boss_rule.to_upper()))
|
|
_spawn_word(String(event.get("target_member_id", P910Content.BOSS_ID)), word, CYAN)
|
|
elif kind == "boss_plan":
|
|
var target := String(event.get("moved_member_id", event.get("target_member_id", P910Content.BOSS_ID)))
|
|
_spawn_word(target, _tr("换序", "SHIFT") if String(event.get("plan", "")) == "break_spindle" else _tr("固线", "HOLD"), GOLD_LIGHT)
|
|
|
|
|
|
func _render_boss_battle_result() -> void:
|
|
var band := ColorRect.new()
|
|
band.name = "P910BossResultBand"
|
|
band.position = Vector2(0, size.y - 72)
|
|
band.size = Vector2(size.x, 72)
|
|
band.color = Color(0.015, 0.035, 0.075, 0.95)
|
|
display_root.add_child(band)
|
|
var won: bool = boss_battle.state.outcome == "victory"
|
|
var text := _outlined_label(_tr("织机停止 · 写入第二契约", "LOOM HALTED · RECORD CONTRACT 2") if won else _tr("队伍战败 · 返回同一集结", "COMPANY DEFEATED · RETURN TO THE SAME MUSTER"), 17, GOLD_LIGHT if won else CORAL)
|
|
text.position = Vector2(16, size.y - 68)
|
|
text.size = Vector2(size.x * 0.68, 60)
|
|
display_root.add_child(text)
|
|
var settle := _action_button("P910BossSettleButton", _tr("确认战果", "CONFIRM RESULT"), settle_and_return)
|
|
settle.position = Vector2(size.x - 212, size.y - 64)
|
|
settle.size = Vector2(194, 52)
|
|
display_root.add_child(settle)
|
|
|
|
|
|
func _handle_battle_event(event: Dictionary) -> void:
|
|
super._handle_battle_event(event)
|
|
if String(event.get("kind", "")) == "enemy_rule" and String(event.get("rule", "")) in ["route_gust", "opposed_route_gust"]:
|
|
var target := String(event.get("target_member_id", ""))
|
|
if target.is_empty() and not Array(event.get("target_member_ids", [])).is_empty():
|
|
target = String(event.target_member_ids[0])
|
|
_spawn_word(target, _tr("阵风", "GUST"), CYAN)
|
|
|
|
|
|
func _render_contract_result() -> void:
|
|
_add_background(WINDGLASS_BACKGROUND)
|
|
_add_contract_header(_tr("第二契约完成", "SECOND CONTRACT COMPLETE"), _tr("永久结算只执行一次", "Persistent settlement applies exactly once"))
|
|
var loom := _add_actor("Riftblade", Vector2(size.x * 0.42, size.y * 0.22), minf(160.0, size.y * 0.40), "P910StoppedLoom", true, "death")
|
|
loom.self_modulate = Color("#9ac4cc")
|
|
var summary := _outlined_label(_tr("风暴织机停止\n完成印记 CLEAR-RG01-C02\n+18 声望 · Company Record · 开启 RG-02", "GALE LOOM HALTED\nCLEAR-RG01-C02\n+18 RENOWN · COMPANY RECORD · RG-02 UNLOCK"), 16, WHITE)
|
|
summary.name = "P910ContractResultSummary"
|
|
summary.position = Vector2(size.x * 0.12, size.y * 0.22)
|
|
summary.size = Vector2(size.x * 0.42, size.y * 0.48)
|
|
summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(summary)
|
|
var close := _action_button("P910CloseContractButton", _tr("写入公会记录", "WRITE GUILD RECORD"), close_contract)
|
|
close.position = Vector2(size.x * 0.62, size.y * 0.56)
|
|
close.size = Vector2(minf(248, size.x * 0.30), 54)
|
|
display_root.add_child(close)
|
|
|
|
|
|
func _add_contract_header(title_text: String, subtitle_text: String) -> void:
|
|
var shade := ColorRect.new()
|
|
shade.name = "P910ContractHeader"
|
|
shade.position = Vector2.ZERO
|
|
shade.size = Vector2(size.x, 56)
|
|
shade.color = Color(0.015, 0.035, 0.075, 0.88)
|
|
shade.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
display_root.add_child(shade)
|
|
var title := _outlined_label(title_text, 16, GOLD_LIGHT)
|
|
title.position = Vector2(14, 2)
|
|
title.size = Vector2(size.x * 0.56, 50)
|
|
title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
display_root.add_child(title)
|
|
var subtitle := _outlined_label(subtitle_text, 10, WHITE)
|
|
subtitle.position = Vector2(size.x * 0.48, 2)
|
|
subtitle.size = Vector2(size.x * 0.50 - 14, 50)
|
|
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
subtitle.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(subtitle)
|
|
|
|
|
|
func _render_persistent_result() -> void:
|
|
_add_background(HALL_BACKGROUND)
|
|
_add_contract_header(_tr("风晶边境 · 两份契约完成", "WINDGLASS VERGE · TWO CONTRACTS CLEARED"), _tr("下一地区印记已点亮", "The next Region seal is now lit"))
|
|
var seal_width := (size.x - 36.0) / 8.0
|
|
for index in range(8):
|
|
var seal_id := "RG-%02d" % (index + 1)
|
|
var unlocked: bool = seal_id in market.state.unlocked_region_ids
|
|
var seal := _outlined_label("◆\n%s" % seal_id, 9, GOLD_LIGHT if unlocked else Color(0.42, 0.50, 0.58))
|
|
seal.name = "P910RegionSeal_%s" % seal_id
|
|
seal.position = Vector2(18 + seal_width * index, 56)
|
|
seal.size = Vector2(seal_width, 50)
|
|
seal.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
display_root.add_child(seal)
|
|
var record: Dictionary = market.state.company_records[-1]
|
|
var rows: Array = record.company
|
|
var actor_size := clampf(minf(68.0, (size.x * 0.60) / maxf(1.0, float(rows.size()))), 46.0, 68.0)
|
|
var spacing := minf(actor_size + 3.0, (size.x * 0.60 - actor_size) / maxf(1.0, float(rows.size() - 1)))
|
|
for index in rows.size():
|
|
var row: Dictionary = rows[index]
|
|
var profession: Dictionary = content.professions[String(row.profession_id)]
|
|
var actor := _add_actor(String(profession.visual_rig), Vector2(18 + spacing * index, size.y * 0.48), actor_size, "P910RecordActor_%s" % String(row.recruit_id), true, "victory")
|
|
actor.self_modulate = _p910_profession_color(String(row.profession_id))
|
|
var summary := _outlined_label(_tr("声望 %d\n完成印记 CLEAR-RG01-C02\nCompany Record %d 名 / 12 回合\nRG-02 已开启" % [market.state.persistent_renown, rows.size()], "RENOWN %d\nCLEAR-RG01-C02\nCOMPANY RECORD %d / 12 ROUNDS\nRG-02 UNLOCKED" % [market.state.persistent_renown, rows.size()]), 15, WHITE)
|
|
summary.name = "P910PersistentSummary"
|
|
summary.position = Vector2(size.x * 0.64, size.y * 0.30)
|
|
summary.size = Vector2(size.x * 0.33, size.y * 0.50)
|
|
summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
display_root.add_child(summary)
|
|
|
|
|
|
func _boss_plan_name() -> String:
|
|
var plan: String = String(boss_battle.state.plan) if boss_battle_active else String(market.state.selected_boss_plan)
|
|
return _tr("破轴逆风", "BREAK SPINDLE") if plan == "break_spindle" else _tr("固线攻核", "HOLD LINE")
|
|
|
|
|
|
func _p910_profession_color(profession_id: String) -> Color:
|
|
return {
|
|
"PF-B01": GOLD_LIGHT,
|
|
"PF-B02": CORAL,
|
|
"PF-B03": JADE,
|
|
"PF-B04": CYAN,
|
|
"PF-B05": GOLD,
|
|
"PF-B06": VIOLET,
|
|
"PF-B07": JADE,
|
|
"PF-B08": Color("#ef9cbc"),
|
|
"PF-B09": Color("#78d9d0"),
|
|
"PF-B11": GOLD_LIGHT,
|
|
}.get(profession_id, WHITE)
|
|
|
|
|
|
func _battle_actor_name(actor_id: String) -> String:
|
|
if content.enemies.has(actor_id):
|
|
return _tr(String(content.enemies[actor_id].name_zh), String(content.enemies[actor_id].name_en))
|
|
var profession_id := String(_recruit_record(actor_id).get("profession_id", ""))
|
|
return _tr({
|
|
"PF-B01": "守卫",
|
|
"PF-B02": "长弦",
|
|
"PF-B03": "术士",
|
|
"PF-B04": "游侠",
|
|
"PF-B05": "弓手",
|
|
"PF-B06": "咒师",
|
|
"PF-B07": "治疗",
|
|
"PF-B08": "铸师",
|
|
"PF-B09": "潮语",
|
|
"PF-B11": "风匠",
|
|
}.get(profession_id, profession_id), {
|
|
"PF-B01": "GUARD",
|
|
"PF-B02": "BOW",
|
|
"PF-B03": "MAGE",
|
|
"PF-B04": "SCOUT",
|
|
"PF-B05": "ARCHER",
|
|
"PF-B06": "HEXER",
|
|
"PF-B07": "HEAL",
|
|
"PF-B08": "SMITH",
|
|
"PF-B09": "TIDE",
|
|
"PF-B11": "GALE",
|
|
}.get(profession_id, profession_id))
|