Add prebattle camp overview
This commit is contained in:
@@ -58,6 +58,7 @@ const DIALOGUE_PANEL_SIZE := Vector2(1040, 210)
|
||||
const DIALOGUE_PORTRAIT_SIZE := Vector2(164, 186)
|
||||
const DIALOGUE_COLUMN_SIZE := Vector2(820, 186)
|
||||
const DIALOGUE_TEXT_SIZE := Vector2(800, 104)
|
||||
const BRIEFING_CAMP_THUMBNAIL_SIZE := Vector2(138, 72)
|
||||
const CAMP_TALK_PORTRAIT_SIZE := Vector2(58, 58)
|
||||
const SHOP_MERCHANT_BADGE_SIZE := Vector2(50, 50)
|
||||
const SHOP_ITEM_ICON_SIZE := Vector2(48, 48)
|
||||
@@ -110,6 +111,10 @@ var briefing_panel: PanelContainer
|
||||
var briefing_title_label: Label
|
||||
var briefing_location_label: Label
|
||||
var briefing_objective_label: Label
|
||||
var briefing_camp_overview_row: HBoxContainer
|
||||
var briefing_camp_overview_texture: TextureRect
|
||||
var briefing_camp_overview_fallback_label: Label
|
||||
var briefing_camp_overview_label: Label
|
||||
var briefing_label: Label
|
||||
var chapter_button: Button
|
||||
var chapter_menu: VBoxContainer
|
||||
@@ -632,8 +637,40 @@ func _create_hud() -> void:
|
||||
briefing_objective_label.custom_minimum_size = Vector2(640, 48)
|
||||
briefing_column.add_child(briefing_objective_label)
|
||||
|
||||
briefing_camp_overview_row = HBoxContainer.new()
|
||||
briefing_camp_overview_row.custom_minimum_size = Vector2(640, 76)
|
||||
briefing_camp_overview_row.add_theme_constant_override("separation", 8)
|
||||
briefing_column.add_child(briefing_camp_overview_row)
|
||||
|
||||
var camp_thumbnail_panel := PanelContainer.new()
|
||||
camp_thumbnail_panel.custom_minimum_size = BRIEFING_CAMP_THUMBNAIL_SIZE
|
||||
briefing_camp_overview_row.add_child(camp_thumbnail_panel)
|
||||
|
||||
var camp_thumbnail_stack := Control.new()
|
||||
camp_thumbnail_stack.custom_minimum_size = BRIEFING_CAMP_THUMBNAIL_SIZE
|
||||
camp_thumbnail_panel.add_child(camp_thumbnail_stack)
|
||||
|
||||
briefing_camp_overview_texture = TextureRect.new()
|
||||
briefing_camp_overview_texture.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
briefing_camp_overview_texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
briefing_camp_overview_texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
camp_thumbnail_stack.add_child(briefing_camp_overview_texture)
|
||||
|
||||
briefing_camp_overview_fallback_label = Label.new()
|
||||
briefing_camp_overview_fallback_label.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
briefing_camp_overview_fallback_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
briefing_camp_overview_fallback_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
briefing_camp_overview_fallback_label.add_theme_font_size_override("font_size", 18)
|
||||
camp_thumbnail_stack.add_child(briefing_camp_overview_fallback_label)
|
||||
|
||||
briefing_camp_overview_label = Label.new()
|
||||
briefing_camp_overview_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
briefing_camp_overview_label.custom_minimum_size = Vector2(490, 72)
|
||||
briefing_camp_overview_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
briefing_camp_overview_row.add_child(briefing_camp_overview_label)
|
||||
|
||||
var briefing_scroll := ScrollContainer.new()
|
||||
briefing_scroll.custom_minimum_size = Vector2(640, 96)
|
||||
briefing_scroll.custom_minimum_size = Vector2(640, 72)
|
||||
briefing_column.add_child(briefing_scroll)
|
||||
|
||||
briefing_label = Label.new()
|
||||
@@ -1296,6 +1333,8 @@ func _handle_key(event: InputEventKey) -> void:
|
||||
_hide_chapter_menu()
|
||||
elif shop_menu != null and shop_menu.visible:
|
||||
_hide_shop_menu()
|
||||
elif talk_menu != null and talk_menu.visible:
|
||||
_hide_talk_menu()
|
||||
elif armory_menu != null and armory_menu.visible:
|
||||
_hide_armory_menu()
|
||||
elif roster_menu != null and roster_menu.visible:
|
||||
@@ -3992,6 +4031,111 @@ func _format_briefing_rewards() -> String:
|
||||
return _join_strings(parts, ", ")
|
||||
|
||||
|
||||
func _update_briefing_camp_overview(briefing: Dictionary, prep_locked: bool) -> void:
|
||||
if briefing_camp_overview_row == null:
|
||||
return
|
||||
var overview_text := _format_briefing_camp_overview_text(briefing, prep_locked)
|
||||
briefing_camp_overview_row.visible = not overview_text.is_empty()
|
||||
if briefing_camp_overview_label != null:
|
||||
briefing_camp_overview_label.text = overview_text
|
||||
var texture := _current_battle_background_texture()
|
||||
var has_texture := texture != null
|
||||
if briefing_camp_overview_texture != null:
|
||||
briefing_camp_overview_texture.texture = texture
|
||||
briefing_camp_overview_texture.visible = has_texture
|
||||
if briefing_camp_overview_fallback_label != null:
|
||||
briefing_camp_overview_fallback_label.text = "Camp"
|
||||
briefing_camp_overview_fallback_label.visible = not has_texture
|
||||
|
||||
|
||||
func _format_briefing_camp_overview_text(briefing: Dictionary, prep_locked: bool) -> String:
|
||||
var parts := []
|
||||
var location := str(briefing.get("location", "")).strip_edges()
|
||||
if not location.is_empty():
|
||||
parts.append(location)
|
||||
parts.append("Gold %dG" % campaign_state.gold)
|
||||
var talk_text := _format_briefing_talk_overview_text()
|
||||
if not talk_text.is_empty():
|
||||
parts.append(talk_text)
|
||||
var shop_text := _format_briefing_shop_overview_text()
|
||||
if not shop_text.is_empty():
|
||||
parts.append(shop_text)
|
||||
var roster_text := _format_briefing_roster_overview_text()
|
||||
if not roster_text.is_empty():
|
||||
parts.append(roster_text)
|
||||
var formation_text := _format_briefing_formation_overview_text()
|
||||
if not formation_text.is_empty():
|
||||
parts.append(formation_text)
|
||||
if not state.get_controllable_player_units().is_empty():
|
||||
parts.append("Armory ready")
|
||||
if prep_locked:
|
||||
parts.append("Replay prep locked")
|
||||
return _join_strings(parts, " | ")
|
||||
|
||||
|
||||
func _format_briefing_talk_overview_text() -> String:
|
||||
var conversations := _camp_conversation_entries()
|
||||
if conversations.is_empty():
|
||||
return ""
|
||||
var supply_counts := _camp_conversation_supply_counts(conversations)
|
||||
var ready := int(supply_counts.get("ready", 0))
|
||||
var claimed := int(supply_counts.get("claimed", 0))
|
||||
var parts := ["Talk %d" % conversations.size()]
|
||||
if ready > 0:
|
||||
parts.append("%d supply" % ready)
|
||||
if claimed > 0:
|
||||
parts.append("%d claimed" % claimed)
|
||||
return _join_strings(parts, ", ")
|
||||
|
||||
|
||||
func _format_briefing_shop_overview_text() -> String:
|
||||
var buy_count := state.get_shop_item_ids().size()
|
||||
var sell_count := _get_sellable_item_ids().size()
|
||||
if buy_count <= 0 and sell_count <= 0:
|
||||
return ""
|
||||
if sell_count > 0:
|
||||
return "Shop %d buy, %d sell" % [buy_count, sell_count]
|
||||
return "Shop %d buy" % buy_count
|
||||
|
||||
|
||||
func _format_briefing_roster_overview_text() -> String:
|
||||
if not state.has_deployment_roster():
|
||||
return ""
|
||||
var deployed_count := state.get_deployed_player_count()
|
||||
var max_units := state.get_deployment_max_units()
|
||||
if max_units > 0:
|
||||
return "Deploy %d/%d" % [deployed_count, max_units]
|
||||
return "Deploy %d" % deployed_count
|
||||
|
||||
|
||||
func _format_briefing_formation_overview_text() -> String:
|
||||
var formation_count := state.get_formation_cells().size()
|
||||
if formation_count <= 0:
|
||||
return ""
|
||||
return "Formation %d tiles" % formation_count
|
||||
|
||||
|
||||
func _camp_conversation_supply_counts(conversations: Array) -> Dictionary:
|
||||
var counts := {"ready": 0, "claimed": 0}
|
||||
for conversation in conversations:
|
||||
if typeof(conversation) != TYPE_DICTIONARY:
|
||||
continue
|
||||
var effects: Array = conversation.get("effects", [])
|
||||
var has_supply := false
|
||||
for effect in effects:
|
||||
if typeof(effect) == TYPE_DICTIONARY and str(effect.get("type", "")) == "grant_item":
|
||||
has_supply = true
|
||||
break
|
||||
if not has_supply:
|
||||
continue
|
||||
var conversation_id := str(conversation.get("id", ""))
|
||||
if campaign_state.has_claimed_camp_conversation_effects(active_scenario_id, conversation_id):
|
||||
counts["claimed"] = int(counts.get("claimed", 0)) + 1
|
||||
else:
|
||||
counts["ready"] = int(counts.get("ready", 0)) + 1
|
||||
return counts
|
||||
|
||||
|
||||
func _show_briefing() -> void:
|
||||
_play_bgm("menu")
|
||||
var briefing := state.get_briefing()
|
||||
@@ -4013,6 +4157,8 @@ func _show_briefing() -> void:
|
||||
briefing_label.text = body
|
||||
briefing_panel.visible = true
|
||||
battle_started = false
|
||||
var prep_locked := _is_prebattle_prep_locked()
|
||||
_update_briefing_camp_overview(briefing, prep_locked)
|
||||
_hide_chapter_menu()
|
||||
_hide_shop_menu()
|
||||
_hide_talk_menu()
|
||||
@@ -4021,7 +4167,6 @@ func _show_briefing() -> void:
|
||||
_hide_formation_menu()
|
||||
_hide_save_menu()
|
||||
_clear_pending_move_state()
|
||||
var prep_locked := _is_prebattle_prep_locked()
|
||||
if chapter_button != null:
|
||||
chapter_button.disabled = campaign_state.get_chapter_progress_entries().is_empty()
|
||||
if shop_button != null:
|
||||
@@ -4718,6 +4863,7 @@ func _reload_from_campaign_state() -> void:
|
||||
_hide_equip_menu()
|
||||
_hide_chapter_menu()
|
||||
_hide_shop_menu()
|
||||
_hide_talk_menu()
|
||||
_hide_armory_menu()
|
||||
_hide_roster_menu()
|
||||
_hide_formation_menu()
|
||||
|
||||
@@ -25,6 +25,7 @@ func _init() -> void:
|
||||
_check_completed_replay_cannot_claim(failures)
|
||||
_check_conditional_camp_conversations(failures)
|
||||
_check_wuchao_branch_camp_conversations(failures)
|
||||
_check_talk_menu_closes_on_cancel_and_reload(failures)
|
||||
|
||||
if not _restore_user_file(SAVE_PATH, save_backup):
|
||||
failures.append("could not restore automatic campaign save")
|
||||
@@ -280,6 +281,25 @@ func _check_wuchao_branch_camp_conversations(failures: Array[String]) -> void:
|
||||
defense_scene.free()
|
||||
|
||||
|
||||
func _check_talk_menu_closes_on_cancel_and_reload(failures: Array[String]) -> void:
|
||||
var scene = _new_prebattle_scene(failures, "talk menu close guards")
|
||||
if scene == null:
|
||||
return
|
||||
scene._create_hud()
|
||||
scene.briefing_panel.visible = true
|
||||
scene.talk_menu.visible = true
|
||||
var cancel_event := InputEventKey.new()
|
||||
cancel_event.keycode = KEY_ESCAPE
|
||||
scene._handle_key(cancel_event)
|
||||
if scene.talk_menu.visible:
|
||||
failures.append("Escape should close the pre-battle talk menu")
|
||||
scene.talk_menu.visible = true
|
||||
scene._reload_from_campaign_state()
|
||||
if scene.talk_menu.visible:
|
||||
failures.append("campaign reload should hide the pre-battle talk menu")
|
||||
scene.free()
|
||||
|
||||
|
||||
func _new_prebattle_scene(failures: Array[String], label: String):
|
||||
return _new_prebattle_scene_for(failures, label, SCENARIO_ID, SCENARIO_PATH)
|
||||
|
||||
|
||||
@@ -195,6 +195,10 @@ func _check_scene_texture_loading(failures: Array[String]) -> void:
|
||||
var merchant_notice := scene._format_shop_merchant_notice_text(scene.state.get_shop_merchant())
|
||||
if not merchant_notice.contains("Camp Merchant") or not merchant_notice.contains("Roadside stores"):
|
||||
failures.append("shop merchant notice should include name and flavor lines")
|
||||
var camp_overview := scene._format_briefing_camp_overview_text(scene.state.get_briefing(), false)
|
||||
for expected in ["Yingchuan, 184 CE", "Gold 0G", "Talk 3", "1 supply", "Shop 2 buy", "Deploy 2/2", "Formation 4 tiles", "Armory ready"]:
|
||||
if not camp_overview.contains(expected):
|
||||
failures.append("camp overview should include %s: %s" % [expected, camp_overview])
|
||||
var bean: Dictionary = scene.state.get_item_def("bean")
|
||||
var bean_buy_detail := scene._format_shop_buy_item_detail_text("bean", bean, int(bean.get("price", 0)), 0)
|
||||
if not bean_buy_detail.contains("Heal 20") or not bean_buy_detail.contains("Owned x0"):
|
||||
|
||||
Reference in New Issue
Block a user