Add post-move action menu flow
This commit is contained in:
@@ -639,7 +639,7 @@ func clear_selection() -> void:
|
|||||||
_notify_changed()
|
_notify_changed()
|
||||||
|
|
||||||
|
|
||||||
func try_move_selected(cell: Vector2i) -> bool:
|
func try_move_selected(cell: Vector2i, defer_reach_events := false) -> bool:
|
||||||
var unit := get_selected_unit()
|
var unit := get_selected_unit()
|
||||||
if unit.is_empty() or battle_status != STATUS_ACTIVE:
|
if unit.is_empty() or battle_status != STATUS_ACTIVE:
|
||||||
return false
|
return false
|
||||||
@@ -660,9 +660,40 @@ func try_move_selected(cell: Vector2i) -> bool:
|
|||||||
unit["moved"] = true
|
unit["moved"] = true
|
||||||
unit_motion_requested.emit(str(unit.get("id", "")), from_cell, cell)
|
unit_motion_requested.emit(str(unit.get("id", "")), from_cell, cell)
|
||||||
_emit_log("%s moved to %s." % [unit["name"], _format_cell(cell)])
|
_emit_log("%s moved to %s." % [unit["name"], _format_cell(cell)])
|
||||||
|
if not defer_reach_events:
|
||||||
|
_run_events("unit_reaches_tile", str(unit.get("team", "")), turn_number, unit)
|
||||||
|
_check_battle_status()
|
||||||
|
_notify_changed()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func commit_pending_move_events(unit_id: String) -> bool:
|
||||||
|
var unit := get_unit(unit_id)
|
||||||
|
if unit.is_empty() or battle_status != STATUS_ACTIVE:
|
||||||
|
return false
|
||||||
_run_events("unit_reaches_tile", str(unit.get("team", "")), turn_number, unit)
|
_run_events("unit_reaches_tile", str(unit.get("team", "")), turn_number, unit)
|
||||||
_check_battle_status()
|
_check_battle_status()
|
||||||
_notify_changed()
|
_notify_changed()
|
||||||
|
return battle_status == STATUS_ACTIVE
|
||||||
|
|
||||||
|
|
||||||
|
func cancel_pending_move(unit_id: String, from_cell: Vector2i, to_cell: Vector2i) -> bool:
|
||||||
|
var unit := get_unit(unit_id)
|
||||||
|
if unit.is_empty() or battle_status != STATUS_ACTIVE:
|
||||||
|
return false
|
||||||
|
if bool(unit.get("acted", false)) or not bool(unit.get("moved", false)):
|
||||||
|
return false
|
||||||
|
if unit.get("pos", Vector2i(-1, -1)) != to_cell:
|
||||||
|
return false
|
||||||
|
var blocker := get_unit_at(from_cell)
|
||||||
|
if not blocker.is_empty() and str(blocker.get("id", "")) != unit_id:
|
||||||
|
return false
|
||||||
|
|
||||||
|
unit["pos"] = from_cell
|
||||||
|
unit["moved"] = false
|
||||||
|
unit_motion_requested.emit(unit_id, to_cell, from_cell)
|
||||||
|
_emit_log("%s cancels movement." % unit["name"])
|
||||||
|
_notify_changed()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ const DIALOGUE_COLUMN_SIZE := Vector2(820, 186)
|
|||||||
const DIALOGUE_TEXT_SIZE := Vector2(800, 104)
|
const DIALOGUE_TEXT_SIZE := Vector2(800, 104)
|
||||||
const HUD_UNIT_PORTRAIT_SIZE := Vector2(92, 104)
|
const HUD_UNIT_PORTRAIT_SIZE := Vector2(92, 104)
|
||||||
const HUD_UNIT_PORTRAIT_STACK_SIZE := Vector2(88, 100)
|
const HUD_UNIT_PORTRAIT_STACK_SIZE := Vector2(88, 100)
|
||||||
|
const POST_MOVE_MENU_SIZE := Vector2(228, 120)
|
||||||
|
const POST_MOVE_MENU_OFFSET := Vector2(20, -34)
|
||||||
|
|
||||||
var state: BattleState = BattleStateScript.new()
|
var state: BattleState = BattleStateScript.new()
|
||||||
var campaign_state: CampaignState = CampaignStateScript.new()
|
var campaign_state: CampaignState = CampaignStateScript.new()
|
||||||
@@ -70,6 +72,10 @@ var item_cells: Array[Vector2i] = []
|
|||||||
var threat_cells: Array[Vector2i] = []
|
var threat_cells: Array[Vector2i] = []
|
||||||
var selected_skill_id := ""
|
var selected_skill_id := ""
|
||||||
var selected_item_id := ""
|
var selected_item_id := ""
|
||||||
|
var basic_attack_targeting := false
|
||||||
|
var pending_move_unit_id := ""
|
||||||
|
var pending_move_from_cell := Vector2i(-1, -1)
|
||||||
|
var pending_move_to_cell := Vector2i(-1, -1)
|
||||||
var show_threat_overlay := false
|
var show_threat_overlay := false
|
||||||
var active_scenario_id := ""
|
var active_scenario_id := ""
|
||||||
var battle_started := false
|
var battle_started := false
|
||||||
@@ -158,6 +164,13 @@ var item_list: VBoxContainer
|
|||||||
var equip_button: Button
|
var equip_button: Button
|
||||||
var equip_menu: VBoxContainer
|
var equip_menu: VBoxContainer
|
||||||
var equip_list: VBoxContainer
|
var equip_list: VBoxContainer
|
||||||
|
var post_move_menu: PanelContainer
|
||||||
|
var post_move_title_label: Label
|
||||||
|
var post_move_attack_button: Button
|
||||||
|
var post_move_tactic_button: Button
|
||||||
|
var post_move_item_button: Button
|
||||||
|
var post_move_wait_button: Button
|
||||||
|
var post_move_cancel_button: Button
|
||||||
var threat_button: Button
|
var threat_button: Button
|
||||||
var restart_button: Button
|
var restart_button: Button
|
||||||
var new_campaign_button: Button
|
var new_campaign_button: Button
|
||||||
@@ -220,6 +233,8 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
queue_redraw()
|
queue_redraw()
|
||||||
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||||
_handle_board_click(event.position)
|
_handle_board_click(event.position)
|
||||||
|
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
|
||||||
|
_handle_cancel_input()
|
||||||
elif event is InputEventKey and event.pressed and not event.echo:
|
elif event is InputEventKey and event.pressed and not event.echo:
|
||||||
_handle_key(event)
|
_handle_key(event)
|
||||||
|
|
||||||
@@ -429,6 +444,58 @@ func _create_hud() -> void:
|
|||||||
equip_list.add_theme_constant_override("separation", 4)
|
equip_list.add_theme_constant_override("separation", 4)
|
||||||
equip_menu.add_child(equip_list)
|
equip_menu.add_child(equip_list)
|
||||||
|
|
||||||
|
post_move_menu = PanelContainer.new()
|
||||||
|
post_move_menu.visible = false
|
||||||
|
post_move_menu.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
post_move_menu.custom_minimum_size = POST_MOVE_MENU_SIZE
|
||||||
|
post_move_menu.size = POST_MOVE_MENU_SIZE
|
||||||
|
root.add_child(post_move_menu)
|
||||||
|
|
||||||
|
var post_move_column := VBoxContainer.new()
|
||||||
|
post_move_column.add_theme_constant_override("separation", 4)
|
||||||
|
post_move_menu.add_child(post_move_column)
|
||||||
|
|
||||||
|
post_move_title_label = Label.new()
|
||||||
|
post_move_title_label.text = "Choose Action"
|
||||||
|
post_move_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
post_move_column.add_child(post_move_title_label)
|
||||||
|
|
||||||
|
var post_move_grid := GridContainer.new()
|
||||||
|
post_move_grid.columns = 2
|
||||||
|
post_move_grid.add_theme_constant_override("h_separation", 6)
|
||||||
|
post_move_grid.add_theme_constant_override("v_separation", 4)
|
||||||
|
post_move_column.add_child(post_move_grid)
|
||||||
|
|
||||||
|
post_move_attack_button = Button.new()
|
||||||
|
post_move_attack_button.text = "Attack"
|
||||||
|
post_move_attack_button.tooltip_text = "Choose an enemy target from this unit's current position."
|
||||||
|
post_move_attack_button.pressed.connect(_on_post_move_attack_pressed)
|
||||||
|
post_move_grid.add_child(post_move_attack_button)
|
||||||
|
|
||||||
|
post_move_tactic_button = Button.new()
|
||||||
|
post_move_tactic_button.text = "Tactic"
|
||||||
|
post_move_tactic_button.tooltip_text = "Open tactics after this move."
|
||||||
|
post_move_tactic_button.pressed.connect(_on_post_move_tactic_pressed)
|
||||||
|
post_move_grid.add_child(post_move_tactic_button)
|
||||||
|
|
||||||
|
post_move_item_button = Button.new()
|
||||||
|
post_move_item_button.text = "Item"
|
||||||
|
post_move_item_button.tooltip_text = "Open consumable items after this move."
|
||||||
|
post_move_item_button.pressed.connect(_on_post_move_item_pressed)
|
||||||
|
post_move_grid.add_child(post_move_item_button)
|
||||||
|
|
||||||
|
post_move_wait_button = Button.new()
|
||||||
|
post_move_wait_button.text = "Wait"
|
||||||
|
post_move_wait_button.tooltip_text = "Commit this move and end the unit's action."
|
||||||
|
post_move_wait_button.pressed.connect(_on_post_move_wait_pressed)
|
||||||
|
post_move_grid.add_child(post_move_wait_button)
|
||||||
|
|
||||||
|
post_move_cancel_button = Button.new()
|
||||||
|
post_move_cancel_button.text = "Cancel Move"
|
||||||
|
post_move_cancel_button.tooltip_text = "Return to the starting cell."
|
||||||
|
post_move_cancel_button.pressed.connect(_on_post_move_cancel_pressed)
|
||||||
|
post_move_column.add_child(post_move_cancel_button)
|
||||||
|
|
||||||
log_box = RichTextLabel.new()
|
log_box = RichTextLabel.new()
|
||||||
log_box.custom_minimum_size = Vector2(420, 70)
|
log_box.custom_minimum_size = Vector2(420, 70)
|
||||||
log_box.fit_content = false
|
log_box.fit_content = false
|
||||||
@@ -938,50 +1005,173 @@ func _handle_board_click(screen_position: Vector2) -> void:
|
|||||||
if _can_select(clicked_unit):
|
if _can_select(clicked_unit):
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_clear_pending_move_state()
|
||||||
state.select_unit(clicked_unit["id"])
|
state.select_unit(clicked_unit["id"])
|
||||||
return
|
return
|
||||||
|
|
||||||
if not selected_item_id.is_empty():
|
if not selected_item_id.is_empty():
|
||||||
if state.try_use_selected_item_on_cell(selected_item_id, cell):
|
if item_cells.has(cell) and _commit_pending_move_before_action() and state.try_use_selected_item_on_cell(selected_item_id, cell):
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_clear_pending_move_state()
|
||||||
|
return
|
||||||
|
if _has_pending_move():
|
||||||
return
|
return
|
||||||
if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", "") and _can_select(clicked_unit):
|
if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", "") and _can_select(clicked_unit):
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
state.select_unit(clicked_unit["id"])
|
state.select_unit(clicked_unit["id"])
|
||||||
return
|
return
|
||||||
|
|
||||||
if not selected_skill_id.is_empty():
|
if not selected_skill_id.is_empty():
|
||||||
if state.try_cast_selected_skill(selected_skill_id, cell):
|
if skill_cells.has(cell) and _commit_pending_move_before_action() and state.try_cast_selected_skill(selected_skill_id, cell):
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_clear_pending_move_state()
|
||||||
|
return
|
||||||
|
if _has_pending_move():
|
||||||
return
|
return
|
||||||
if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", "") and _can_select(clicked_unit):
|
if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", "") and _can_select(clicked_unit):
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
state.select_unit(clicked_unit["id"])
|
state.select_unit(clicked_unit["id"])
|
||||||
return
|
return
|
||||||
|
|
||||||
if not clicked_unit.is_empty() and clicked_unit.get("team", "") != selected_unit.get("team", ""):
|
if not clicked_unit.is_empty() and clicked_unit.get("team", "") != selected_unit.get("team", ""):
|
||||||
if state.try_attack_selected(clicked_unit["id"]):
|
if _has_pending_move() and not basic_attack_targeting:
|
||||||
|
return
|
||||||
|
var target_cell: Vector2i = clicked_unit.get("pos", cell)
|
||||||
|
if attack_cells.has(target_cell) and _commit_pending_move_before_action() and state.try_attack_selected(clicked_unit["id"]):
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_clear_pending_move_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", ""):
|
if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", ""):
|
||||||
|
if _has_pending_move():
|
||||||
|
return
|
||||||
if _can_select(clicked_unit):
|
if _can_select(clicked_unit):
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_clear_pending_move_state()
|
||||||
state.select_unit(clicked_unit["id"])
|
state.select_unit(clicked_unit["id"])
|
||||||
return
|
return
|
||||||
|
|
||||||
if state.try_move_selected(cell):
|
var selected_start_cell: Vector2i = selected_unit.get("pos", Vector2i(-1, -1))
|
||||||
|
if not _has_pending_move() and state.try_move_selected(cell, true):
|
||||||
|
pending_move_unit_id = str(selected_unit.get("id", ""))
|
||||||
|
pending_move_from_cell = selected_start_cell
|
||||||
|
pending_move_to_cell = cell
|
||||||
|
selected_skill_id = ""
|
||||||
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_hide_tactic_menu()
|
||||||
|
_hide_item_menu()
|
||||||
|
_hide_equip_menu()
|
||||||
|
_refresh_ranges()
|
||||||
|
_show_post_move_menu()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
return
|
return
|
||||||
|
|
||||||
if _can_select(clicked_unit):
|
if _can_select(clicked_unit):
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_clear_pending_move_state()
|
||||||
state.select_unit(clicked_unit["id"])
|
state.select_unit(clicked_unit["id"])
|
||||||
|
|
||||||
|
|
||||||
|
func _has_pending_move() -> bool:
|
||||||
|
return (
|
||||||
|
not pending_move_unit_id.is_empty()
|
||||||
|
and pending_move_from_cell != Vector2i(-1, -1)
|
||||||
|
and pending_move_to_cell != Vector2i(-1, -1)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _clear_pending_move_state(hide_menu := true) -> void:
|
||||||
|
pending_move_unit_id = ""
|
||||||
|
pending_move_from_cell = Vector2i(-1, -1)
|
||||||
|
pending_move_to_cell = Vector2i(-1, -1)
|
||||||
|
basic_attack_targeting = false
|
||||||
|
if hide_menu:
|
||||||
|
_hide_post_move_menu()
|
||||||
|
|
||||||
|
|
||||||
|
func _commit_pending_move_before_action() -> bool:
|
||||||
|
if not _has_pending_move():
|
||||||
|
return true
|
||||||
|
var unit_id := pending_move_unit_id
|
||||||
|
_clear_pending_move_state()
|
||||||
|
var still_active := state.commit_pending_move_events(unit_id)
|
||||||
|
return still_active and state.can_player_act() and not state.get_selected_unit().is_empty() and not _is_input_locked()
|
||||||
|
|
||||||
|
|
||||||
|
func _cancel_pending_move() -> bool:
|
||||||
|
if not _has_pending_move():
|
||||||
|
return false
|
||||||
|
var unit_id := pending_move_unit_id
|
||||||
|
var from_cell := pending_move_from_cell
|
||||||
|
var to_cell := pending_move_to_cell
|
||||||
|
selected_skill_id = ""
|
||||||
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_hide_tactic_menu()
|
||||||
|
_hide_item_menu()
|
||||||
|
_hide_equip_menu()
|
||||||
|
_hide_post_move_menu()
|
||||||
|
if state.cancel_pending_move(unit_id, from_cell, to_cell):
|
||||||
|
_clear_pending_move_state()
|
||||||
|
_play_ui_cancel()
|
||||||
|
_refresh_ranges()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_cancel_input() -> bool:
|
||||||
|
if _has_pending_move():
|
||||||
|
return _cancel_pending_move()
|
||||||
|
if tactic_menu != null and tactic_menu.visible:
|
||||||
|
_play_ui_cancel()
|
||||||
|
_hide_tactic_menu()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
|
return true
|
||||||
|
if item_menu != null and item_menu.visible:
|
||||||
|
_play_ui_cancel()
|
||||||
|
_hide_item_menu()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
|
return true
|
||||||
|
if equip_menu != null and equip_menu.visible:
|
||||||
|
_play_ui_cancel()
|
||||||
|
_hide_equip_menu()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
|
return true
|
||||||
|
if not selected_skill_id.is_empty() or not selected_item_id.is_empty() or basic_attack_targeting:
|
||||||
|
_play_ui_cancel()
|
||||||
|
selected_skill_id = ""
|
||||||
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_refresh_ranges()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
|
return true
|
||||||
|
if not state.get_selected_unit().is_empty():
|
||||||
|
_play_ui_cancel()
|
||||||
|
state.clear_selection()
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
func _handle_key(event: InputEventKey) -> void:
|
func _handle_key(event: InputEventKey) -> void:
|
||||||
if event.keycode == KEY_ENTER and briefing_panel != null and briefing_panel.visible:
|
if event.keycode == KEY_ENTER and briefing_panel != null and briefing_panel.visible:
|
||||||
if _is_prep_menu_visible():
|
if _is_prep_menu_visible():
|
||||||
@@ -1006,21 +1196,10 @@ func _handle_key(event: InputEventKey) -> void:
|
|||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
elif equip_menu != null and equip_menu.visible:
|
elif equip_menu != null and equip_menu.visible:
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
elif selected_skill_id.is_empty() and selected_item_id.is_empty():
|
|
||||||
state.clear_selection()
|
|
||||||
else:
|
else:
|
||||||
selected_skill_id = ""
|
_handle_cancel_input()
|
||||||
selected_item_id = ""
|
|
||||||
_refresh_ranges()
|
|
||||||
_update_hud()
|
|
||||||
queue_redraw()
|
|
||||||
elif event.keycode == KEY_SPACE and not _is_input_locked():
|
elif event.keycode == KEY_SPACE and not _is_input_locked():
|
||||||
selected_skill_id = ""
|
_on_end_turn_pressed()
|
||||||
selected_item_id = ""
|
|
||||||
_hide_tactic_menu()
|
|
||||||
_hide_item_menu()
|
|
||||||
_hide_equip_menu()
|
|
||||||
state.end_player_turn()
|
|
||||||
elif event.keycode == KEY_R and not _is_prep_menu_visible():
|
elif event.keycode == KEY_R and not _is_prep_menu_visible():
|
||||||
_on_restart_pressed()
|
_on_restart_pressed()
|
||||||
elif event.keycode == KEY_T and not _is_input_locked():
|
elif event.keycode == KEY_T and not _is_input_locked():
|
||||||
@@ -1346,6 +1525,11 @@ func _draw_overlays() -> void:
|
|||||||
for cell in move_cells:
|
for cell in move_cells:
|
||||||
draw_rect(_rect_for_cell(cell), Color(0.16, 0.55, 0.95, 0.28))
|
draw_rect(_rect_for_cell(cell), Color(0.16, 0.55, 0.95, 0.28))
|
||||||
|
|
||||||
|
if _has_pending_move() and state.is_inside(pending_move_to_cell):
|
||||||
|
var pending_rect := _rect_for_cell(pending_move_to_cell)
|
||||||
|
draw_rect(pending_rect.grow(-4.0), Color(1.0, 0.82, 0.28, 0.32))
|
||||||
|
draw_rect(pending_rect.grow(-7.0), Color(1.0, 0.88, 0.36, 0.86), false, 2.0)
|
||||||
|
|
||||||
for cell in attack_cells:
|
for cell in attack_cells:
|
||||||
draw_rect(_rect_for_cell(cell), Color(0.95, 0.22, 0.18, 0.24))
|
draw_rect(_rect_for_cell(cell), Color(0.95, 0.22, 0.18, 0.24))
|
||||||
|
|
||||||
@@ -1847,6 +2031,8 @@ func _target_preview_badge() -> Dictionary:
|
|||||||
return _skill_target_preview_badge(selected, target)
|
return _skill_target_preview_badge(selected, target)
|
||||||
if not selected_item_id.is_empty():
|
if not selected_item_id.is_empty():
|
||||||
return _item_target_preview_badge(selected, target)
|
return _item_target_preview_badge(selected, target)
|
||||||
|
if _has_pending_move() and not basic_attack_targeting:
|
||||||
|
return {}
|
||||||
var attack_badge := _attack_target_preview_badge(selected, target)
|
var attack_badge := _attack_target_preview_badge(selected, target)
|
||||||
if not attack_badge.is_empty():
|
if not attack_badge.is_empty():
|
||||||
return attack_badge
|
return attack_badge
|
||||||
@@ -1942,6 +2128,8 @@ func _item_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dic
|
|||||||
func _hover_intent_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
|
func _hover_intent_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
|
||||||
if selected.is_empty():
|
if selected.is_empty():
|
||||||
return {}
|
return {}
|
||||||
|
if _has_pending_move():
|
||||||
|
return {}
|
||||||
if not state.can_player_act():
|
if not state.can_player_act():
|
||||||
return {}
|
return {}
|
||||||
if not bool(selected.get("controllable", true)) or bool(selected.get("acted", false)):
|
if not bool(selected.get("controllable", true)) or bool(selected.get("acted", false)):
|
||||||
@@ -2039,20 +2227,24 @@ func _refresh_ranges() -> void:
|
|||||||
if selected.is_empty():
|
if selected.is_empty():
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
return
|
return
|
||||||
move_cells = state.get_movement_range(selected["id"])
|
move_cells = state.get_movement_range(selected["id"])
|
||||||
if not selected_skill_id.is_empty():
|
if not selected_skill_id.is_empty():
|
||||||
skill_cells = state.get_skill_cells(selected["id"], selected_skill_id)
|
skill_cells = state.get_skill_cells(selected["id"], selected_skill_id)
|
||||||
if skill_cells.is_empty():
|
if skill_cells.is_empty():
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
attack_cells = state.get_attack_cells(selected["id"])
|
if not _has_pending_move() or basic_attack_targeting:
|
||||||
|
attack_cells = state.get_attack_cells(selected["id"])
|
||||||
elif not selected_item_id.is_empty():
|
elif not selected_item_id.is_empty():
|
||||||
item_cells = state.get_item_target_cells(selected["id"], selected_item_id)
|
item_cells = state.get_item_target_cells(selected["id"], selected_item_id)
|
||||||
if item_cells.is_empty():
|
if item_cells.is_empty():
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
attack_cells = state.get_attack_cells(selected["id"])
|
if not _has_pending_move() or basic_attack_targeting:
|
||||||
|
attack_cells = state.get_attack_cells(selected["id"])
|
||||||
else:
|
else:
|
||||||
attack_cells = state.get_attack_cells(selected["id"])
|
if not _has_pending_move() or basic_attack_targeting:
|
||||||
|
attack_cells = state.get_attack_cells(selected["id"])
|
||||||
|
|
||||||
|
|
||||||
func _update_hud() -> void:
|
func _update_hud() -> void:
|
||||||
@@ -2098,6 +2290,7 @@ func _update_hud() -> void:
|
|||||||
_update_end_turn_button()
|
_update_end_turn_button()
|
||||||
restart_button.disabled = campaign_complete_screen
|
restart_button.disabled = campaign_complete_screen
|
||||||
_update_threat_button()
|
_update_threat_button()
|
||||||
|
_update_post_move_menu()
|
||||||
|
|
||||||
|
|
||||||
func _format_objective_hud_text() -> String:
|
func _format_objective_hud_text() -> String:
|
||||||
@@ -2666,6 +2859,7 @@ func _on_state_changed() -> void:
|
|||||||
if state.get_selected_unit().is_empty():
|
if state.get_selected_unit().is_empty():
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
@@ -2914,9 +3108,11 @@ func _show_next_dialogue() -> void:
|
|||||||
return
|
return
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
|
_hide_post_move_menu()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
active_dialogue_lines = dialogue_queue.pop_front()
|
active_dialogue_lines = dialogue_queue.pop_front()
|
||||||
active_dialogue_index = 0
|
active_dialogue_index = 0
|
||||||
@@ -3066,18 +3262,29 @@ func _is_dialogue_visible() -> bool:
|
|||||||
func _on_wait_pressed() -> void:
|
func _on_wait_pressed() -> void:
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
if state.try_wait_selected():
|
if _commit_pending_move_before_action() and state.try_wait_selected():
|
||||||
_play_ui_confirm()
|
_play_ui_confirm()
|
||||||
|
|
||||||
|
|
||||||
func _on_end_turn_pressed() -> void:
|
func _on_end_turn_pressed() -> void:
|
||||||
if state.can_player_act():
|
if state.can_player_act():
|
||||||
_play_ui_confirm()
|
_play_ui_confirm()
|
||||||
|
if _has_pending_move():
|
||||||
|
selected_skill_id = ""
|
||||||
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_hide_tactic_menu()
|
||||||
|
_hide_item_menu()
|
||||||
|
_hide_equip_menu()
|
||||||
|
if not (_commit_pending_move_before_action() and state.try_wait_selected()):
|
||||||
|
return
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
@@ -3091,6 +3298,7 @@ func _on_tactic_pressed() -> void:
|
|||||||
if state.is_unit_skill_sealed(selected["id"]):
|
if state.is_unit_skill_sealed(selected["id"]):
|
||||||
_play_ui_cancel()
|
_play_ui_cancel()
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
@@ -3099,8 +3307,12 @@ func _on_tactic_pressed() -> void:
|
|||||||
_play_ui_click()
|
_play_ui_click()
|
||||||
if tactic_menu != null and tactic_menu.visible:
|
if tactic_menu != null and tactic_menu.visible:
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
|
if _has_pending_move():
|
||||||
|
_show_post_move_menu()
|
||||||
else:
|
else:
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_hide_post_move_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
_show_tactic_menu(selected)
|
_show_tactic_menu(selected)
|
||||||
@@ -3116,8 +3328,12 @@ func _on_item_pressed() -> void:
|
|||||||
_play_ui_click()
|
_play_ui_click()
|
||||||
if item_menu != null and item_menu.visible:
|
if item_menu != null and item_menu.visible:
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
|
if _has_pending_move():
|
||||||
|
_show_post_move_menu()
|
||||||
else:
|
else:
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_hide_post_move_menu()
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
_show_item_menu(selected)
|
_show_item_menu(selected)
|
||||||
@@ -3138,8 +3354,10 @@ func _on_equip_pressed() -> void:
|
|||||||
else:
|
else:
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
|
_hide_post_move_menu()
|
||||||
_show_equip_menu(selected)
|
_show_equip_menu(selected)
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
@@ -3172,6 +3390,7 @@ func _on_restart_pressed() -> void:
|
|||||||
shop_sell_mode = false
|
shop_sell_mode = false
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
armory_unit_id = ""
|
armory_unit_id = ""
|
||||||
formation_unit_id = ""
|
formation_unit_id = ""
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
@@ -3216,6 +3435,7 @@ func _load_scenario(scenario_id: String) -> void:
|
|||||||
unit_motion_by_unit.clear()
|
unit_motion_by_unit.clear()
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
@@ -3358,6 +3578,7 @@ func _show_briefing() -> void:
|
|||||||
_hide_roster_menu()
|
_hide_roster_menu()
|
||||||
_hide_formation_menu()
|
_hide_formation_menu()
|
||||||
_hide_save_menu()
|
_hide_save_menu()
|
||||||
|
_clear_pending_move_state()
|
||||||
var prep_locked := _is_prebattle_prep_locked()
|
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()
|
||||||
@@ -3392,6 +3613,7 @@ func _start_battle_from_briefing() -> void:
|
|||||||
_hide_roster_menu()
|
_hide_roster_menu()
|
||||||
_hide_formation_menu()
|
_hide_formation_menu()
|
||||||
_hide_save_menu()
|
_hide_save_menu()
|
||||||
|
_clear_pending_move_state()
|
||||||
state.run_battle_begin_events()
|
state.run_battle_begin_events()
|
||||||
_show_next_dialogue()
|
_show_next_dialogue()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
@@ -3914,6 +4136,7 @@ func _reload_from_campaign_state() -> void:
|
|||||||
unit_motion_by_unit.clear()
|
unit_motion_by_unit.clear()
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
armory_unit_id = ""
|
armory_unit_id = ""
|
||||||
formation_unit_id = ""
|
formation_unit_id = ""
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
@@ -4497,6 +4720,7 @@ func _try_show_post_battle_dialogue() -> bool:
|
|||||||
dialogue_queue.clear()
|
dialogue_queue.clear()
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
@@ -4834,6 +5058,7 @@ func _show_campaign_complete() -> void:
|
|||||||
item_cells.clear()
|
item_cells.clear()
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
@@ -4870,6 +5095,7 @@ func _on_new_campaign_pressed() -> void:
|
|||||||
post_battle_dialogue_started = false
|
post_battle_dialogue_started = false
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
_clear_pending_move_state()
|
||||||
formation_unit_id = ""
|
formation_unit_id = ""
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
@@ -4885,6 +5111,119 @@ func _on_new_campaign_pressed() -> void:
|
|||||||
_update_hud()
|
_update_hud()
|
||||||
|
|
||||||
|
|
||||||
|
func _show_post_move_menu() -> void:
|
||||||
|
if post_move_menu == null or not _has_pending_move():
|
||||||
|
return
|
||||||
|
_update_post_move_menu()
|
||||||
|
post_move_menu.visible = true
|
||||||
|
post_move_menu.move_to_front()
|
||||||
|
|
||||||
|
|
||||||
|
func _hide_post_move_menu() -> void:
|
||||||
|
if post_move_menu == null:
|
||||||
|
return
|
||||||
|
post_move_menu.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func _update_post_move_menu() -> void:
|
||||||
|
if post_move_menu == null:
|
||||||
|
return
|
||||||
|
if not _has_pending_move():
|
||||||
|
_hide_post_move_menu()
|
||||||
|
return
|
||||||
|
var selected := state.get_selected_unit()
|
||||||
|
if selected.is_empty() or str(selected.get("id", "")) != pending_move_unit_id or bool(selected.get("acted", false)):
|
||||||
|
_hide_post_move_menu()
|
||||||
|
return
|
||||||
|
_position_post_move_menu()
|
||||||
|
if post_move_title_label != null:
|
||||||
|
post_move_title_label.text = "%s: choose action" % str(selected.get("name", "Unit"))
|
||||||
|
if post_move_attack_button != null:
|
||||||
|
var attack_blocked := state.get_attack_cells(pending_move_unit_id).is_empty()
|
||||||
|
post_move_attack_button.disabled = attack_blocked
|
||||||
|
post_move_attack_button.tooltip_text = "No enemy is in range after this move." if attack_blocked else "Choose an enemy target from this unit's current position."
|
||||||
|
if post_move_tactic_button != null:
|
||||||
|
var skill_ids := state.get_skill_ids(pending_move_unit_id)
|
||||||
|
var skill_blocked := skill_ids.is_empty() or state.is_unit_skill_sealed(pending_move_unit_id) or state.get_first_usable_skill_id(pending_move_unit_id).is_empty()
|
||||||
|
post_move_tactic_button.disabled = skill_blocked
|
||||||
|
post_move_tactic_button.tooltip_text = "No usable tactic is available after this move." if skill_blocked else "Open tactics after this move."
|
||||||
|
if post_move_item_button != null:
|
||||||
|
var item_blocked := not _unit_has_usable_item_target(pending_move_unit_id)
|
||||||
|
post_move_item_button.disabled = item_blocked
|
||||||
|
post_move_item_button.tooltip_text = "No usable item target is in range." if item_blocked else "Open consumable items after this move."
|
||||||
|
if post_move_wait_button != null:
|
||||||
|
post_move_wait_button.disabled = false
|
||||||
|
post_move_wait_button.tooltip_text = "Commit this move and end the unit's action."
|
||||||
|
if post_move_cancel_button != null:
|
||||||
|
post_move_cancel_button.disabled = false
|
||||||
|
post_move_cancel_button.tooltip_text = "Right-click also returns to the starting cell."
|
||||||
|
|
||||||
|
|
||||||
|
func _position_post_move_menu() -> void:
|
||||||
|
if post_move_menu == null:
|
||||||
|
return
|
||||||
|
var cell_rect := _rect_for_cell(pending_move_to_cell)
|
||||||
|
var next_position := cell_rect.position + Vector2(TILE_SIZE, 0.0) + POST_MOVE_MENU_OFFSET
|
||||||
|
var board_size := Vector2(state.map_size.x, state.map_size.y) * TILE_SIZE
|
||||||
|
var board_min := BOARD_OFFSET + Vector2(4.0, 4.0)
|
||||||
|
var board_max := BOARD_OFFSET + board_size - POST_MOVE_MENU_SIZE - Vector2(4.0, 4.0)
|
||||||
|
board_max.x = maxf(board_min.x, board_max.x)
|
||||||
|
board_max.y = maxf(board_min.y, board_max.y)
|
||||||
|
next_position.x = clampf(next_position.x, board_min.x, board_max.x)
|
||||||
|
next_position.y = clampf(next_position.y, board_min.y, board_max.y)
|
||||||
|
post_move_menu.position = next_position
|
||||||
|
|
||||||
|
|
||||||
|
func _on_post_move_attack_pressed() -> void:
|
||||||
|
if not _has_pending_move():
|
||||||
|
return
|
||||||
|
if state.get_attack_cells(pending_move_unit_id).is_empty():
|
||||||
|
_play_ui_cancel()
|
||||||
|
return
|
||||||
|
_play_ui_click()
|
||||||
|
selected_skill_id = ""
|
||||||
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = true
|
||||||
|
_hide_tactic_menu()
|
||||||
|
_hide_item_menu()
|
||||||
|
_hide_equip_menu()
|
||||||
|
_hide_post_move_menu()
|
||||||
|
_refresh_ranges()
|
||||||
|
_update_hud()
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_post_move_tactic_pressed() -> void:
|
||||||
|
if not _has_pending_move():
|
||||||
|
return
|
||||||
|
_hide_post_move_menu()
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_on_tactic_pressed()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_post_move_item_pressed() -> void:
|
||||||
|
if not _has_pending_move():
|
||||||
|
return
|
||||||
|
_hide_post_move_menu()
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_on_item_pressed()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_post_move_wait_pressed() -> void:
|
||||||
|
selected_skill_id = ""
|
||||||
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
|
_hide_tactic_menu()
|
||||||
|
_hide_item_menu()
|
||||||
|
_hide_equip_menu()
|
||||||
|
if _commit_pending_move_before_action() and state.try_wait_selected():
|
||||||
|
_play_ui_confirm()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_post_move_cancel_pressed() -> void:
|
||||||
|
_cancel_pending_move()
|
||||||
|
|
||||||
|
|
||||||
func _set_action_button_state(button: Button, label: String, disabled: bool, tooltip := "") -> void:
|
func _set_action_button_state(button: Button, label: String, disabled: bool, tooltip := "") -> void:
|
||||||
if button == null:
|
if button == null:
|
||||||
return
|
return
|
||||||
@@ -5129,9 +5468,11 @@ func _on_tactic_skill_pressed(skill_id: String) -> void:
|
|||||||
_play_ui_confirm()
|
_play_ui_confirm()
|
||||||
selected_skill_id = skill_id
|
selected_skill_id = skill_id
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
|
_hide_post_move_menu()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
@@ -5140,7 +5481,10 @@ func _on_tactic_skill_pressed(skill_id: String) -> void:
|
|||||||
func _on_tactic_cancel_pressed() -> void:
|
func _on_tactic_cancel_pressed() -> void:
|
||||||
_play_ui_cancel()
|
_play_ui_cancel()
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
|
if _has_pending_move():
|
||||||
|
_show_post_move_menu()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
@@ -5252,9 +5596,11 @@ func _on_item_selected_pressed(item_id: String) -> void:
|
|||||||
_play_ui_confirm()
|
_play_ui_confirm()
|
||||||
selected_item_id = item_id
|
selected_item_id = item_id
|
||||||
selected_skill_id = ""
|
selected_skill_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
_hide_tactic_menu()
|
_hide_tactic_menu()
|
||||||
_hide_equip_menu()
|
_hide_equip_menu()
|
||||||
|
_hide_post_move_menu()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
@@ -5263,7 +5609,10 @@ func _on_item_selected_pressed(item_id: String) -> void:
|
|||||||
func _on_item_cancel_pressed() -> void:
|
func _on_item_cancel_pressed() -> void:
|
||||||
_play_ui_cancel()
|
_play_ui_cancel()
|
||||||
selected_item_id = ""
|
selected_item_id = ""
|
||||||
|
basic_attack_targeting = false
|
||||||
_hide_item_menu()
|
_hide_item_menu()
|
||||||
|
if _has_pending_move():
|
||||||
|
_show_post_move_menu()
|
||||||
_refresh_ranges()
|
_refresh_ranges()
|
||||||
_update_hud()
|
_update_hud()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|||||||
124
tools/smoke_post_move_action_flow.gd
Normal file
124
tools/smoke_post_move_action_flow.gd
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
|
||||||
|
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
var failures: Array[String] = []
|
||||||
|
_check_deferred_move_events(failures)
|
||||||
|
_check_scene_post_move_menu_flow(failures)
|
||||||
|
|
||||||
|
if failures.is_empty():
|
||||||
|
print("post move action flow smoke ok")
|
||||||
|
quit(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
for failure in failures:
|
||||||
|
push_error(failure)
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
|
||||||
|
func _check_deferred_move_events(failures: Array[String]) -> void:
|
||||||
|
var state = BattleStateScript.new()
|
||||||
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||||
|
failures.append("could not load opening battle for pending move state")
|
||||||
|
return
|
||||||
|
|
||||||
|
var from_cell := Vector2i(1, 3)
|
||||||
|
var to_cell := Vector2i(2, 3)
|
||||||
|
state.battle_events.append({
|
||||||
|
"id": "pending_move_gold",
|
||||||
|
"once": true,
|
||||||
|
"when": {
|
||||||
|
"type": "unit_reaches_tile",
|
||||||
|
"unit_ids": ["cao_cao"],
|
||||||
|
"pos": [to_cell.x, to_cell.y]
|
||||||
|
},
|
||||||
|
"actions": [{"type": "grant_gold", "amount": 77}]
|
||||||
|
})
|
||||||
|
|
||||||
|
if not state.select_unit("cao_cao"):
|
||||||
|
failures.append("could not select Cao Cao for pending move state")
|
||||||
|
return
|
||||||
|
if not state.try_move_selected(to_cell, true):
|
||||||
|
failures.append("tentative move should succeed")
|
||||||
|
return
|
||||||
|
|
||||||
|
var cao_cao: Dictionary = state.get_unit("cao_cao")
|
||||||
|
if cao_cao.get("pos", Vector2i.ZERO) != to_cell:
|
||||||
|
failures.append("tentative move should update position")
|
||||||
|
if not bool(cao_cao.get("moved", false)) or bool(cao_cao.get("acted", false)):
|
||||||
|
failures.append("tentative move should mark moved but not acted")
|
||||||
|
if state.get_battle_gold_reward() != 0:
|
||||||
|
failures.append("tentative move should defer unit_reaches_tile rewards")
|
||||||
|
|
||||||
|
if not state.cancel_pending_move("cao_cao", from_cell, to_cell):
|
||||||
|
failures.append("pending move cancel should succeed")
|
||||||
|
cao_cao = state.get_unit("cao_cao")
|
||||||
|
if cao_cao.get("pos", Vector2i.ZERO) != from_cell or bool(cao_cao.get("moved", false)) or bool(cao_cao.get("acted", false)):
|
||||||
|
failures.append("pending move cancel should restore original action state")
|
||||||
|
if state.get_battle_gold_reward() != 0:
|
||||||
|
failures.append("canceled pending move should not fire movement event")
|
||||||
|
|
||||||
|
if not state.try_move_selected(to_cell, true):
|
||||||
|
failures.append("second tentative move should succeed")
|
||||||
|
if not state.commit_pending_move_events("cao_cao"):
|
||||||
|
failures.append("pending move commit should keep battle active")
|
||||||
|
if state.get_battle_gold_reward() != 77:
|
||||||
|
failures.append("pending move commit should fire deferred movement event")
|
||||||
|
|
||||||
|
|
||||||
|
func _check_scene_post_move_menu_flow(failures: Array[String]) -> void:
|
||||||
|
var scene = BattleSceneScript.new()
|
||||||
|
scene._create_hud()
|
||||||
|
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||||
|
failures.append("could not load opening battle for scene flow")
|
||||||
|
scene.free()
|
||||||
|
return
|
||||||
|
scene.battle_started = true
|
||||||
|
scene.campaign_complete_screen = false
|
||||||
|
scene.briefing_panel.visible = false
|
||||||
|
scene.result_panel.visible = false
|
||||||
|
scene._clear_pending_move_state()
|
||||||
|
|
||||||
|
if not scene.state.select_unit("cao_cao"):
|
||||||
|
failures.append("could not select Cao Cao for scene flow")
|
||||||
|
scene.free()
|
||||||
|
return
|
||||||
|
scene._refresh_ranges()
|
||||||
|
scene._handle_board_click(_screen_for_cell(Vector2i(2, 3)))
|
||||||
|
|
||||||
|
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
|
||||||
|
if not scene._has_pending_move():
|
||||||
|
failures.append("scene should keep pending move metadata after move click")
|
||||||
|
if scene.post_move_menu == null or not scene.post_move_menu.visible:
|
||||||
|
failures.append("post-move action menu should be visible after moving")
|
||||||
|
if cao_cao.get("pos", Vector2i.ZERO) != Vector2i(2, 3) or not bool(cao_cao.get("moved", false)) or bool(cao_cao.get("acted", false)):
|
||||||
|
failures.append("scene move should be pending with action still available")
|
||||||
|
if scene.state.get_selected_unit().is_empty():
|
||||||
|
failures.append("scene should keep moved unit selected for follow-up action")
|
||||||
|
if not scene.attack_cells.is_empty():
|
||||||
|
failures.append("attack cells should wait until Attack is chosen from the post-move menu")
|
||||||
|
|
||||||
|
scene._handle_cancel_input()
|
||||||
|
cao_cao = scene.state.get_unit("cao_cao")
|
||||||
|
if scene._has_pending_move() or (scene.post_move_menu != null and scene.post_move_menu.visible):
|
||||||
|
failures.append("right-click cancel handler should clear pending move menu state")
|
||||||
|
if cao_cao.get("pos", Vector2i.ZERO) != Vector2i(1, 3) or bool(cao_cao.get("moved", false)) or bool(cao_cao.get("acted", false)):
|
||||||
|
failures.append("right-click cancel handler should restore the starting cell")
|
||||||
|
|
||||||
|
scene._refresh_ranges()
|
||||||
|
scene._handle_board_click(_screen_for_cell(Vector2i(2, 3)))
|
||||||
|
scene._on_post_move_wait_pressed()
|
||||||
|
cao_cao = scene.state.get_unit("cao_cao")
|
||||||
|
if cao_cao.get("pos", Vector2i.ZERO) != Vector2i(2, 3) or not bool(cao_cao.get("moved", false)) or not bool(cao_cao.get("acted", false)):
|
||||||
|
failures.append("post-move Wait should commit movement and end the unit action")
|
||||||
|
if not scene.state.get_selected_unit().is_empty() or scene._has_pending_move():
|
||||||
|
failures.append("post-move Wait should clear selection and pending metadata")
|
||||||
|
|
||||||
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
|
func _screen_for_cell(cell: Vector2i) -> Vector2:
|
||||||
|
return BattleSceneScript.BOARD_OFFSET + Vector2(cell.x, cell.y) * BattleSceneScript.TILE_SIZE + Vector2.ONE * (BattleSceneScript.TILE_SIZE * 0.5)
|
||||||
Reference in New Issue
Block a user