Commit b553c088 authored by Gavin An's avatar Gavin An

유닛 생성 대기 시, 팔기 버튼 활성화

parent 45c18ca1
......@@ -45,9 +45,9 @@ func _ready() -> void:
_create_runtime_bottom_buttons()
_on_gold_changed(GameManager.player_gold)
## 마우스/터치 입력 중 유닛 배치 확정에 해당하는 입력을 HudUnitPlacement에 위임합니다.
## 배치 입력이 처리된 경우 viewport에 handled를 표시해 같은 클릭이 다른 게임 입력으로 중복 전달되지 않게 합니다.
func _input(event: InputEvent) -> void:
## GUI가 처리하지 않은 마우스/터치 입력만 HudUnitPlacement에 위임합니다.
## 팔기 버튼 같은 HUD 컨트롤이 먼저 클릭을 소비해야 하므로 _input이 아닌 _unhandled_input에서 타일 배치를 처리합니다.
func _unhandled_input(event: InputEvent) -> void:
if _unit_placement and _unit_placement.has_method("handle_input"):
var handled: bool = _unit_placement.call("handle_input", event)
if handled:
......
......@@ -16,6 +16,7 @@ const OVERLAY_POOL_WARMUP_BATCH_SIZE: int = 16
var _hud: Control = null
var _unit_spawn_button: Button = null
var _bottom_bar: Control = null
var _base_unit_scene: PackedScene = null
var _spawn_button_default_text: String = ""
......@@ -38,12 +39,15 @@ var _pending_preview_cooldown_label: Label = null
var _pending_preview_range_label: Label = null
var _pending_preview_cost_label: Label = null
var _pending_preview_color_swatch: ColorRect = null
var _pending_sell_button: Button = null
var _bottom_bar_visibility_before_pending: Dictionary = {}
## HUD에서 필요한 참조를 주입받습니다.
## RefCounted는 씬 트리에 직접 붙지 않으므로, viewport/tree 접근은 HUD 노드를 통해 수행합니다.
func setup(hud: Control, unit_spawn_button: Button, base_unit_scene: PackedScene) -> void:
_hud = hud
_unit_spawn_button = unit_spawn_button
_bottom_bar = _unit_spawn_button.get_parent() as Control
_base_unit_scene = base_unit_scene
_spawn_button_default_text = _unit_spawn_button.text
......@@ -90,7 +94,7 @@ func handle_input(event: InputEvent) -> bool:
return false
## 현재 보유한 유닛 템플릿 중 하나를 무작위로 골라 타일 선택 대기 상태로 전환합니다.
## 실제 골드 차감은 타일 배치가 확정되는 순간 처리하여, 잘못 누른 소환 버튼이 즉시 비용을 쓰지 않게 합니다.
## pending 상태에서 팔기 환불이 가능해야 하므로, 유닛 생성 버튼을 누르는 순간 소환 비용을 먼저 지불합니다.
func begin_random_unit_placement() -> void:
if GameManager.is_game_over or has_pending_unit_placement():
return
......@@ -104,6 +108,9 @@ func begin_random_unit_placement() -> void:
var random_index: int = randi() % DataManager.unit_templates.size()
var chosen_template: UnitData = DataManager.unit_templates[random_index]
var target_scene: PackedScene = _get_scene_for_unit(chosen_template)
if not GameManager.spend_gold(GameManager.unit_spawn_cost):
return
_begin_unit_placement(target_scene, chosen_template, GameManager.unit_spawn_cost)
## 유닛 ID에 대응하는 전용 씬을 반환합니다.
......@@ -128,6 +135,7 @@ func _begin_unit_placement(unit_scene: PackedScene, unit_data: UnitData, spawn_c
_pending_spawn_cost = spawn_cost
_unit_spawn_button.text = "Select Tile"
_show_pending_unit_preview(unit_data, spawn_cost)
_enter_pending_bottom_bar_mode()
_show_placement_tile_overlays()
## 배치 대기 상태와 타일 오버레이를 함께 정리합니다.
......@@ -138,6 +146,7 @@ func clear_pending_unit_placement() -> void:
_pending_spawn_cost = 0
_unit_spawn_button.text = _spawn_button_default_text
_hide_pending_unit_preview()
_restore_bottom_bar_from_pending_mode()
_clear_placement_tile_overlays()
## 화면 좌표에서 타일을 찾고, 점유/골드 조건을 검증한 뒤 유닛을 실제 전장에 배치합니다.
......@@ -150,10 +159,6 @@ func _try_place_pending_unit(screen_pos: Vector2) -> void:
if _is_tile_occupied(tile_cell):
return
if not GameManager.spend_gold(_pending_spawn_cost):
clear_pending_unit_placement()
return
var new_unit: Node = _pending_unit_scene.instantiate()
var parent_scene: Node = _hud.get_tree().current_scene
var tile_center: Vector3 = tile_info["center"]
......@@ -168,6 +173,88 @@ func _try_place_pending_unit(screen_pos: Vector2) -> void:
clear_pending_unit_placement()
## pending 상태에 진입하면 BottomBar의 기존 UI를 모두 숨기고 팔기 버튼만 표시합니다.
## 기존 버튼들의 visible 상태는 Dictionary에 저장해 배치 완료/판매 후 원래 상태로 복구합니다.
func _enter_pending_bottom_bar_mode() -> void:
if not _bottom_bar:
return
_ensure_pending_sell_button()
_bottom_bar_visibility_before_pending.clear()
for child: Node in _bottom_bar.get_children():
if child is Control:
var control_child: Control = child as Control
_bottom_bar_visibility_before_pending[control_child] = control_child.visible
control_child.visible = control_child == _pending_sell_button
var refund_amount: int = _get_pending_sell_refund_amount()
_pending_sell_button.text = "팔기 (+%d G)" % refund_amount
_pending_sell_button.visible = true
_pending_sell_button.disabled = false
## pending 상태가 끝나면 BottomBar 자식들의 visible 상태를 pending 진입 전으로 되돌립니다.
## 팔기 버튼은 재사용을 위해 삭제하지 않고 숨겨 둡니다.
func _restore_bottom_bar_from_pending_mode() -> void:
if not _bottom_bar:
return
for child: Node in _bottom_bar.get_children():
if child is Control:
var control_child: Control = child as Control
if control_child == _pending_sell_button:
control_child.visible = false
else:
control_child.visible = _bottom_bar_visibility_before_pending.get(control_child, true)
_bottom_bar_visibility_before_pending.clear()
## pending 전용 팔기 버튼을 최초 1회 생성합니다.
## hud.tscn의 BottomBar 원본을 직접 수정하지 않고, 런타임에 추가해 필요할 때만 보여줍니다.
func _ensure_pending_sell_button() -> void:
if _pending_sell_button and is_instance_valid(_pending_sell_button):
return
_pending_sell_button = Button.new()
_pending_sell_button.name = "SellPendingUnitButton"
_pending_sell_button.text = "팔기"
_pending_sell_button.custom_minimum_size = Vector2(150, 46)
_pending_sell_button.mouse_filter = Control.MOUSE_FILTER_STOP
_pending_sell_button.add_theme_font_size_override("font_size", 18)
_pending_sell_button.add_theme_stylebox_override("normal", _create_pending_sell_button_style())
_pending_sell_button.pressed.connect(_on_sell_pending_unit_pressed)
_bottom_bar.add_child(_pending_sell_button)
## pending 유닛을 판매하고 소환 비용의 50%만 환불합니다.
## 생성 시 이미 전체 비용을 지불했으므로, 여기서는 절반만 add_gold()로 돌려준 뒤 pending 상태를 정리합니다.
func _on_sell_pending_unit_pressed() -> void:
if not has_pending_unit_placement():
return
var refund_amount: int = _get_pending_sell_refund_amount()
if refund_amount > 0:
GameManager.add_gold(refund_amount)
clear_pending_unit_placement()
## 현재 pending 유닛을 팔 때 돌려받을 골드를 계산합니다.
## 비용이 홀수일 경우 소수점은 버려 과지급이 발생하지 않게 합니다.
func _get_pending_sell_refund_amount() -> int:
return int(float(_pending_spawn_cost) * 0.5)
## 팔기 버튼 스타일을 생성합니다.
## pending 중 BottomBar에 버튼 하나만 남기 때문에, 위험/취소 계열 액션임을 붉은 색감으로 구분합니다.
func _create_pending_sell_button_style() -> StyleBoxFlat:
var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color(0.55, 0.08, 0.08, 0.95)
style.border_color = Color(1.0, 0.35, 0.25, 0.9)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.set_corner_radius_all(4)
return style
## 화면 좌표에 대응하는 GridMap 셀과 배치 중심 좌표를 계산합니다.
## 카메라에서 월드로 레이를 쏜 뒤, 맞은 지점 주변에서 실제 타일 셀을 찾습니다.
func _get_tile_info_from_screen(screen_pos: Vector2) -> Dictionary:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment