extends Control class_name HUDLayer ## 스트립 왼쪽 위에 붙는 한 줄짜리 정보 바와 짧은 알림. ## 스트립은 화면에 도킹되므로 창을 끌어 옮기는 기능은 없다. 위치는 메뉴에서 바꾼다. signal shop_pressed signal menu_pressed ## 정보 바가 위에서 자리를 얼마나 먹는지. 바 높이는 글꼴과 내용에 따라 달라지므로 ## 재어서 알려주고, main이 받아 수조 격자를 그만큼 아래로 내린다. signal reserved_top_changed(px: float) ## 바 아래로 이만큼은 띄워야 첫 줄 칸이 바에 닿지 않는다. const BAR_CLEARANCE := 22.0 @onready var control_bar: PanelContainer = $ControlBar @onready var bubbles_label: Label = %BubblesLabel @onready var bps_label: Label = %BpsLabel @onready var beauty_label: Label = %BeautyLabel @onready var zone_label: Label = %ZoneLabel @onready var shop_button: Button = %ShopButton @onready var menu_button: Button = %MenuButton @onready var toast: Label = %Toast @onready var layer_bar: LayerBar = %LayerBar @onready var trash: PanelContainer = %Trash @onready var trash_label: Label = %TrashLabel var _toast_tween: Tween func _ready() -> void: # 바를 납작하게 눌러 둔다. 이 높이만큼 격자가 밀리고, # 남는 자리에 먼 바다 배경이 드러난다. var bar_style := UIStyle.panel(UIStyle.PANEL_BG, 11) bar_style.content_margin_top = 6 bar_style.content_margin_bottom = 6 control_bar.add_theme_stylebox_override("panel", bar_style) UIStyle.label(bubbles_label, 20, UIStyle.INK) UIStyle.label(bps_label, 13, UIStyle.INK_DIM) UIStyle.label(beauty_label, 13, UIStyle.GOLD) UIStyle.label(zone_label, 13, UIStyle.ACCENT) UIStyle.label(toast, 14, UIStyle.INK) toast.modulate.a = 0.0 trash.add_theme_stylebox_override("panel", UIStyle.panel(Color(0.35, 0.06, 0.10, 0.88), 12)) UIStyle.label(trash_label, 13, UIStyle.INK) trash.hide() UIStyle.style_button(shop_button) UIStyle.style_button(menu_button) shop_button.pressed.connect(func(): shop_pressed.emit()) menu_button.pressed.connect(func(): menu_pressed.emit()) control_bar.resized.connect(func(): _place_toast() reserved_top_changed.emit(reserved_top())) _place_toast() GameState.zone_unlocked.connect(func(_i: int): _refresh_zone_label()) GameState.state_changed.connect(_refresh_zone_label) GameState.reloaded.connect(_refresh_zone_label) SaveManager.offline_earned.connect(_on_offline_earned) _refresh_zone_label() func _process(_delta: float) -> void: bubbles_label.text = NumberFormat.short(GameState.bubbles) # 정원이 곧 성장의 한계라 초당 생산량 옆에 늘 같이 둔다. bps_label.text = "초당 %s · 생물 %d/%d" % [ NumberFormat.short(GameState.bps), GameState.fish_total, GameState.capacity_total() ] beauty_label.text = "아름다움 %s · x%.2f" % [ NumberFormat.short(GameState.beauty), GameState.beauty_mult ] ## 몇 개 구역을 열었고 다음은 무엇인지. 해금 조건 자체는 잠긴 구역 위에 직접 표시된다. func _refresh_zone_label() -> void: var opened := GameState.unlocked_zones var total := Catalog.zone_count() if not GameState.has_next_zone(): zone_label.text = "구역 %d/%d · 모두 열었어요" % [opened, total] return var next_zone := Catalog.zone(GameState.next_zone_index()) zone_label.text = "구역 %d/%d · 다음 %s %s" % [ opened, total, next_zone.name, NumberFormat.short(GameState.next_zone_cost()) ] func _on_offline_earned(amount: float, seconds: int) -> void: show_toast("%s 동안 거품 %s를 모았어요" % [ NumberFormat.duration(seconds), NumberFormat.short(amount) ], 4.5) ## 알림은 정보 바 바로 아래 왼쪽에 붙인다. ## 가운데에 두면 바가 길어졌을 때 글자가 겹쳐 둘 다 못 읽는다. func _place_toast() -> void: # 층 선택기가 왼쪽 아래를 쓰므로 알림은 그 폭만큼 비켜 앉힌다. toast.position = Vector2( control_bar.position.x + layer_bar.size.x + 14.0, control_bar.position.y + control_bar.size.y + 6.0) func show_toast(text: String, duration := 2.0) -> void: toast.text = text if _toast_tween and _toast_tween.is_valid(): _toast_tween.kill() _toast_tween = create_tween() _toast_tween.tween_property(toast, "modulate:a", 1.0, 0.2) _toast_tween.tween_interval(duration) _toast_tween.tween_property(toast, "modulate:a", 0.0, 0.5) ## 무언가를 집어 든 동안만 휴지통을 보여준다. 평소에는 자리를 차지하지 않는다. func set_trash_visible(on: bool) -> void: trash.visible = on ## 휴지통의 사각형. HUD와 바다가 같은 좌표계(Main의 전체 영역)를 쓰므로 그대로 넘길 수 있다. func trash_area() -> Rect2: return Rect2(trash.position, trash.size) ## 정보 바가 먹는 세로 자리. 격자는 이 아래에서 시작해야 한다. func reserved_top() -> float: return control_bar.position.y + control_bar.size.y + BAR_CLEARANCE