diff --git a/runtime/entry/entry_shell.gd b/runtime/entry/entry_shell.gd index d41f1efc..b183b0e5 100644 --- a/runtime/entry/entry_shell.gd +++ b/runtime/entry/entry_shell.gd @@ -563,6 +563,8 @@ func show_guild_handoff(company := {}) -> void: resolved = loaded.company.duplicate(true) guild_contract_service.company_path = initial_company_service.company_path guild_company = resolved + show_guild_anchor(resolved) + return current_screen = "guild_handoff" _build_world_base(true) var frame_safe := MarginContainer.new() @@ -696,6 +698,164 @@ func show_guild_handoff(company := {}) -> void: focus_target.call_deferred("grab_focus") +func show_guild_anchor(company: Dictionary) -> void: + var resolved := company.duplicate(true) + guild_company = resolved + current_screen = "guild_handoff" + _build_world_base(true) + var margin := maxi(14, int(settings.get("safe_area", 24))) + var root := Control.new() + root.name = "GuildAnchorRoot" + root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + root.offset_left = margin + root.offset_right = -margin + root.offset_top = 10 + root.offset_bottom = -10 + add_child(root) + + var top_rail := ColorRect.new() + top_rail.name = "GuildAnchorTopRail" + top_rail.set_anchors_and_offsets_preset(Control.PRESET_TOP_WIDE) + top_rail.offset_bottom = 64 + top_rail.color = Color(0.04, 0.10, 0.13, 0.72) + top_rail.mouse_filter = Control.MOUSE_FILTER_IGNORE + root.add_child(top_rail) + var top_rule := ColorRect.new() + top_rule.name = "GuildAnchorTopRule" + top_rule.position = Vector2(18, 58) + top_rule.size = Vector2(maxf(1.0, size.x - margin * 2.0 - 36.0), 2) + top_rule.color = Color("#d6b76c") + top_rule.mouse_filter = Control.MOUSE_FILTER_IGNORE + root.add_child(top_rule) + var title := _label(_tr("公会大厅", "GUILD HALL"), _scaled(22), Color("#fff4d6")) + title.name = "GuildAnchorTitle" + title.position = Vector2(18, 9) + title.size = Vector2(330, 30) + root.add_child(title) + var status := _label(_tr("持续远征", "PERSISTENT COMPANY"), _scaled(11), Color("#a9d9ef")) + status.name = "GuildAnchorStatus" + status.position = Vector2(20, 38) + status.size = Vector2(330, 18) + root.add_child(status) + var company_status := _label(_tr("成员 %d", "%d MEMBERS") % resolved.owned_recruits.size(), _scaled(13), Color("#f0ca75")) + company_status.name = "GuildAnchorCompanyStatus" + company_status.anchor_left = 1.0 + company_status.anchor_right = 1.0 + company_status.offset_left = -230 + company_status.offset_right = -18 + company_status.offset_top = 20 + company_status.offset_bottom = 48 + company_status.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + root.add_child(company_status) + + var body := Control.new() + body.name = "GuildHandoffBody" + body.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + body.offset_top = 74 + body.offset_bottom = -62 + root.add_child(body) + var roster := HBoxContainer.new() + roster.name = "GuildRoster" + roster.anchor_right = 0.57 + roster.anchor_bottom = 1.0 + roster.offset_left = 18 + roster.offset_top = 10 + roster.offset_right = -6 + roster.offset_bottom = -8 + roster.size_flags_horizontal = Control.SIZE_EXPAND_FILL + roster.add_theme_constant_override("separation", 8) + body.add_child(roster) + for index in range(resolved.owned_recruits.size()): + roster.add_child(_guild_member_column(index, resolved.owned_recruits[index])) + + var next := VBoxContainer.new() + next.name = "GuildNextActions" + next.anchor_left = 0.59 + next.anchor_right = 1.0 + next.anchor_bottom = 1.0 + next.offset_left = 0 + next.offset_top = 10 + next.offset_right = -8 + next.offset_bottom = -8 + next.add_theme_constant_override("separation", 5) + body.add_child(next) + var active_contract: bool = String(resolved.get("active_contract", {}).get("status", "")) in ["market", "battle", "result", "recovery"] + var returned_contract: bool = String(resolved.get("active_contract", {}).get("status", "")) == "returned" + var next_label := _label(_tr("下一步", "NEXT ACTION"), _scaled(10), Color("#e8d39d")) + next_label.name = "GuildNextHeading" + next.add_child(next_label) + var next_copy := _label( + _tr("前往市场扩充远征团。", "Enter the Market to expand the Company.") if active_contract else _tr("选择首个委托与难度。", "Choose the first Contract and difficulty."), + _scaled(14), Color("#fff4d6") + ) + next_copy.name = "GuildNextCopy" + next_copy.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + next.add_child(next_copy) + var contract := _guild_destination_button( + "GuildContractActive" if active_contract else "GuildContractBoard", + "contract", + _tr("委托已开始", "Contract Active") if active_contract else _tr("委托板", "Contract Board"), + func(): _set_guild_notice(_tr("委托已开始,请进入市场。", "The Contract is active; enter its Market.")) if active_contract else show_contract_choice() + ) + _set_entry_button_disabled(contract, active_contract or returned_contract) + contract.visible = not active_contract and not returned_contract + next.add_child(contract) + var contract_reason := _label( + _tr("已开始:风镜初渡", "Active: First Windglass Crossing") if active_contract else _tr("可用:选择难度后确认", "Available: choose a difficulty, then confirm"), + _scaled(10), Color("#e8d39d") + ) + contract_reason.name = "GuildContractReason" + contract_reason.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + next.add_child(contract_reason) + var market := _guild_destination_button( + "GuildMarket" if active_contract else "GuildMarketUnavailable", + "market", + _tr("进入市场", "Enter Market") if active_contract else _tr("市场", "Market"), + func(): show_market_handoff(resolved) if active_contract else _set_guild_notice(_tr("请先确认首个委托。", "Confirm the first Contract first.")) + ) + _set_entry_button_disabled(market, not active_contract) + market.visible = active_contract + next.add_child(market) + var market_reason := _label( + _tr("可用:市场准备", "Available: Market preparation") if active_contract else _tr("不可用:尚未建立首个委托。", "Unavailable: no first Contract has been created."), + _scaled(10), Color("#e8d39d") + ) + market_reason.name = "GuildMarketReason" + market_reason.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + next.add_child(market_reason) + var notice := _label("", _scaled(10), Color("#f0ca75")) + notice.name = "GuildNotice" + notice.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + next.add_child(notice) + + var bottom_rail := ColorRect.new() + bottom_rail.name = "GuildAnchorBottomRail" + bottom_rail.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_WIDE) + bottom_rail.offset_top = -54 + bottom_rail.color = Color(0.04, 0.09, 0.11, 0.76) + bottom_rail.mouse_filter = Control.MOUSE_FILTER_IGNORE + root.add_child(bottom_rail) + var footer := HBoxContainer.new() + footer.name = "GuildHandoffFooter" + footer.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_WIDE) + footer.offset_top = -52 + footer.add_theme_constant_override("separation", 8) + root.add_child(footer) + var back := _company_button("GuildTitle", _tr("返回标题", "Back to Title"), back_to_title, "company_utility", "back") + back.custom_minimum_size = Vector2(190, 48) + footer.add_child(back) + var footer_spacer := Control.new() + footer_spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + footer.add_child(footer_spacer) + var roster_button := _company_button("GuildRosterButton", _tr("成员", "Roster"), show_roster.bind(resolved), "content_secondary", "inspect") + roster_button.custom_minimum_size = Vector2(160, 48) + footer.add_child(roster_button) + var settings_button := _company_button("GuildSettings", _tr("设置", "Settings"), show_settings_from_guild, "content_secondary", "settings") + settings_button.custom_minimum_size = Vector2(160, 48) + footer.add_child(settings_button) + (contract if contract.visible else market if market.visible else roster_button).call_deferred("grab_focus") + + func show_roster(company := {}) -> void: var resolved: Dictionary = Dictionary(company).duplicate(true) if company is Dictionary else guild_company.duplicate(true) if resolved.is_empty():