Add village 3D wiring, voice lines, and 1.74 m world scale.
Houses ~8.5 m; chimps capped ~0.85 m with safer axis fitting. Large GLB/PBR packs stay local until Git LFS (Forgejo HTTP 413). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1d9e0a6532
commit
1cc8aa9b9d
507 changed files with 12991 additions and 579 deletions
|
|
@ -1,9 +1,16 @@
|
|||
extends CanvasLayer
|
||||
## Global UI: pause, inventory, shop, settings, toast, dialog, burger-cham.
|
||||
## Global UI: pause, inventory, shop, settings, toast, VN-dialog, burger-cham.
|
||||
|
||||
@onready var pause_panel: PanelContainer = $PausePanel
|
||||
@onready var dialog_panel: PanelContainer = $DialogPanel
|
||||
@onready var dialog_label: Label = $DialogPanel/Margin/DialogLabel
|
||||
@onready var dialog_layer: Control = $DialogLayer
|
||||
@onready var dialog_dim: ColorRect = $DialogLayer/Dim
|
||||
@onready var dialog_panel: PanelContainer = $DialogLayer/DialogPanel
|
||||
@onready var dialog_label: Label = $DialogLayer/DialogPanel/Margin/VBox/DialogLabel
|
||||
@onready var speaker_label: Label = $DialogLayer/DialogPanel/Margin/VBox/SpeakerLabel
|
||||
@onready var continue_hint: Label = $DialogLayer/DialogPanel/Margin/VBox/Footer/ContinueHint
|
||||
@onready var advance_btn: Button = $DialogLayer/DialogPanel/Margin/VBox/Footer/AdvanceButton
|
||||
@onready var bark_panel: PanelContainer = $BarkPanel
|
||||
@onready var bark_label: Label = $BarkPanel/BarkLabel
|
||||
@onready var toast_label: Label = $ToastLabel
|
||||
@onready var burger_overlay: ColorRect = $BurgerOverlay
|
||||
@onready var burger_label: Label = $BurgerOverlay/BurgerLabel
|
||||
|
|
@ -12,20 +19,50 @@ extends CanvasLayer
|
|||
@onready var sfx_slider: HSlider = $PausePanel/Margin/VBox/SfxSlider
|
||||
@onready var music_slider: HSlider = $PausePanel/Margin/VBox/MusicSlider
|
||||
@onready var lang_option: OptionButton = $PausePanel/Margin/VBox/LangOption
|
||||
@onready var hp_label: Label = $HUD/HpLabel
|
||||
@onready var hp_hearts: HBoxContainer = $HUD/HpHearts
|
||||
@onready var status_label: Label = $HUD/StatusLabel
|
||||
@onready var fat_stim: ColorRect = $FatStim
|
||||
@onready var fat_label: Label = $FatStim/FatLabel
|
||||
@onready var interact_prompt: Label = $HUD/InteractPrompt
|
||||
@onready var attack_flash: ColorRect = $HUD/AttackFlash
|
||||
|
||||
const TYPEWRITER_CPS := 42.0
|
||||
const PANEL_SLIDE := 36.0
|
||||
|
||||
var _dialog_timer: float = 0.0
|
||||
var _fat_blink: float = 0.0
|
||||
var _toast_brief_timer: float = 0.0
|
||||
var _heart_full: Texture2D
|
||||
var _heart_empty: Texture2D
|
||||
|
||||
## VN dialog state
|
||||
var _dialog_open: bool = false
|
||||
var _dialog_blocking: bool = false
|
||||
var _dialog_paused_game: bool = false
|
||||
var _dialog_full_text: String = ""
|
||||
var _dialog_visible_chars: float = 0.0
|
||||
var _dialog_typing: bool = false
|
||||
var _dialog_queue: Array[Dictionary] = []
|
||||
var _dialog_panel_rest_top: float = -210.0
|
||||
var _bark_timer: float = 0.0
|
||||
var _continue_blink: float = 0.0
|
||||
var _voice_cast: Dictionary = {}
|
||||
var _open_tween: Tween
|
||||
|
||||
func _ready() -> void:
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
layer = 100
|
||||
_heart_full = load("res://assets/sprites/placeholders/heart.png")
|
||||
_heart_empty = load("res://assets/sprites/placeholders/heart_empty.png")
|
||||
_load_voice_cast()
|
||||
_style_dialog_panel()
|
||||
pause_panel.visible = false
|
||||
dialog_panel.visible = false
|
||||
dialog_layer.visible = false
|
||||
bark_panel.visible = false
|
||||
burger_overlay.visible = false
|
||||
toast_label.visible = false
|
||||
_dialog_panel_rest_top = dialog_panel.offset_top
|
||||
if advance_btn:
|
||||
advance_btn.pressed.connect(_on_dialog_advance_pressed)
|
||||
_setup_lang_option()
|
||||
_refresh_texts()
|
||||
sfx_slider.value = Settings.sfx_volume
|
||||
|
|
@ -42,14 +79,126 @@ func _ready() -> void:
|
|||
_on_hp(GameState.hp, GameState.max_hp)
|
||||
_refresh_inventory()
|
||||
_refresh_points(Shop.dressler_points)
|
||||
if interact_prompt:
|
||||
interact_prompt.visible = false
|
||||
if attack_flash:
|
||||
attack_flash.visible = false
|
||||
|
||||
func _load_voice_cast() -> void:
|
||||
var f := FileAccess.open("res://data/voice_cast.json", FileAccess.READ)
|
||||
if f == null:
|
||||
return
|
||||
var parsed: Variant = JSON.parse_string(f.get_as_text())
|
||||
if typeof(parsed) == TYPE_DICTIONARY:
|
||||
_voice_cast = parsed
|
||||
|
||||
func _style_dialog_panel() -> void:
|
||||
var box := StyleBoxFlat.new()
|
||||
box.bg_color = Color(0.07, 0.08, 0.12, 0.94)
|
||||
box.border_color = Color(0.85, 0.72, 0.4, 0.85)
|
||||
box.set_border_width_all(2)
|
||||
box.set_corner_radius_all(10)
|
||||
box.content_margin_left = 4
|
||||
box.content_margin_right = 4
|
||||
box.content_margin_top = 4
|
||||
box.content_margin_bottom = 4
|
||||
dialog_panel.add_theme_stylebox_override("panel", box)
|
||||
var bark_box := box.duplicate() as StyleBoxFlat
|
||||
bark_box.bg_color = Color(0.1, 0.08, 0.06, 0.88)
|
||||
bark_panel.add_theme_stylebox_override("panel", bark_box)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _dialog_timer > 0.0:
|
||||
_dialog_timer -= delta
|
||||
if _dialog_timer <= 0.0:
|
||||
dialog_panel.visible = false
|
||||
if _dialog_open and _dialog_typing:
|
||||
_dialog_visible_chars += TYPEWRITER_CPS * delta
|
||||
var shown := mini(int(_dialog_visible_chars), _dialog_full_text.length())
|
||||
dialog_label.text = _dialog_full_text.substr(0, shown)
|
||||
if shown >= _dialog_full_text.length():
|
||||
_dialog_typing = false
|
||||
_dialog_visible_chars = float(_dialog_full_text.length())
|
||||
if _dialog_open and not _dialog_typing:
|
||||
_continue_blink += delta
|
||||
var blink_a: float = 0.45 + 0.4 * abs(sin(_continue_blink * 4.0))
|
||||
continue_hint.modulate.a = blink_a
|
||||
advance_btn.modulate.a = 0.85 + 0.15 * abs(sin(_continue_blink * 4.0))
|
||||
if _bark_timer > 0.0:
|
||||
_bark_timer -= delta
|
||||
if _bark_timer <= 0.0:
|
||||
bark_panel.visible = false
|
||||
if _toast_brief_timer > 0.0:
|
||||
_toast_brief_timer -= delta
|
||||
_update_fat_stim(delta)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not _dialog_open or not _dialog_blocking:
|
||||
return
|
||||
# Advance first: Space is both ui_accept and interact — don't let interact steal it.
|
||||
if event.is_action_pressed("dialog_advance") or event.is_action_pressed("ui_accept"):
|
||||
_advance_dialog()
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
# Gameplay-only keys must NOT dismiss / advance dialog (E / attack click)
|
||||
if event.is_action_pressed("interact") or event.is_action_pressed("attack"):
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
|
||||
func set_interact_prompt(text: String) -> void:
|
||||
if interact_prompt == null:
|
||||
return
|
||||
if _dialog_open or text == "" or GameState.current_scene_id in ["title_screen", "death_screen", "intro_cutscene", "catscene"]:
|
||||
interact_prompt.visible = false
|
||||
return
|
||||
interact_prompt.text = text
|
||||
interact_prompt.visible = true
|
||||
|
||||
func _wait(sec: float) -> void:
|
||||
## Safe timer: works when paused; never call create_timer on a null tree.
|
||||
var tree := get_tree()
|
||||
if tree == null:
|
||||
tree = Engine.get_main_loop() as SceneTree
|
||||
if tree == null:
|
||||
return
|
||||
await tree.create_timer(sec, true).timeout
|
||||
|
||||
func force_close_dialog() -> void:
|
||||
## Called on scene change so pause/dialog state cannot leak to title.
|
||||
_dialog_queue.clear()
|
||||
_dialog_open = false
|
||||
_dialog_typing = false
|
||||
_dialog_blocking = false
|
||||
if dialog_layer:
|
||||
dialog_layer.visible = false
|
||||
if bark_panel:
|
||||
bark_panel.visible = false
|
||||
_bark_timer = 0.0
|
||||
_dialog_paused_game = false
|
||||
_sync_player_focus(true)
|
||||
|
||||
func pulse_attack() -> void:
|
||||
if attack_flash == null:
|
||||
return
|
||||
attack_flash.color = Color(1.0, 0.85, 0.3, 0.18)
|
||||
attack_flash.visible = true
|
||||
await _wait(0.08)
|
||||
if is_instance_valid(attack_flash):
|
||||
attack_flash.visible = false
|
||||
|
||||
func pulse_interact() -> void:
|
||||
if attack_flash == null:
|
||||
return
|
||||
attack_flash.color = Color(0.5, 1.0, 0.7, 0.14)
|
||||
attack_flash.visible = true
|
||||
await _wait(0.1)
|
||||
if is_instance_valid(attack_flash):
|
||||
attack_flash.visible = false
|
||||
|
||||
func show_toast_brief(text: String, duration: float = 0.5) -> void:
|
||||
toast_label.text = text
|
||||
toast_label.visible = true
|
||||
_toast_brief_timer = duration
|
||||
await _wait(duration)
|
||||
if _toast_brief_timer <= 0.0:
|
||||
toast_label.visible = false
|
||||
|
||||
func _update_fat_stim(delta: float) -> void:
|
||||
if fat_stim == null:
|
||||
return
|
||||
|
|
@ -59,8 +208,23 @@ func _update_fat_stim(delta: float) -> void:
|
|||
_fat_blink += delta
|
||||
fat_label.text = Locale.tr_key("dialog_tiramisu")
|
||||
fat_stim.color = Color(0.05, 0.05, 0.08, 0.35 + 0.25 * abs(sin(_fat_blink * 3.0)))
|
||||
var stim := fat_stim.get_node_or_null("StimImage") as TextureRect
|
||||
if stim == null:
|
||||
stim = TextureRect.new()
|
||||
stim.name = "StimImage"
|
||||
fat_stim.add_child(stim)
|
||||
stim.set_anchors_preset(Control.PRESET_CENTER_BOTTOM)
|
||||
stim.offset_left = -80.0
|
||||
stim.offset_top = -200.0
|
||||
stim.offset_right = 80.0
|
||||
stim.offset_bottom = -40.0
|
||||
stim.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
stim.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
stim.texture = load("res://assets/sprites/placeholders/shorts_stim.png")
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if _dialog_open and _dialog_blocking:
|
||||
return
|
||||
if event.is_action_pressed("pause_menu"):
|
||||
toggle_pause()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
|
@ -68,6 +232,8 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
func toggle_pause() -> void:
|
||||
if GameState.current_scene_id in ["title_screen", "death_screen", "intro_cutscene", "catscene"]:
|
||||
return
|
||||
if _dialog_open and _dialog_blocking:
|
||||
return
|
||||
pause_panel.visible = not pause_panel.visible
|
||||
get_tree().paused = pause_panel.visible
|
||||
_sync_player_focus(not pause_panel.visible)
|
||||
|
|
@ -80,10 +246,134 @@ func _sync_player_focus(focused: bool) -> void:
|
|||
if node.has_method("set_gameplay_focused"):
|
||||
node.set_gameplay_focused(focused)
|
||||
|
||||
func show_dialog(text: String, duration: float = 3.5) -> void:
|
||||
dialog_label.text = text
|
||||
dialog_panel.visible = true
|
||||
_dialog_timer = duration
|
||||
## Blocking VN dialog (pauses world). `duration` ignored for dismiss — user advances with Enter/C.
|
||||
func show_dialog(text: String, _duration: float = 3.5, voice_key: String = "", blocking: bool = true) -> void:
|
||||
if not blocking:
|
||||
_show_bark(text, maxf(_duration, 2.0), voice_key)
|
||||
return
|
||||
_dialog_queue.append({"text": text, "voice_key": voice_key})
|
||||
if not _dialog_open:
|
||||
_present_next_dialog()
|
||||
|
||||
func show_dialog_key(key: String, duration: float = 3.5, blocking: bool = true) -> void:
|
||||
show_dialog(Locale.tr_key(key), duration, key, blocking)
|
||||
|
||||
func is_dialog_open() -> bool:
|
||||
return _dialog_open and _dialog_blocking
|
||||
|
||||
func _show_bark(text: String, duration: float, voice_key: String) -> void:
|
||||
bark_label.text = text
|
||||
bark_panel.visible = true
|
||||
_bark_timer = duration
|
||||
if voice_key != "":
|
||||
AudioDirector.play_dialog_line(voice_key)
|
||||
|
||||
func _present_next_dialog() -> void:
|
||||
if _dialog_queue.is_empty():
|
||||
_close_dialog()
|
||||
return
|
||||
var entry: Dictionary = _dialog_queue.pop_front()
|
||||
var text: String = str(entry.get("text", ""))
|
||||
var voice_key: String = str(entry.get("voice_key", ""))
|
||||
_dialog_full_text = text
|
||||
_dialog_visible_chars = 0.0
|
||||
_dialog_typing = text.length() > 0
|
||||
_dialog_blocking = true
|
||||
_dialog_open = true
|
||||
dialog_label.text = ""
|
||||
speaker_label.text = _speaker_for_key(voice_key)
|
||||
continue_hint.text = Locale.tr_key("dialog_continue")
|
||||
advance_btn.text = Locale.tr_key("dialog_advance_btn")
|
||||
dialog_layer.visible = true
|
||||
if interact_prompt:
|
||||
interact_prompt.visible = false
|
||||
_begin_dialog_pause()
|
||||
# Player _ready may run after this and steal mouse — re-assert after scene settles.
|
||||
call_deferred("_ensure_dialog_input_ready")
|
||||
_play_open_anim()
|
||||
if voice_key != "":
|
||||
AudioDirector.play_dialog_line(voice_key)
|
||||
|
||||
func _speaker_for_key(voice_key: String) -> String:
|
||||
if voice_key == "":
|
||||
return Locale.tr_key("speaker_system")
|
||||
var lines: Variant = _voice_cast.get("lines", {})
|
||||
if typeof(lines) == TYPE_DICTIONARY and lines.has(voice_key):
|
||||
var npc := str(lines[voice_key])
|
||||
var sk := "speaker_%s" % npc
|
||||
var named := Locale.tr_key(sk)
|
||||
if named != sk:
|
||||
return named
|
||||
return npc.capitalize()
|
||||
return Locale.tr_key("speaker_system")
|
||||
|
||||
func _play_open_anim() -> void:
|
||||
if _open_tween and _open_tween.is_running():
|
||||
_open_tween.kill()
|
||||
dialog_dim.color.a = 0.0
|
||||
dialog_panel.modulate.a = 0.0
|
||||
dialog_panel.offset_top = _dialog_panel_rest_top + PANEL_SLIDE
|
||||
dialog_panel.offset_bottom = -28.0 + PANEL_SLIDE
|
||||
_open_tween = create_tween()
|
||||
_open_tween.set_parallel(true)
|
||||
_open_tween.tween_property(dialog_dim, "color:a", 0.55, 0.28).set_ease(Tween.EASE_OUT)
|
||||
_open_tween.tween_property(dialog_panel, "modulate:a", 1.0, 0.32).set_ease(Tween.EASE_OUT)
|
||||
_open_tween.tween_property(dialog_panel, "offset_top", _dialog_panel_rest_top, 0.35).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
||||
_open_tween.tween_property(dialog_panel, "offset_bottom", -28.0, 0.35).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
||||
|
||||
func _advance_dialog() -> void:
|
||||
if not _dialog_open:
|
||||
return
|
||||
if _dialog_typing:
|
||||
# First confirm: finish typewriter
|
||||
_dialog_typing = false
|
||||
_dialog_visible_chars = float(_dialog_full_text.length())
|
||||
dialog_label.text = _dialog_full_text
|
||||
return
|
||||
# Second confirm: next line or close
|
||||
if not _dialog_queue.is_empty():
|
||||
_present_next_dialog()
|
||||
else:
|
||||
_close_dialog()
|
||||
|
||||
func _on_dialog_advance_pressed() -> void:
|
||||
_advance_dialog()
|
||||
|
||||
func _begin_dialog_pause() -> void:
|
||||
if pause_panel.visible:
|
||||
_dialog_paused_game = false
|
||||
return
|
||||
if not get_tree().paused:
|
||||
get_tree().paused = true
|
||||
_dialog_paused_game = true
|
||||
else:
|
||||
_dialog_paused_game = true
|
||||
_sync_player_focus(false)
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
func _ensure_dialog_input_ready() -> void:
|
||||
if not _dialog_open or not _dialog_blocking:
|
||||
return
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
_sync_player_focus(false)
|
||||
if advance_btn:
|
||||
advance_btn.disabled = false
|
||||
advance_btn.grab_focus()
|
||||
|
||||
func _end_dialog_pause() -> void:
|
||||
if _dialog_paused_game and not pause_panel.visible:
|
||||
get_tree().paused = false
|
||||
_sync_player_focus(true)
|
||||
_dialog_paused_game = false
|
||||
|
||||
func _close_dialog() -> void:
|
||||
_dialog_open = false
|
||||
_dialog_blocking = false
|
||||
_dialog_typing = false
|
||||
_dialog_queue.clear()
|
||||
dialog_layer.visible = false
|
||||
AudioDirector.stop_voice()
|
||||
_end_dialog_pause()
|
||||
|
||||
func _setup_lang_option() -> void:
|
||||
lang_option.clear()
|
||||
|
|
@ -103,14 +393,14 @@ func _refresh_texts() -> void:
|
|||
$PausePanel/Margin/VBox/ResumeButton.text = Locale.tr_key("menu_resume")
|
||||
$PausePanel/Margin/VBox/ShopTitle.text = Locale.tr_key("menu_shop")
|
||||
$PausePanel/Margin/VBox/BuyPointButton.text = Locale.tr_key("menu_buy_point")
|
||||
$PausePanel/Margin/VBox/BuySunglassesButton.text = Locale.tr_key("shop_sunglasses")
|
||||
$PausePanel/Margin/VBox/BuyTicketButton.text = Locale.tr_key("shop_ticket")
|
||||
$PausePanel/Margin/VBox/BuyMilkButton.text = Locale.tr_key("shop_continue_joke")
|
||||
$PausePanel/Margin/VBox/BuyTiramisuButton.text = Locale.tr_key("shop_next_level")
|
||||
$PausePanel/Margin/VBox/SfxLabel.text = Locale.tr_key("menu_sfx")
|
||||
$PausePanel/Margin/VBox/MusicLabel.text = Locale.tr_key("menu_music")
|
||||
$PausePanel/Margin/VBox/LangLabel.text = Locale.tr_key("menu_language")
|
||||
$PausePanel/Margin/VBox/InventoryLabel.text = Locale.tr_key("menu_inventory")
|
||||
if continue_hint:
|
||||
continue_hint.text = Locale.tr_key("dialog_continue")
|
||||
if advance_btn:
|
||||
advance_btn.text = Locale.tr_key("dialog_advance_btn")
|
||||
_refresh_points(Shop.dressler_points)
|
||||
|
||||
func _refresh_points(points: int) -> void:
|
||||
|
|
@ -118,34 +408,81 @@ func _refresh_points(points: int) -> void:
|
|||
|
||||
func _refresh_inventory() -> void:
|
||||
inventory_list.clear()
|
||||
inventory_list.fixed_icon_size = Vector2i(int(SpriteFit.UI_ICON_PX), int(SpriteFit.UI_ICON_PX))
|
||||
var items := Inventory.get_all()
|
||||
for id in items.keys():
|
||||
var count: int = int(items[id])
|
||||
var item_name: String = Locale.tr_key("item_" + str(id))
|
||||
var idx := inventory_list.item_count
|
||||
inventory_list.add_item("%s x%d" % [item_name, count])
|
||||
inventory_list.set_item_metadata(inventory_list.item_count - 1, id)
|
||||
inventory_list.set_item_metadata(idx, id)
|
||||
var icon_path := Inventory.get_icon_path(str(id))
|
||||
if icon_path != "":
|
||||
var tex: Texture2D = load(icon_path)
|
||||
if tex:
|
||||
inventory_list.set_item_icon(idx, tex)
|
||||
|
||||
func _on_hp(hp: int, max_hp: int) -> void:
|
||||
_rebuild_hearts(hp, max_hp)
|
||||
var extra := ""
|
||||
if GameState.is_fat():
|
||||
extra += " | " + Locale.tr_key("status_fat", [str(int(GameState.fat_time_left))])
|
||||
extra += Locale.tr_key("status_fat", [str(int(GameState.fat_time_left))])
|
||||
if GameState.is_shocked():
|
||||
extra += " | " + Locale.tr_key("status_shock")
|
||||
if extra != "":
|
||||
extra += " · "
|
||||
extra += Locale.tr_key("status_shock")
|
||||
if GameState.is_glitched_slow():
|
||||
extra += " | " + Locale.tr_key("status_glitch")
|
||||
hp_label.text = "HP %d/%d%s" % [hp, max_hp, extra]
|
||||
if extra != "":
|
||||
extra += " · "
|
||||
extra += Locale.tr_key("status_glitch")
|
||||
if status_label:
|
||||
status_label.text = extra
|
||||
status_label.visible = extra != ""
|
||||
|
||||
func _rebuild_hearts(hp: int, max_hp: int) -> void:
|
||||
if hp_hearts == null:
|
||||
return
|
||||
while hp_hearts.get_child_count() > 0:
|
||||
var c := hp_hearts.get_child(0)
|
||||
hp_hearts.remove_child(c)
|
||||
c.queue_free()
|
||||
var n := maxi(max_hp, 1)
|
||||
for i in range(n):
|
||||
var tex_rect := TextureRect.new()
|
||||
tex_rect.custom_minimum_size = Vector2(28, 28)
|
||||
tex_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
tex_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
tex_rect.texture = _heart_full if i < hp else _heart_empty
|
||||
tex_rect.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
hp_hearts.add_child(tex_rect)
|
||||
|
||||
func _on_achievement(_id: String, title: String) -> void:
|
||||
toast_label.text = Locale.tr_key("toast_achievement", [title])
|
||||
toast_label.visible = true
|
||||
await get_tree().create_timer(3.0).timeout
|
||||
toast_label.visible = false
|
||||
await _wait(3.0)
|
||||
if is_instance_valid(toast_label):
|
||||
toast_label.visible = false
|
||||
|
||||
func _on_burger_cham() -> void:
|
||||
burger_label.text = Locale.tr_key("burger_cham")
|
||||
burger_overlay.visible = true
|
||||
await get_tree().create_timer(2.5).timeout
|
||||
burger_overlay.visible = false
|
||||
var img := burger_overlay.get_node_or_null("BurgerImage") as TextureRect
|
||||
if img == null:
|
||||
img = TextureRect.new()
|
||||
img.name = "BurgerImage"
|
||||
burger_overlay.add_child(img)
|
||||
img.set_anchors_preset(Control.PRESET_CENTER)
|
||||
img.offset_left = -120.0
|
||||
img.offset_top = -200.0
|
||||
img.offset_right = 120.0
|
||||
img.offset_bottom = -40.0
|
||||
img.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
img.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
img.texture = load("res://assets/sprites/placeholders/burger_cham.png")
|
||||
SpriteFit.apply_texture_rect_budget(img, 240.0)
|
||||
await _wait(2.5)
|
||||
if is_instance_valid(burger_overlay):
|
||||
burger_overlay.visible = false
|
||||
|
||||
func _on_resume_pressed() -> void:
|
||||
pause_panel.visible = false
|
||||
|
|
@ -155,18 +492,6 @@ func _on_resume_pressed() -> void:
|
|||
func _on_buy_point() -> void:
|
||||
Shop.purchase("buy_point")
|
||||
|
||||
func _on_buy_sunglasses() -> void:
|
||||
Shop.purchase("sunglasses")
|
||||
|
||||
func _on_buy_ticket() -> void:
|
||||
Shop.purchase("death_ticket")
|
||||
|
||||
func _on_buy_milk() -> void:
|
||||
Shop.purchase("continue_joke")
|
||||
|
||||
func _on_buy_tiramisu() -> void:
|
||||
Shop.purchase("next_level")
|
||||
|
||||
func _on_sfx_changed(value: float) -> void:
|
||||
Settings.set_sfx_volume(value)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue