486 lines
22 KiB
GDScript
486 lines
22 KiB
GDScript
class_name P98RuntimeController
|
||
extends "res://p9_7/runtime_controller.gd"
|
||
|
||
const P98Content = preload("res://content/p9_8_content.gd")
|
||
const P98BattleService = preload("res://p9_8/battle_service.gd")
|
||
const P98BossBattleService = preload("res://p9_8/boss_battle_service.gd")
|
||
const P98BossService = preload("res://p9_8/boss_service.gd")
|
||
const P98ContractState = preload("res://p9_8/contract_state.gd")
|
||
const P98SaveService = preload("res://p9_8/save_service.gd")
|
||
|
||
const PANE_COLORS := {
|
||
"projectile": Color("#73e3e6"),
|
||
"spell": Color("#b29aff"),
|
||
"forced_movement": Color("#ffb15e"),
|
||
}
|
||
|
||
var boss_battle = P98BossBattleService.new()
|
||
var boss_mode := false
|
||
var boss_actor: TextureRect
|
||
var boss_hp_fill: ColorRect
|
||
var boss_pane_label: Label
|
||
|
||
|
||
func _bootstrap_market() -> void:
|
||
if entry_snapshot.is_empty():
|
||
last_code = "entry_snapshot_required"
|
||
_queue_render()
|
||
return
|
||
_install_p98_content()
|
||
market = P98BossService.new()
|
||
var restored := false
|
||
if bool(entry_snapshot.get("p98_activated", false)):
|
||
restored = market.state.restore_snapshot(entry_snapshot)
|
||
else:
|
||
restored = market.state.initialize_from_p97(entry_snapshot)
|
||
if not restored:
|
||
market = null
|
||
last_code = "p98_snapshot_invalid"
|
||
_queue_render()
|
||
return
|
||
battle = P98BattleService.new()
|
||
saves = P98SaveService.new()
|
||
pending_line_ids.assign(market.state.deployed_recruit_ids)
|
||
selected_inventory_instance = ""
|
||
focused_company_id = ""
|
||
last_code = "p98_contract_ready"
|
||
battle_active = false
|
||
battle_result_visible = false
|
||
boss_mode = false
|
||
_save_stable()
|
||
_queue_render()
|
||
|
||
|
||
func _install_p98_content() -> void:
|
||
content = P98Content.snapshot()
|
||
var view_professions: Dictionary = content.base_professions.duplicate(true)
|
||
for promotion_id in content.promotions:
|
||
var promotion: Dictionary = Dictionary(content.promotions[promotion_id]).duplicate(true)
|
||
var base: Dictionary = Dictionary(content.base_professions[promotion.parent_id]).duplicate(true)
|
||
base.merge(promotion, true)
|
||
view_professions[promotion_id] = base
|
||
content["professions"] = view_professions
|
||
|
||
|
||
func choose_artifact(artifact_id: String) -> void:
|
||
if market == null:
|
||
return
|
||
_accept_market_result(market.choose_artifact(_operation("artifact", artifact_id), artifact_id), true)
|
||
|
||
|
||
func choose_promotion(promotion_id: String) -> void:
|
||
if market == null or market.state.promotion_draft.is_empty():
|
||
return
|
||
var recruit_id := String(market.state.promotion_draft.recruit_id)
|
||
_accept_market_result(market.confirm_promotion(_operation("promotion", promotion_id), recruit_id, promotion_id), true)
|
||
|
||
|
||
func choose_boss_plan(plan: String) -> void:
|
||
if market == null:
|
||
return
|
||
var committed: Dictionary = market.commit_boss_plan(_operation("boss-plan", plan), plan)
|
||
last_code = String(committed.code)
|
||
if not committed.ok:
|
||
_queue_render()
|
||
return
|
||
_save_stable()
|
||
boss_battle = P98BossBattleService.new()
|
||
var started: Dictionary = boss_battle.start_from_contract(market.state)
|
||
last_code = String(started.code)
|
||
if not started.ok:
|
||
_queue_render()
|
||
return
|
||
boss_mode = 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()
|
||
_queue_render()
|
||
|
||
|
||
func prepare_full_boss_line() -> void:
|
||
if market == null or int(market.state.contract_phase) != P98ContractState.ContractPhase.BOSS_MUSTER or market.state.recovery_required:
|
||
return
|
||
var candidates: Array[String] = []
|
||
for recruit_id in market.state.company.slice(0, mini(market.state.line_capacity, market.state.company.size())):
|
||
if int(market.state.member_condition.get(recruit_id, 0)) > 0:
|
||
candidates.append(String(recruit_id))
|
||
var ordered: Array[String] = market.auto_order_candidate(candidates)
|
||
if ordered.is_empty():
|
||
last_code = "boss_line_unavailable"
|
||
_queue_render()
|
||
return
|
||
pending_line_ids.assign(ordered)
|
||
_accept_market_result(market.commit_deployment(_operation("boss-deploy", "full-line"), ordered), true)
|
||
|
||
|
||
func advance_battle_ticks(ticks: int) -> Dictionary:
|
||
if not boss_mode:
|
||
return super.advance_battle_ticks(ticks)
|
||
if not battle_active or battle_result_visible:
|
||
return {"ok": false, "code": "boss_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 boss_mode:
|
||
super.settle_and_return()
|
||
return
|
||
if not battle_active or not boss_battle.state.terminal:
|
||
return
|
||
var result: Dictionary = market.settle_boss(_operation("boss-settle", boss_battle.state.plan), boss_battle.state.snapshot())
|
||
last_code = String(result.code)
|
||
if not result.ok:
|
||
_queue_render()
|
||
return
|
||
battle_active = false
|
||
battle_result_visible = false
|
||
boss_mode = false
|
||
pending_line_ids.assign(market.state.deployed_recruit_ids)
|
||
_save_stable()
|
||
_queue_render()
|
||
|
||
|
||
func close_contract() -> void:
|
||
if market == null:
|
||
return
|
||
var result: Dictionary = market.close_contract(_operation("contract-close", "windglass"))
|
||
last_code = String(result.code)
|
||
if result.ok:
|
||
pending_line_ids.clear()
|
||
focused_company_id = ""
|
||
_save_stable()
|
||
_queue_render()
|
||
|
||
|
||
func _operation(kind: String, target: String) -> String:
|
||
operation_index += 1
|
||
return "p98-ui-%s-%d-%d-%s" % [kind, market.state.revision, operation_index, target]
|
||
|
||
|
||
func _render_market() -> void:
|
||
match int(market.state.contract_phase):
|
||
P98ContractState.ContractPhase.BOSS_MUSTER:
|
||
_render_boss_muster()
|
||
P98ContractState.ContractPhase.CONTRACT_RESULT:
|
||
_render_contract_result()
|
||
P98ContractState.ContractPhase.RUN_CLOSED:
|
||
_render_persistent_result()
|
||
_:
|
||
if market.state.selected_artifact_id.is_empty():
|
||
_render_artifact_shrine()
|
||
elif not market.state.promotion_draft.is_empty():
|
||
_render_promotion_anvil()
|
||
else:
|
||
super._render_market()
|
||
|
||
|
||
func _render_artifact_shrine() -> void:
|
||
_add_background(HALL_BACKGROUND)
|
||
_add_contract_header(_tr("风晶契约 · 选择共享规则", "WINDGLASS CONTRACT · CHOOSE A SHARED RULE"), _tr("从下一座集市起永久生效", "Permanent from the next Market"))
|
||
var draft: Dictionary = market.state.artifact_draft
|
||
var offers: Array = draft.get("offer_ids", [])
|
||
for index in range(offers.size()):
|
||
var artifact_id := String(offers[index])
|
||
var row: Dictionary = content.artifacts[artifact_id]
|
||
var center_x := size.x * (0.34 if index == 0 else 0.66)
|
||
_add_ground_ring(Vector2(center_x, size.y * 0.62), 150, CYAN if index == 0 else GOLD, "P98ArtifactRing_%s" % artifact_id)
|
||
var glyph := _outlined_label("◇" if index == 0 else "◆", 58, CYAN if index == 0 else GOLD_LIGHT)
|
||
glyph.position = Vector2(center_x - 48, size.y * 0.25)
|
||
glyph.size = Vector2(96, 112)
|
||
glyph.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
display_root.add_child(glyph)
|
||
var name_label := _outlined_label(_tr(String(row.name_zh), String(row.name_en)), 19, WHITE)
|
||
name_label.position = Vector2(center_x - 160, size.y * 0.52)
|
||
name_label.size = Vector2(320, 42)
|
||
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
display_root.add_child(name_label)
|
||
var consequence := _outlined_label(_tr("获得:%s\n代价:%s" % [row.benefit_zh, row.cost_zh], "GAIN: %s\nCOST: %s" % [row.benefit_en, row.cost_en]), 11, WHITE)
|
||
consequence.position = Vector2(center_x - 180, size.y * 0.62)
|
||
consequence.size = Vector2(360, 66)
|
||
consequence.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
consequence.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(consequence)
|
||
var choose := _action_button("P98ArtifactButton_%s" % artifact_id, _tr("立下规则", "BIND RULE"), choose_artifact.bind(artifact_id))
|
||
choose.position = Vector2(center_x - 86, size.y - 66)
|
||
choose.size = Vector2(172, 50)
|
||
display_root.add_child(choose)
|
||
|
||
|
||
func _render_promotion_anvil() -> void:
|
||
_add_background(HALL_BACKGROUND)
|
||
var draft: Dictionary = market.state.promotion_draft
|
||
var recruit_id := String(draft.recruit_id)
|
||
_add_contract_header(_tr("晋阶铁砧 · %s" % _recruit_name(recruit_id), "PROMOTION ANVIL · %s" % _recruit_name(recruit_id)), _tr("保留等级、生命、装备与队列位置", "Keeps level, health, equipment and line position"))
|
||
var actor := _add_actor(_recruit_rig(recruit_id), Vector2(size.x * 0.5 - 58, size.y * 0.20), 116, "P98PromotionActor", true)
|
||
actor.self_modulate = _recruit_color(recruit_id)
|
||
for index in range(Array(draft.offer_ids).size()):
|
||
var promotion_id := String(draft.offer_ids[index])
|
||
var row: Dictionary = content.promotions[promotion_id]
|
||
var x := size.x * (0.27 if index == 0 else 0.73)
|
||
var title := _outlined_label(_tr(String(row.name_zh), String(row.name_en)), 19, GOLD_LIGHT)
|
||
title.position = Vector2(x - 175, size.y * 0.51)
|
||
title.size = Vector2(350, 40)
|
||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
display_root.add_child(title)
|
||
var choice := _outlined_label(_tr(String(row.choice_zh), String(row.choice_en)), 11, WHITE)
|
||
choice.position = Vector2(x - 180, size.y * 0.61)
|
||
choice.size = Vector2(360, 54)
|
||
choice.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
choice.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(choice)
|
||
var button := _action_button("P98PromotionButton_%s" % promotion_id, _tr("选择晋阶", "PROMOTE"), choose_promotion.bind(promotion_id))
|
||
button.position = Vector2(x - 88, size.y - 66)
|
||
button.size = Vector2(176, 50)
|
||
display_root.add_child(button)
|
||
|
||
|
||
func _render_boss_muster() -> void:
|
||
_add_background(WINDGLASS_BACKGROUND)
|
||
_add_contract_header(_tr("Boss 集结 · 多棱镜鹿", "BOSS MUSTER · THE MANY-PANED STAG"), _tr("投射屏障起始 · 半血后每次转两棱 · 鹿角横扫最前列", "Projectile pane first · two-step turns below half · sweep hits the foremost"))
|
||
_add_ground_ring(Vector2(size.x * 0.68, size.y * 0.71), 190, VIOLET, "P98BossFootprintRear")
|
||
_add_ground_ring(Vector2(size.x * 0.77, size.y * 0.71), 190, CYAN, "P98BossFootprintFront")
|
||
var stag := _add_actor("Riftblade", Vector2(size.x * 0.66, size.y * 0.30), minf(190, size.y * 0.48), "P98BossMusterActor", true, "idle")
|
||
stag.self_modulate = Color("#d9c6ff")
|
||
var company_text := _outlined_label(_tr("战线 %d/%d · ◆%d · 当前 Artifact:%s" % [market.state.deployed_recruit_ids.size(), market.state.line_capacity, market.state.coin, _artifact_name()], "LINE %d/%d · ◆%d · ARTIFACT: %s" % [market.state.deployed_recruit_ids.size(), market.state.line_capacity, market.state.coin, _artifact_name()]), 12, WHITE)
|
||
company_text.position = Vector2(18, size.y * 0.27)
|
||
company_text.size = Vector2(size.x * 0.48, 44)
|
||
display_root.add_child(company_text)
|
||
if market.state.recovery_required:
|
||
var recovery := _outlined_label(_tr("队伍溃败:支付 ◆%d,回到同一只鹿、同一棱面与同一阵容。" % market.state.recovery_cost, "DEFEAT: pay ◆%d and return to the same Stag, pane and Company." % market.state.recovery_cost), 13, CORAL)
|
||
recovery.position = Vector2(18, size.y * 0.46)
|
||
recovery.size = Vector2(size.x * 0.48, 62)
|
||
recovery.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(recovery)
|
||
var recover := _action_button("P98BossRecoveryButton", _tr("整队恢复 ◆%d" % market.state.recovery_cost, "RECOVER ◆%d" % market.state.recovery_cost), recover_company)
|
||
recover.position = Vector2(18, size.y - 68)
|
||
recover.size = Vector2(220, 52)
|
||
display_root.add_child(recover)
|
||
return
|
||
var rally := _secondary_button("P98BossRallyButton", _tr("最终整编 %d/%d" % [market.state.deployed_recruit_ids.size(), market.state.line_capacity], "FINAL RALLY %d/%d" % [market.state.deployed_recruit_ids.size(), market.state.line_capacity]), prepare_full_boss_line)
|
||
rally.position = Vector2(18, size.y * 0.38)
|
||
rally.size = Vector2(224, 52)
|
||
display_root.add_child(rally)
|
||
var plans: Array = market.state.boss_muster.get("plans", [])
|
||
for index in range(plans.size()):
|
||
var row: Dictionary = plans[index]
|
||
var x := 18.0 + float(index) * 240.0
|
||
var summary := _outlined_label(_tr(String(row.summary_zh), String(row.summary_en)), 10, WHITE)
|
||
summary.position = Vector2(x, size.y * 0.52)
|
||
summary.size = Vector2(224, 80)
|
||
summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(summary)
|
||
var button := _action_button("P98BossPlanButton_%s" % String(row.id), _tr(String(row.name_zh), String(row.name_en)), choose_boss_plan.bind(String(row.id)))
|
||
button.position = Vector2(x, size.y - 68)
|
||
button.size = Vector2(224, 52)
|
||
display_root.add_child(button)
|
||
|
||
|
||
func _render_battle() -> void:
|
||
if not boss_mode:
|
||
super._render_battle()
|
||
for node_name in ["P97PauseButton", "P97SpeedButton"]:
|
||
var control := find_child(node_name, true, false) as Control
|
||
if control != null:
|
||
control.position.y = 4
|
||
control.size.y = 48
|
||
return
|
||
_render_boss_battle()
|
||
|
||
|
||
func _render_boss_battle() -> void:
|
||
_add_background(WINDGLASS_BACKGROUND)
|
||
var title := _outlined_label(_tr("多棱镜鹿 · %s" % _boss_plan_name(), "THE MANY-PANED STAG · %s" % _boss_plan_name()), 15, WHITE)
|
||
title.position = Vector2(16, 6)
|
||
title.size = Vector2(size.x * 0.52, 42)
|
||
display_root.add_child(title)
|
||
boss_pane_label = _outlined_label("", 12, PANE_COLORS.get(boss_battle.state.pane, CYAN))
|
||
boss_pane_label.position = Vector2(size.x * 0.48, 6)
|
||
boss_pane_label.size = Vector2(size.x * 0.25, 42)
|
||
boss_pane_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
display_root.add_child(boss_pane_label)
|
||
var pause := _secondary_button("P98BossPauseButton", "▶" 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("P98BossSpeedButton", "%dx" % battle_speed, toggle_battle_speed)
|
||
speed.position = Vector2(size.x - 58, 4)
|
||
speed.size = Vector2(50, 48)
|
||
display_root.add_child(speed)
|
||
for recruit_id in boss_battle.state.member_ids:
|
||
var actor := _add_actor(_recruit_rig(recruit_id), Vector2.ZERO, clampf(size.y * 0.25, 92, 132), "P98BossBattleActor_%s" % recruit_id, false)
|
||
actor.self_modulate = _recruit_color(recruit_id)
|
||
battle_actors[recruit_id] = actor
|
||
boss_actor = _add_actor("Riftblade", Vector2.ZERO, clampf(size.y * 0.46, 160, 220), "P98BossBattleActor_BO-R1-01", false)
|
||
boss_actor.self_modulate = Color("#d9c6ff")
|
||
battle_actors["BO-R1-01"] = boss_actor
|
||
var hp_back := ColorRect.new()
|
||
hp_back.name = "P98BossVitality"
|
||
hp_back.position = Vector2(size.x * 0.58, 56)
|
||
hp_back.size = Vector2(size.x * 0.34, 14)
|
||
hp_back.color = Color(0.02, 0.04, 0.07, 0.94)
|
||
display_root.add_child(hp_back)
|
||
boss_hp_fill = ColorRect.new()
|
||
boss_hp_fill.position = Vector2(2, 2)
|
||
boss_hp_fill.size = Vector2(hp_back.size.x - 4, 10)
|
||
boss_hp_fill.color = VIOLET
|
||
hp_back.add_child(boss_hp_fill)
|
||
_update_boss_visuals()
|
||
if battle_result_visible:
|
||
_render_boss_battle_result()
|
||
|
||
|
||
func _update_boss_visuals() -> void:
|
||
if not boss_mode or boss_actor == null:
|
||
return
|
||
for recruit_id in boss_battle.state.member_ids:
|
||
var actor: TextureRect = battle_actors.get(recruit_id)
|
||
if actor == null:
|
||
continue
|
||
var hp := int(boss_battle.state.member_hp[recruit_id])
|
||
var x := _battle_x(int(boss_battle.state.member_positions[recruit_id]))
|
||
actor.position = Vector2(x - actor.size.x * 0.5, size.y * 0.51)
|
||
actor.texture = animation.texture(_recruit_rig(recruit_id), "idle" if boss_battle.state.terminal else "advance", world_clock)
|
||
actor.modulate.a = 0.25 if hp <= 0 else 1.0
|
||
actor.visible = hp > 0 or not boss_battle.state.terminal
|
||
boss_actor.position = Vector2(_battle_x(100) - boss_actor.size.x * 0.62, size.y * 0.34)
|
||
boss_actor.texture = animation.texture("Riftblade", "death" if boss_battle.state.boss_hp <= 0 else "attack" if int(boss_battle.state.tick) % 12 <= 2 else "idle", world_clock)
|
||
if boss_hp_fill != null:
|
||
boss_hp_fill.size.x = maxf(0, (size.x * 0.34 - 4) * float(boss_battle.state.boss_hp) / maxf(1, boss_battle.state.boss_max_hp))
|
||
if boss_pane_label != null:
|
||
boss_pane_label.text = _tr("当前屏障:%s%s" % [_pane_name(boss_battle.state.pane), " · 双步旋转" if boss_battle.state.phase_two else ""], "PANE: %s%s" % [_pane_name(boss_battle.state.pane), " · TWO-STEP" if boss_battle.state.phase_two else ""])
|
||
boss_pane_label.add_theme_color_override("font_color", PANE_COLORS.get(boss_battle.state.pane, CYAN))
|
||
|
||
|
||
func _update_battle_control_labels() -> void:
|
||
if not boss_mode:
|
||
super._update_battle_control_labels()
|
||
return
|
||
var pause := find_child("P98BossPauseButton", true, false) as Button
|
||
var speed := find_child("P98BossSpeedButton", true, false) as Button
|
||
if pause != null:
|
||
pause.text = "▶" if battle_paused else "Ⅱ"
|
||
if speed != null:
|
||
speed.text = "%dx" % battle_speed
|
||
|
||
|
||
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 target := String(event.get("target_member_id", "BO-R1-01"))
|
||
var delta := int(event.get("delta", 0))
|
||
_spawn_number(delta, target, JADE if delta > 0 else CORAL)
|
||
if target == "BO-R1-01":
|
||
_spawn_projectile(source, target)
|
||
elif kind == "boss_action":
|
||
var target := String(event.get("target_member_id", ""))
|
||
_spawn_number(int(event.get("delta", 0)), target, CORAL)
|
||
_spawn_vfx(target, HIT_VFX)
|
||
elif kind == "boss_rule":
|
||
_spawn_word("BO-R1-01", _tr("转棱", "PANE TURN") if String(event.get("rule", "")) == "pane_rotated" else _tr("屏障", "SCREENED"), PANE_COLORS.get(String(event.get("to_family", event.get("pane", "projectile"))), CYAN))
|
||
elif kind == "boss_plan":
|
||
_spawn_word("BO-R1-01", _tr("固阵", "BRACE") if String(event.get("plan", "")) == "brace" else _tr("破面", "ROTATE"), GOLD_LIGHT)
|
||
|
||
|
||
func _render_boss_battle_result() -> void:
|
||
var band := ColorRect.new()
|
||
band.name = "P98BossResultBand"
|
||
band.position = Vector2(0, size.y - 76)
|
||
band.size = Vector2(size.x, 76)
|
||
band.color = Color(0.015, 0.035, 0.075, 0.95)
|
||
display_root.add_child(band)
|
||
var won: bool = String(boss_battle.state.outcome) == "victory"
|
||
var result := _outlined_label(_tr("契约首领击破 · 结算永久记录", "CONTRACT BOSS DEFEATED · RECORD THE CLEAR") if won else _tr("战线溃败 · 可返回同一集结点恢复", "COMPANY DEFEATED · RECOVER AT THE SAME MUSTER"), 18, GOLD_LIGHT if won else CORAL)
|
||
result.position = Vector2(18, size.y - 70)
|
||
result.size = Vector2(size.x * 0.67, 58)
|
||
display_root.add_child(result)
|
||
var button := _action_button("P98BossSettleButton", _tr("确认战果", "CONFIRM RESULT"), settle_and_return)
|
||
button.position = Vector2(size.x - 212, size.y - 66)
|
||
button.size = Vector2(194, 52)
|
||
display_root.add_child(button)
|
||
|
||
|
||
func _render_contract_result() -> void:
|
||
_add_background(WINDGLASS_BACKGROUND)
|
||
_add_contract_header(_tr("风晶契约已完成", "WINDGLASS CONTRACT COMPLETE"), _tr("永久结算只执行一次", "The persistent settlement applies exactly once"))
|
||
var result: Dictionary = market.state.pending_boss_result
|
||
var summary := _outlined_label(_tr("多棱镜鹿已击破\n计划:%s\n永久获得:3 声望 · Company Record · Boss/Artifact/晋阶图鉴" % _boss_plan_name(), "THE MANY-PANED STAG DEFEATED\nPLAN: %s\nPERSIST: 3 RENOWN · COMPANY RECORD · BOSS/ARTIFACT/PROMOTION DISCOVERIES" % _boss_plan_name()), 17, WHITE)
|
||
summary.position = Vector2(size.x * 0.16, size.y * 0.27)
|
||
summary.size = Vector2(size.x * 0.68, size.y * 0.42)
|
||
summary.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
summary.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||
summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(summary)
|
||
var close := _action_button("P98CloseContractButton", _tr("写入公会编年史", "WRITE THE GUILD RECORD"), close_contract)
|
||
close.position = Vector2(size.x * 0.5 - 132, size.y - 72)
|
||
close.size = Vector2(264, 54)
|
||
display_root.add_child(close)
|
||
|
||
|
||
func _render_persistent_result() -> void:
|
||
_add_background(HALL_BACKGROUND)
|
||
_add_contract_header(_tr("风晶契约 · 永久完成", "WINDGLASS CONTRACT · PERMANENT CLEAR"), _tr("下一份契约方向已开启", "The next Contract direction is open"))
|
||
var record: Dictionary = market.state.company_records[-1] if not market.state.company_records.is_empty() else {}
|
||
var company_size := Array(record.get("company", [])).size()
|
||
var summary := _outlined_label(_tr("声望 %d\n完成印记 CLEAR-RG01-C01\nCompany Record %d 名成员 / 12 回合\n已发现 %d 项\n\n本轮金币、队伍、装备与市场已结清;永久记录保留。" % [market.state.persistent_renown, company_size, market.state.discovery_ids.size()], "RENOWN %d\nCLEAR MARK CLEAR-RG01-C01\nCOMPANY RECORD %d MEMBERS / 12 ROUNDS\nDISCOVERIES %d\n\nRun Coin, Company, gear and Market are closed; the permanent record remains." % [market.state.persistent_renown, company_size, market.state.discovery_ids.size()]), 16, WHITE)
|
||
summary.position = Vector2(size.x * 0.17, size.y * 0.24)
|
||
summary.size = Vector2(size.x * 0.66, size.y * 0.55)
|
||
summary.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
summary.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||
summary.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(summary)
|
||
|
||
|
||
func _add_contract_header(title_text: String, subtitle_text: String) -> void:
|
||
var shade := ColorRect.new()
|
||
shade.position = Vector2.ZERO
|
||
shade.size = Vector2(size.x, 58)
|
||
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, 17, GOLD_LIGHT)
|
||
title.position = Vector2(16, 2)
|
||
title.size = Vector2(size.x * 0.58, 52)
|
||
display_root.add_child(title)
|
||
var subtitle := _outlined_label(subtitle_text, 11, WHITE)
|
||
subtitle.position = Vector2(size.x * 0.48, 2)
|
||
subtitle.size = Vector2(size.x * 0.50 - 16, 52)
|
||
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||
subtitle.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
display_root.add_child(subtitle)
|
||
|
||
|
||
func _artifact_name() -> String:
|
||
var artifact_id := String(market.state.selected_artifact_id)
|
||
if artifact_id.is_empty() or not content.artifacts.has(artifact_id):
|
||
return _tr("未选择", "NONE")
|
||
var row: Dictionary = content.artifacts[artifact_id]
|
||
return _tr(String(row.name_zh), String(row.name_en))
|
||
|
||
|
||
func _boss_plan_name() -> String:
|
||
var plan := String(market.state.selected_boss_plan)
|
||
return _tr("固阵反击", "BRACE AND PUNISH") if plan == "brace" else _tr("转棱破面", "ROTATE THE PANE")
|
||
|
||
|
||
func _pane_name(pane: String) -> String:
|
||
var names := {
|
||
"projectile": [_tr("投射", "PROJECTILE")],
|
||
"spell": [_tr("法术", "SPELL")],
|
||
"forced_movement": [_tr("位移", "FORCED MOVE")],
|
||
}
|
||
return String(names.get(pane, [pane])[0])
|