Add placeholder battle audio
This commit is contained in:
@@ -103,6 +103,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
||||
- Campaign completion screen.
|
||||
- New campaign save reset.
|
||||
- Victory and defeat result overlay.
|
||||
- Placeholder menu/battle BGM plus UI, movement, attack, tactic, item, victory, and defeat SFX.
|
||||
- Data-driven scenario setup through JSON definitions and deployments.
|
||||
|
||||
## Run
|
||||
@@ -111,7 +112,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
||||
2. Open this folder as a Godot project.
|
||||
3. Run the project. The main scene is `res://scenes/battle_scene.tscn`.
|
||||
|
||||
Campaign order lives at `res://data/campaign/campaign.json`. Scenarios live under `res://data/scenarios/`, with reusable officers, classes, items, skills, and terrain definitions under `res://data/defs/`.
|
||||
Campaign order lives at `res://data/campaign/campaign.json`. Scenarios live under `res://data/scenarios/`, with reusable officers, classes, items, skills, and terrain definitions under `res://data/defs/`. Placeholder BGM and SFX live under `res://audio/`.
|
||||
|
||||
Data files can be checked with `powershell -NoProfile -ExecutionPolicy Bypass -File tools\validate_data.ps1`.
|
||||
|
||||
|
||||
BIN
audio/bgm/battle_loop_placeholder.wav
Normal file
BIN
audio/bgm/battle_loop_placeholder.wav
Normal file
Binary file not shown.
BIN
audio/bgm/menu_theme_placeholder.wav
Normal file
BIN
audio/bgm/menu_theme_placeholder.wav
Normal file
Binary file not shown.
BIN
audio/sfx/bow_release_01.wav
Normal file
BIN
audio/sfx/bow_release_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/defeat_sting_01.wav
Normal file
BIN
audio/sfx/defeat_sting_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/fire_skill_01.wav
Normal file
BIN
audio/sfx/fire_skill_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/footstep_armor_01.wav
Normal file
BIN
audio/sfx/footstep_armor_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/guard_block_01.wav
Normal file
BIN
audio/sfx/guard_block_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/hit_heavy_01.wav
Normal file
BIN
audio/sfx/hit_heavy_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/item_pickup_01.wav
Normal file
BIN
audio/sfx/item_pickup_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/menu_cancel_01.wav
Normal file
BIN
audio/sfx/menu_cancel_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/menu_confirm_01.wav
Normal file
BIN
audio/sfx/menu_confirm_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/skill_cast_01.wav
Normal file
BIN
audio/sfx/skill_cast_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/slash_01.wav
Normal file
BIN
audio/sfx/slash_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/ui_click_01.wav
Normal file
BIN
audio/sfx/ui_click_01.wav
Normal file
Binary file not shown.
BIN
audio/sfx/victory_sting_01.wav
Normal file
BIN
audio/sfx/victory_sting_01.wav
Normal file
Binary file not shown.
@@ -15,6 +15,7 @@
|
||||
- `scripts/core/campaign_state.gd`: Thin campaign save state for gold, inventory, roster, completed scenarios, and campaign flags.
|
||||
- `scripts/core/battle_state.gd`: Battle rules, unit state, turns, AI, and victory checks.
|
||||
- `scripts/scenes/battle_scene.gd`: Rendering, input handling, and HUD wiring.
|
||||
- `audio/bgm/*.wav` and `audio/sfx/*.wav`: Placeholder music loops and interface/action stingers.
|
||||
- `data/campaign/campaign.json`: Campaign order and scenario paths.
|
||||
- `data/defs/*.json`: Officers, classes, terrain, items, and skills.
|
||||
- `data/scenarios/*.json`: Scenario definitions.
|
||||
@@ -37,7 +38,7 @@
|
||||
4. Show scenario briefing, including the optional pre-battle shop, Armory, Roster, and Formation setup.
|
||||
5. Player selects a unit.
|
||||
6. Briefing completion dispatches battle-begin events, including opening dialogue.
|
||||
7. Unit moves, attacks, chooses a tactic or consumable item from the side menu, changes equipment, counters, gains EXP, levels up, promotes at class thresholds, or waits. Tactic buttons show MP cost, kind, range, and power. Physical attacks roll hit chance from unit agility and target terrain avoid; misses give reduced EXP.
|
||||
7. Unit moves, attacks, chooses a tactic or consumable item from the side menu, changes equipment, counters, gains EXP, levels up, promotes at class thresholds, or waits. Tactic buttons show MP cost, kind, range, and power. Physical attacks roll hit chance from unit agility and target terrain avoid; misses give reduced EXP. The scene plays placeholder SFX for core action and UI feedback.
|
||||
8. Player ends turn.
|
||||
9. Enemy AI moves, attacks, and can cast damage or healing tactics through the same combat and skill resolvers.
|
||||
10. Battle checks scenario-defined defeat conditions first, including commander loss and turn limits, then victory conditions such as enemy defeat or reaching a marked destination tile.
|
||||
@@ -57,4 +58,6 @@ Joined officers gate whether `requires_joined` player deployments are loaded at
|
||||
|
||||
Inventory and campaign flags are copied from `CampaignState` into `BattleState` when a scenario starts. Pre-battle shop stock comes from the loaded scenario's `shop.items` list plus matching `shop.conditional_items` blocks. Purchases are campaign transactions: they spend saved gold immediately, write the save file, and refresh the already-loaded battle inventory before the player begins the battle. Pre-battle Armory equipment changes use the same equipment rules as battle HUD equipment changes, then immediately save merged roster equipment/stat snapshots and inventory stock. Pre-battle Roster uses scenario `roster.max_units`, `roster.required_officers`, and `roster.required_units` to mark optional player deployments as sortie or reserve; reserve units remain in the candidate list but are excluded from board occupancy, drawing, selection, AI targeting, and living-unit victory checks. Non-controllable player units can act as protected escort targets: enemies can attack them and conditions can reference them, but they are skipped by player selection, Armory, Formation, counterattacks, and campaign roster progression snapshots when `persist_progression` is false. Scenario `ai_target_priority` nudges enemy movement and damage-skill scoring toward important targets such as envoys without overriding defeat bonuses or range checks. Pre-battle Formation uses the loaded scenario's `formation.cells` and only mutates current battle unit positions before battle-begin events fire. Destination victory cells from `unit_reaches_tile` conditions are exposed to the scene for objective overlays and tile info. Movement-triggered `unit_reaches_tile` events receive the unit that just moved, so ambushes fire on entry rather than from units already standing on a marker. Briefing data can append matching `briefing.conditional_lines`, and scenario events can use `when.campaign_flags` to branch dialogue, objective changes, and reinforcements from saved campaign choices. Consumable use can restore HP or MP, and in-battle item use plus equipment swaps mutate only the battle copy until victory; defeats and restarts do not spend saved items or preserve in-battle gear changes. Post-battle choices write campaign `flags` only after the result-panel button is pressed; if the player reloads after rewards are saved but before selecting, the pending scenario id restores the victory choice panel without replaying rewards. The next battle button stays locked until the campaign choice save succeeds. Equipment rewards share the same `item_id -> count` inventory stock, cover weapon, armor, and accessory slots, can be equipped from the side HUD before the selected unit moves or acts, and do not appear in the consumable action menu. Already completed scenarios can be replayed without granting duplicate rewards, choices, or replay inventory consumption; pre-battle Shop, Armory, Roster, and Formation are disabled on completed-scenario replays to avoid side effects.
|
||||
|
||||
The battle scene owns presentation audio: briefing and result states use menu BGM, active battles use battle BGM, and log/result hooks trigger placeholder SFX without changing battle rules.
|
||||
|
||||
The `New Campaign` button clears `user://campaign_save.json`, resets campaign state to the first scenario, and reloads the opening briefing.
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
- Portrait pipeline.
|
||||
- Unit sprites and animations.
|
||||
- Battle effects.
|
||||
- Music and sound.
|
||||
- Music and sound. Basic placeholder BGM and SFX now play for menus, battle flow, unit actions, tactics, items, and result states.
|
||||
- Chapter UI and visual novel scenes.
|
||||
|
||||
## Milestone 6: Content Expansion
|
||||
|
||||
@@ -13,6 +13,24 @@ const SKILL_OVERLAY_COLOR := Color(0.56, 0.28, 0.95, 0.28)
|
||||
const ITEM_OVERLAY_COLOR := Color(0.24, 0.74, 0.34, 0.30)
|
||||
const FORMATION_OVERLAY_COLOR := Color(0.95, 0.76, 0.25, 0.34)
|
||||
const OBJECTIVE_OVERLAY_COLOR := Color(1.0, 0.78, 0.18, 0.30)
|
||||
const BGM_MENU := preload("res://audio/bgm/menu_theme_placeholder.wav")
|
||||
const BGM_BATTLE := preload("res://audio/bgm/battle_loop_placeholder.wav")
|
||||
const SFX_BOW_RELEASE := preload("res://audio/sfx/bow_release_01.wav")
|
||||
const SFX_DEFEAT_STING := preload("res://audio/sfx/defeat_sting_01.wav")
|
||||
const SFX_FIRE_SKILL := preload("res://audio/sfx/fire_skill_01.wav")
|
||||
const SFX_FOOTSTEP := preload("res://audio/sfx/footstep_armor_01.wav")
|
||||
const SFX_GUARD_BLOCK := preload("res://audio/sfx/guard_block_01.wav")
|
||||
const SFX_HIT_HEAVY := preload("res://audio/sfx/hit_heavy_01.wav")
|
||||
const SFX_ITEM_PICKUP := preload("res://audio/sfx/item_pickup_01.wav")
|
||||
const SFX_MENU_CANCEL := preload("res://audio/sfx/menu_cancel_01.wav")
|
||||
const SFX_MENU_CONFIRM := preload("res://audio/sfx/menu_confirm_01.wav")
|
||||
const SFX_SKILL_CAST := preload("res://audio/sfx/skill_cast_01.wav")
|
||||
const SFX_SLASH := preload("res://audio/sfx/slash_01.wav")
|
||||
const SFX_UI_CLICK := preload("res://audio/sfx/ui_click_01.wav")
|
||||
const SFX_VICTORY_STING := preload("res://audio/sfx/victory_sting_01.wav")
|
||||
const BGM_VOLUME_DB := -14.0
|
||||
const SFX_VOLUME_DB := -4.0
|
||||
const UI_SFX_VOLUME_DB := -8.0
|
||||
|
||||
var state: BattleState = BattleStateScript.new()
|
||||
var campaign_state: CampaignState = CampaignStateScript.new()
|
||||
@@ -83,10 +101,14 @@ var log_box: RichTextLabel
|
||||
var dialogue_queue: Array = []
|
||||
var active_dialogue_lines: Array = []
|
||||
var active_dialogue_index := 0
|
||||
var bgm_player: AudioStreamPlayer
|
||||
var current_bgm_key := ""
|
||||
var last_announced_battle_status := ""
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_create_hud()
|
||||
_create_audio()
|
||||
state.changed.connect(_on_state_changed)
|
||||
state.log_added.connect(_on_log_added)
|
||||
state.dialogue_requested.connect(_on_dialogue_requested)
|
||||
@@ -447,6 +469,130 @@ func _create_hud() -> void:
|
||||
result_column.add_child(next_battle_button)
|
||||
|
||||
|
||||
func _create_audio() -> void:
|
||||
bgm_player = AudioStreamPlayer.new()
|
||||
bgm_player.name = "BgmPlayer"
|
||||
bgm_player.volume_db = BGM_VOLUME_DB
|
||||
bgm_player.finished.connect(_on_bgm_finished)
|
||||
add_child(bgm_player)
|
||||
|
||||
|
||||
func _on_bgm_finished() -> void:
|
||||
if current_bgm_key.is_empty() or bgm_player == null or bgm_player.stream == null:
|
||||
return
|
||||
bgm_player.play()
|
||||
|
||||
|
||||
func _play_bgm(key: String) -> void:
|
||||
if bgm_player == null:
|
||||
return
|
||||
if current_bgm_key == key and bgm_player.playing:
|
||||
return
|
||||
var stream = _bgm_stream_for(key)
|
||||
if stream == null:
|
||||
return
|
||||
current_bgm_key = key
|
||||
bgm_player.stop()
|
||||
bgm_player.stream = stream
|
||||
bgm_player.play()
|
||||
|
||||
|
||||
func _bgm_stream_for(key: String):
|
||||
if key == "battle":
|
||||
return BGM_BATTLE
|
||||
if key == "menu":
|
||||
return BGM_MENU
|
||||
return null
|
||||
|
||||
|
||||
func _play_sfx(key: String, volume_db := SFX_VOLUME_DB) -> void:
|
||||
var stream = _sfx_stream_for(key)
|
||||
if stream == null:
|
||||
return
|
||||
var player := AudioStreamPlayer.new()
|
||||
player.name = "Sfx_%s" % key
|
||||
player.stream = stream
|
||||
player.volume_db = volume_db
|
||||
player.finished.connect(player.queue_free)
|
||||
add_child(player)
|
||||
player.play()
|
||||
|
||||
|
||||
func _sfx_stream_for(key: String):
|
||||
if key == "bow_release":
|
||||
return SFX_BOW_RELEASE
|
||||
if key == "defeat_sting":
|
||||
return SFX_DEFEAT_STING
|
||||
if key == "fire_skill":
|
||||
return SFX_FIRE_SKILL
|
||||
if key == "footstep":
|
||||
return SFX_FOOTSTEP
|
||||
if key == "guard_block":
|
||||
return SFX_GUARD_BLOCK
|
||||
if key == "hit_heavy":
|
||||
return SFX_HIT_HEAVY
|
||||
if key == "item_pickup":
|
||||
return SFX_ITEM_PICKUP
|
||||
if key == "menu_cancel":
|
||||
return SFX_MENU_CANCEL
|
||||
if key == "menu_confirm":
|
||||
return SFX_MENU_CONFIRM
|
||||
if key == "skill_cast":
|
||||
return SFX_SKILL_CAST
|
||||
if key == "slash":
|
||||
return SFX_SLASH
|
||||
if key == "ui_click":
|
||||
return SFX_UI_CLICK
|
||||
if key == "victory_sting":
|
||||
return SFX_VICTORY_STING
|
||||
return null
|
||||
|
||||
|
||||
func _play_ui_click() -> void:
|
||||
_play_sfx("ui_click", UI_SFX_VOLUME_DB)
|
||||
|
||||
|
||||
func _play_ui_confirm() -> void:
|
||||
_play_sfx("menu_confirm", UI_SFX_VOLUME_DB)
|
||||
|
||||
|
||||
func _play_ui_cancel() -> void:
|
||||
_play_sfx("menu_cancel", UI_SFX_VOLUME_DB)
|
||||
|
||||
|
||||
func _play_log_sfx(message: String) -> void:
|
||||
if message.contains(" but misses."):
|
||||
_play_sfx("guard_block")
|
||||
elif message.contains(" attacks ") or message.contains(" counterattacks "):
|
||||
if message.contains(" for "):
|
||||
_play_sfx("hit_heavy")
|
||||
elif message.contains(" casts "):
|
||||
if message.contains("restoring"):
|
||||
_play_sfx("skill_cast")
|
||||
else:
|
||||
_play_sfx("fire_skill")
|
||||
elif message.contains(" moved to ") or message.contains(" advances to ") or message.contains(" takes formation at ") or message.contains(" arrives at "):
|
||||
_play_sfx("footstep")
|
||||
elif message.contains(" uses ") or message.contains(" equips ") or message.begins_with("Bought "):
|
||||
_play_sfx("item_pickup")
|
||||
elif message.contains(" reaches Lv.") or message.contains(" promotes to "):
|
||||
_play_sfx("menu_confirm")
|
||||
|
||||
|
||||
func _announce_battle_status_audio() -> void:
|
||||
if state.battle_status == BattleState.STATUS_ACTIVE:
|
||||
last_announced_battle_status = ""
|
||||
return
|
||||
if last_announced_battle_status == state.battle_status:
|
||||
return
|
||||
last_announced_battle_status = state.battle_status
|
||||
_play_bgm("menu")
|
||||
if state.battle_status == BattleState.STATUS_VICTORY:
|
||||
_play_sfx("victory_sting")
|
||||
elif state.battle_status == BattleState.STATUS_DEFEAT:
|
||||
_play_sfx("defeat_sting")
|
||||
|
||||
|
||||
func _handle_board_click(screen_position: Vector2) -> void:
|
||||
if formation_menu != null and formation_menu.visible:
|
||||
_handle_formation_board_click(screen_position)
|
||||
@@ -500,6 +646,9 @@ func _handle_board_click(screen_position: Vector2) -> void:
|
||||
return
|
||||
|
||||
if not clicked_unit.is_empty() and clicked_unit.get("team", "") != selected_unit.get("team", ""):
|
||||
var attack_sfx := "bow_release" if int(selected_unit.get("range", 1)) > 1 else "slash"
|
||||
if attack_cells.has(clicked_unit.get("pos", Vector2i(-1, -1))):
|
||||
_play_sfx(attack_sfx)
|
||||
if state.try_attack_selected(clicked_unit["id"]):
|
||||
selected_skill_id = ""
|
||||
selected_item_id = ""
|
||||
@@ -928,6 +1077,7 @@ func _update_item_forecast(selected: Dictionary) -> void:
|
||||
func _update_result_panel() -> void:
|
||||
if result_panel == null:
|
||||
return
|
||||
_announce_battle_status_audio()
|
||||
if campaign_complete_screen:
|
||||
_clear_dialogue()
|
||||
_clear_result_choices()
|
||||
@@ -989,6 +1139,7 @@ func _on_state_changed() -> void:
|
||||
|
||||
|
||||
func _on_log_added(message: String) -> void:
|
||||
_play_log_sfx(message)
|
||||
if log_box == null:
|
||||
return
|
||||
log_box.append_text(message + "\n")
|
||||
@@ -1069,6 +1220,7 @@ func _render_dialogue_line() -> void:
|
||||
func _advance_dialogue() -> void:
|
||||
if not _is_dialogue_visible():
|
||||
return
|
||||
_play_ui_click()
|
||||
active_dialogue_index += 1
|
||||
if active_dialogue_index < active_dialogue_lines.size():
|
||||
_render_dialogue_line()
|
||||
@@ -1103,10 +1255,13 @@ func _on_wait_pressed() -> void:
|
||||
_hide_tactic_menu()
|
||||
_hide_item_menu()
|
||||
_hide_equip_menu()
|
||||
state.try_wait_selected()
|
||||
if state.try_wait_selected():
|
||||
_play_ui_confirm()
|
||||
|
||||
|
||||
func _on_end_turn_pressed() -> void:
|
||||
if state.can_player_act():
|
||||
_play_ui_confirm()
|
||||
selected_skill_id = ""
|
||||
selected_item_id = ""
|
||||
_hide_tactic_menu()
|
||||
@@ -1119,6 +1274,7 @@ func _on_tactic_pressed() -> void:
|
||||
var selected := state.get_selected_unit()
|
||||
if selected.is_empty():
|
||||
return
|
||||
_play_ui_click()
|
||||
if tactic_menu != null and tactic_menu.visible:
|
||||
_hide_tactic_menu()
|
||||
else:
|
||||
@@ -1135,6 +1291,7 @@ func _on_item_pressed() -> void:
|
||||
var selected := state.get_selected_unit()
|
||||
if selected.is_empty():
|
||||
return
|
||||
_play_ui_click()
|
||||
if item_menu != null and item_menu.visible:
|
||||
_hide_item_menu()
|
||||
else:
|
||||
@@ -1153,6 +1310,7 @@ func _on_equip_pressed() -> void:
|
||||
return
|
||||
if selected.get("moved", false) or selected.get("acted", false):
|
||||
return
|
||||
_play_ui_click()
|
||||
if equip_menu != null and equip_menu.visible:
|
||||
_hide_equip_menu()
|
||||
else:
|
||||
@@ -1169,6 +1327,7 @@ func _on_equip_pressed() -> void:
|
||||
func _on_restart_pressed() -> void:
|
||||
if campaign_complete_screen:
|
||||
return
|
||||
_play_ui_confirm()
|
||||
if log_box != null:
|
||||
log_box.clear()
|
||||
battle_started = false
|
||||
@@ -1197,6 +1356,8 @@ func _load_pending_post_battle_choice() -> void:
|
||||
post_battle_dialogue_started = true
|
||||
battle_result_summary = campaign_state.get_pending_post_battle_choice_summary()
|
||||
state.battle_status = BattleState.STATUS_VICTORY
|
||||
last_announced_battle_status = BattleState.STATUS_VICTORY
|
||||
_play_bgm("menu")
|
||||
if briefing_panel != null:
|
||||
briefing_panel.visible = false
|
||||
if log_box != null:
|
||||
@@ -1206,6 +1367,7 @@ func _load_pending_post_battle_choice() -> void:
|
||||
func _load_scenario(scenario_id: String) -> void:
|
||||
campaign_complete_screen = false
|
||||
active_scenario_id = scenario_id
|
||||
last_announced_battle_status = ""
|
||||
var scenario_path := campaign_state.get_scenario_path(active_scenario_id)
|
||||
if scenario_path.is_empty():
|
||||
push_error("Missing scenario path for %s." % active_scenario_id)
|
||||
@@ -1234,6 +1396,7 @@ func _load_scenario(scenario_id: String) -> void:
|
||||
|
||||
|
||||
func _show_briefing() -> void:
|
||||
_play_bgm("menu")
|
||||
var briefing := state.get_briefing()
|
||||
if briefing.is_empty():
|
||||
_start_battle_from_briefing()
|
||||
@@ -1269,11 +1432,13 @@ func _show_briefing() -> void:
|
||||
|
||||
|
||||
func _on_begin_battle_pressed() -> void:
|
||||
_play_ui_confirm()
|
||||
_start_battle_from_briefing()
|
||||
|
||||
|
||||
func _start_battle_from_briefing() -> void:
|
||||
battle_started = true
|
||||
_play_bgm("battle")
|
||||
if briefing_panel != null:
|
||||
briefing_panel.visible = false
|
||||
_hide_shop_menu()
|
||||
@@ -1288,6 +1453,7 @@ func _start_battle_from_briefing() -> void:
|
||||
func _on_shop_pressed() -> void:
|
||||
if battle_started or _is_prebattle_prep_locked() or briefing_panel == null or not briefing_panel.visible:
|
||||
return
|
||||
_play_ui_click()
|
||||
if shop_menu != null and shop_menu.visible:
|
||||
_hide_shop_menu()
|
||||
else:
|
||||
@@ -1308,6 +1474,7 @@ func _hide_shop_menu() -> void:
|
||||
func _on_armory_pressed() -> void:
|
||||
if battle_started or _is_prebattle_prep_locked() or briefing_panel == null or not briefing_panel.visible:
|
||||
return
|
||||
_play_ui_click()
|
||||
if armory_menu != null and armory_menu.visible:
|
||||
_hide_armory_menu()
|
||||
else:
|
||||
@@ -1328,6 +1495,7 @@ func _hide_armory_menu() -> void:
|
||||
func _on_roster_pressed() -> void:
|
||||
if battle_started or _is_prebattle_prep_locked() or not state.has_deployment_roster() or briefing_panel == null or not briefing_panel.visible:
|
||||
return
|
||||
_play_ui_click()
|
||||
if roster_menu != null and roster_menu.visible:
|
||||
_hide_roster_menu()
|
||||
else:
|
||||
@@ -1348,6 +1516,7 @@ func _hide_roster_menu() -> void:
|
||||
func _on_formation_pressed() -> void:
|
||||
if battle_started or _is_prebattle_prep_locked() or briefing_panel == null or not briefing_panel.visible:
|
||||
return
|
||||
_play_ui_click()
|
||||
if formation_menu != null and formation_menu.visible:
|
||||
_hide_formation_menu()
|
||||
else:
|
||||
@@ -1434,6 +1603,7 @@ func _on_roster_unit_pressed(unit_id: String) -> void:
|
||||
return
|
||||
var next_deployed := not bool(unit.get("deployed", true))
|
||||
if state.try_set_unit_deployed(unit_id, next_deployed):
|
||||
_play_ui_confirm()
|
||||
if not state.get_unit(formation_unit_id).get("deployed", false):
|
||||
formation_unit_id = ""
|
||||
if not state.get_unit(armory_unit_id).get("deployed", false):
|
||||
@@ -1498,6 +1668,7 @@ func _format_cell_label(value) -> String:
|
||||
|
||||
|
||||
func _on_formation_unit_pressed(unit_id: String) -> void:
|
||||
_play_ui_click()
|
||||
formation_unit_id = unit_id
|
||||
_rebuild_formation_menu()
|
||||
_update_hud()
|
||||
@@ -1511,6 +1682,7 @@ func _handle_formation_board_click(screen_position: Vector2) -> void:
|
||||
if not state.is_inside(cell):
|
||||
return
|
||||
if state.try_set_prebattle_formation(formation_unit_id, cell):
|
||||
_play_ui_confirm()
|
||||
_rebuild_formation_menu()
|
||||
_update_hud()
|
||||
queue_redraw()
|
||||
@@ -1598,6 +1770,7 @@ func _format_armory_unit_button_text(unit: Dictionary) -> String:
|
||||
|
||||
|
||||
func _on_armory_unit_pressed(unit_id: String) -> void:
|
||||
_play_ui_click()
|
||||
armory_unit_id = unit_id
|
||||
_rebuild_armory_menu()
|
||||
_update_hud()
|
||||
@@ -1613,6 +1786,7 @@ func _on_armory_equip_pressed(item_id: String) -> void:
|
||||
if campaign_state.try_save_prebattle_loadout(state.get_player_roster_snapshot(), state.get_inventory_snapshot()):
|
||||
_on_log_added("Saved pre-battle equipment.")
|
||||
else:
|
||||
_play_ui_cancel()
|
||||
_on_log_added("Could not save pre-battle equipment.")
|
||||
_load_scenario(active_scenario_id)
|
||||
_show_briefing()
|
||||
@@ -1701,6 +1875,7 @@ func _on_shop_item_pressed(item_id: String) -> void:
|
||||
state.set_inventory_snapshot(campaign_state.get_inventory_snapshot())
|
||||
_on_log_added("Bought %s for %d gold." % [item_name, price])
|
||||
else:
|
||||
_play_ui_cancel()
|
||||
_on_log_added("Could not buy %s." % item_name)
|
||||
_rebuild_shop_menu()
|
||||
_update_hud()
|
||||
@@ -1775,6 +1950,7 @@ func _on_post_battle_choice_pressed(choice: Dictionary) -> void:
|
||||
return
|
||||
var scenario_id := str(battle_result_summary.get("scenario_id", state.battle_id))
|
||||
if campaign_state.try_apply_post_battle_choice(choice, scenario_id):
|
||||
_play_ui_confirm()
|
||||
battle_result_summary["choice_applied"] = true
|
||||
battle_result_summary["saved"] = true
|
||||
battle_result_summary["choice_label"] = str(choice.get("label", choice.get("id", "")))
|
||||
@@ -1782,6 +1958,7 @@ func _on_post_battle_choice_pressed(choice: Dictionary) -> void:
|
||||
_append_unique_result_values("left_officers", choice.get("leave_officers", []))
|
||||
_on_log_added("Campaign choice saved: %s." % battle_result_summary["choice_label"])
|
||||
else:
|
||||
_play_ui_cancel()
|
||||
_on_log_added("Could not save campaign choice.")
|
||||
_rebuild_result_choices()
|
||||
_update_hud()
|
||||
@@ -1959,6 +2136,7 @@ func _on_next_battle_pressed() -> void:
|
||||
var next_scenario_id := str(battle_result_summary.get("next_scenario_id", ""))
|
||||
if next_scenario_id.is_empty():
|
||||
return
|
||||
_play_ui_confirm()
|
||||
if log_box != null:
|
||||
log_box.clear()
|
||||
_load_scenario(next_scenario_id)
|
||||
@@ -1968,6 +2146,7 @@ func _on_next_battle_pressed() -> void:
|
||||
|
||||
func _show_campaign_complete() -> void:
|
||||
campaign_complete_screen = true
|
||||
_play_bgm("menu")
|
||||
battle_started = false
|
||||
battle_result_applied = true
|
||||
battle_result_summary.clear()
|
||||
@@ -1994,6 +2173,7 @@ func _show_campaign_complete() -> void:
|
||||
|
||||
|
||||
func _on_new_campaign_pressed() -> void:
|
||||
_play_ui_confirm()
|
||||
if log_box != null:
|
||||
log_box.clear()
|
||||
if not campaign_state.reset_save(campaign_state.get_start_scenario_id()):
|
||||
@@ -2116,6 +2296,7 @@ func _format_tactic_range_text(skill: Dictionary) -> String:
|
||||
|
||||
|
||||
func _on_tactic_skill_pressed(skill_id: String) -> void:
|
||||
_play_ui_confirm()
|
||||
selected_skill_id = skill_id
|
||||
selected_item_id = ""
|
||||
_hide_tactic_menu()
|
||||
@@ -2127,6 +2308,7 @@ func _on_tactic_skill_pressed(skill_id: String) -> void:
|
||||
|
||||
|
||||
func _on_tactic_cancel_pressed() -> void:
|
||||
_play_ui_cancel()
|
||||
selected_skill_id = ""
|
||||
_hide_tactic_menu()
|
||||
_refresh_ranges()
|
||||
@@ -2228,6 +2410,7 @@ func _format_item_effect_text(item: Dictionary) -> String:
|
||||
|
||||
|
||||
func _on_item_selected_pressed(item_id: String) -> void:
|
||||
_play_ui_confirm()
|
||||
selected_item_id = item_id
|
||||
selected_skill_id = ""
|
||||
_hide_item_menu()
|
||||
@@ -2239,6 +2422,7 @@ func _on_item_selected_pressed(item_id: String) -> void:
|
||||
|
||||
|
||||
func _on_item_cancel_pressed() -> void:
|
||||
_play_ui_cancel()
|
||||
selected_item_id = ""
|
||||
_hide_item_menu()
|
||||
_refresh_ranges()
|
||||
@@ -2355,6 +2539,7 @@ func _on_equip_item_pressed(item_id: String) -> void:
|
||||
|
||||
|
||||
func _on_equip_cancel_pressed() -> void:
|
||||
_play_ui_cancel()
|
||||
_hide_equip_menu()
|
||||
_update_hud()
|
||||
queue_redraw()
|
||||
|
||||
Reference in New Issue
Block a user