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_PORTRAIT_SIZE := Vector2(164, 186)
|
||||||
const DIALOGUE_COLUMN_SIZE := Vector2(820, 186)
|
const DIALOGUE_COLUMN_SIZE := Vector2(820, 186)
|
||||||
const DIALOGUE_TEXT_SIZE := Vector2(800, 104)
|
const DIALOGUE_TEXT_SIZE := Vector2(800, 104)
|
||||||
|
const BRIEFING_CAMP_THUMBNAIL_SIZE := Vector2(138, 72)
|
||||||
const CAMP_TALK_PORTRAIT_SIZE := Vector2(58, 58)
|
const CAMP_TALK_PORTRAIT_SIZE := Vector2(58, 58)
|
||||||
const SHOP_MERCHANT_BADGE_SIZE := Vector2(50, 50)
|
const SHOP_MERCHANT_BADGE_SIZE := Vector2(50, 50)
|
||||||
const SHOP_ITEM_ICON_SIZE := Vector2(48, 48)
|
const SHOP_ITEM_ICON_SIZE := Vector2(48, 48)
|
||||||
@@ -110,6 +111,10 @@ var briefing_panel: PanelContainer
|
|||||||
var briefing_title_label: Label
|
var briefing_title_label: Label
|
||||||
var briefing_location_label: Label
|
var briefing_location_label: Label
|
||||||
var briefing_objective_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 briefing_label: Label
|
||||||
var chapter_button: Button
|
var chapter_button: Button
|
||||||
var chapter_menu: VBoxContainer
|
var chapter_menu: VBoxContainer
|
||||||
@@ -632,8 +637,40 @@ func _create_hud() -> void:
|
|||||||
briefing_objective_label.custom_minimum_size = Vector2(640, 48)
|
briefing_objective_label.custom_minimum_size = Vector2(640, 48)
|
||||||
briefing_column.add_child(briefing_objective_label)
|
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()
|
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_column.add_child(briefing_scroll)
|
||||||
|
|
||||||
briefing_label = Label.new()
|
briefing_label = Label.new()
|
||||||
@@ -1296,6 +1333,8 @@ func _handle_key(event: InputEventKey) -> void:
|
|||||||
_hide_chapter_menu()
|
_hide_chapter_menu()
|
||||||
elif shop_menu != null and shop_menu.visible:
|
elif shop_menu != null and shop_menu.visible:
|
||||||
_hide_shop_menu()
|
_hide_shop_menu()
|
||||||
|
elif talk_menu != null and talk_menu.visible:
|
||||||
|
_hide_talk_menu()
|
||||||
elif armory_menu != null and armory_menu.visible:
|
elif armory_menu != null and armory_menu.visible:
|
||||||
_hide_armory_menu()
|
_hide_armory_menu()
|
||||||
elif roster_menu != null and roster_menu.visible:
|
elif roster_menu != null and roster_menu.visible:
|
||||||
@@ -3992,6 +4031,111 @@ func _format_briefing_rewards() -> String:
|
|||||||
return _join_strings(parts, ", ")
|
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:
|
func _show_briefing() -> void:
|
||||||
_play_bgm("menu")
|
_play_bgm("menu")
|
||||||
var briefing := state.get_briefing()
|
var briefing := state.get_briefing()
|
||||||
@@ -4013,6 +4157,8 @@ func _show_briefing() -> void:
|
|||||||
briefing_label.text = body
|
briefing_label.text = body
|
||||||
briefing_panel.visible = true
|
briefing_panel.visible = true
|
||||||
battle_started = false
|
battle_started = false
|
||||||
|
var prep_locked := _is_prebattle_prep_locked()
|
||||||
|
_update_briefing_camp_overview(briefing, prep_locked)
|
||||||
_hide_chapter_menu()
|
_hide_chapter_menu()
|
||||||
_hide_shop_menu()
|
_hide_shop_menu()
|
||||||
_hide_talk_menu()
|
_hide_talk_menu()
|
||||||
@@ -4021,7 +4167,6 @@ func _show_briefing() -> void:
|
|||||||
_hide_formation_menu()
|
_hide_formation_menu()
|
||||||
_hide_save_menu()
|
_hide_save_menu()
|
||||||
_clear_pending_move_state()
|
_clear_pending_move_state()
|
||||||
var prep_locked := _is_prebattle_prep_locked()
|
|
||||||
if chapter_button != null:
|
if chapter_button != null:
|
||||||
chapter_button.disabled = campaign_state.get_chapter_progress_entries().is_empty()
|
chapter_button.disabled = campaign_state.get_chapter_progress_entries().is_empty()
|
||||||
if shop_button != null:
|
if shop_button != null:
|
||||||
@@ -4718,6 +4863,7 @@ func _reload_from_campaign_state() -> void:
|
|||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
_hide_chapter_menu()
|
_hide_chapter_menu()
|
||||||
_hide_shop_menu()
|
_hide_shop_menu()
|
||||||
|
_hide_talk_menu()
|
||||||
_hide_armory_menu()
|
_hide_armory_menu()
|
||||||
_hide_roster_menu()
|
_hide_roster_menu()
|
||||||
_hide_formation_menu()
|
_hide_formation_menu()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ func _init() -> void:
|
|||||||
_check_completed_replay_cannot_claim(failures)
|
_check_completed_replay_cannot_claim(failures)
|
||||||
_check_conditional_camp_conversations(failures)
|
_check_conditional_camp_conversations(failures)
|
||||||
_check_wuchao_branch_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):
|
if not _restore_user_file(SAVE_PATH, save_backup):
|
||||||
failures.append("could not restore automatic campaign save")
|
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()
|
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):
|
func _new_prebattle_scene(failures: Array[String], label: String):
|
||||||
return _new_prebattle_scene_for(failures, label, SCENARIO_ID, SCENARIO_PATH)
|
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())
|
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"):
|
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")
|
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: 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)
|
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"):
|
if not bean_buy_detail.contains("Heal 20") or not bean_buy_detail.contains("Owned x0"):
|
||||||
|
|||||||
Reference in New Issue
Block a user