added achievement popup in game

main
Meik Schürmann 2026-06-27 16:25:57 +02:00
parent 7c3d95aa9f
commit b71ea39155
6 changed files with 160 additions and 3 deletions

Binary file not shown.

View File

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c7x1rtkae66k4"
path="res://.godot/imported/achievement_unlock.wav-9a9f9e013cd33e691b8d6734dd2cf61a.sample"
[deps]
source_file="res://assets/music&sfx/sfx/achievement_unlock.wav"
dest_files=["res://.godot/imported/achievement_unlock.wav-9a9f9e013cd33e691b8d6734dd2cf61a.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

View File

@ -1,6 +1,7 @@
extends Node
const SAVE_PATH = "user://achievements.json"
const _POPUP_SCRIPT = preload("res://scripts/achievement_popup.gd")
var achievements: Array = []
func _ready() -> void:
@ -123,15 +124,22 @@ func _define() -> void:
]
func check_all() -> void:
var changed = false
var newly_unlocked: Array = []
for ach in achievements:
if ach.unlocked:
continue
if _meets(ach):
ach.unlocked = true
changed = true
if changed:
newly_unlocked.append(ach)
if not newly_unlocked.is_empty():
_save()
for ach in newly_unlocked:
_show_popup(ach)
func _show_popup(ach: Dictionary) -> void:
var popup = _POPUP_SCRIPT.new()
get_tree().root.add_child(popup)
popup.show_unlock(ach)
func _meets(ach: Dictionary) -> bool:
var d = StatsManager.data

View File

@ -0,0 +1,122 @@
extends CanvasLayer
const _VP_W := 1280.0
const _VP_H := 720.0
const _MARGIN := 16.0
const _SFX_PATH := "res://assets/music&sfx/sfx/achievement_unlock.wav"
var _font = preload("res://assets/fonts/slkscre.ttf")
var _panel: PanelContainer
func _ready() -> void:
layer = 100
process_mode = Node.PROCESS_MODE_ALWAYS
_panel = PanelContainer.new()
_panel.custom_minimum_size = Vector2(310.0, 76.0)
_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
var style := StyleBoxFlat.new()
style.bg_color = Color(0.04, 0.04, 0.12, 0.96)
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.78, 0.62, 0.18, 1.0)
style.corner_radius_top_left = 3
style.corner_radius_top_right = 3
style.corner_radius_bottom_right = 3
style.corner_radius_bottom_left = 3
style.content_margin_left = 8.0
style.content_margin_top = 8.0
style.content_margin_right = 12.0
style.content_margin_bottom = 8.0
_panel.add_theme_stylebox_override("panel", style)
_panel.position = Vector2(_VP_W + 8.0, _VP_H - 76.0 - _MARGIN)
add_child(_panel)
func show_unlock(ach: Dictionary) -> void:
var hbox := HBoxContainer.new()
hbox.add_theme_constant_override("separation", 10)
hbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
_panel.add_child(hbox)
# Icon square
var icon_wrap := Control.new()
icon_wrap.custom_minimum_size = Vector2(52, 52)
icon_wrap.mouse_filter = Control.MOUSE_FILTER_IGNORE
hbox.add_child(icon_wrap)
var icon_bg := ColorRect.new()
icon_bg.color = ach.color
icon_bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
icon_bg.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
icon_wrap.add_child(icon_bg)
var icon_lbl := Label.new()
icon_lbl.text = ach.icon_char
icon_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
icon_lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
icon_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
icon_lbl.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
icon_lbl.add_theme_font_override("font", _font)
icon_lbl.add_theme_font_size_override("font_size", 22)
icon_lbl.add_theme_color_override("font_color", Color(1.0, 1.0, 0.88))
icon_lbl.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.8))
icon_lbl.add_theme_constant_override("outline_size", 2)
icon_wrap.add_child(icon_lbl)
# Text block
var vbox := VBoxContainer.new()
vbox.size_flags_vertical = Control.SIZE_SHRINK_CENTER
vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
hbox.add_child(vbox)
var header := Label.new()
header.text = "Achievement Unlocked!"
header.mouse_filter = Control.MOUSE_FILTER_IGNORE
header.add_theme_font_override("font", _font)
header.add_theme_font_size_override("font_size", 13)
header.add_theme_color_override("font_color", Color(0.78, 0.62, 0.18, 1.0))
vbox.add_child(header)
var title_lbl := Label.new()
title_lbl.text = ach.title
title_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
title_lbl.add_theme_font_override("font", _font)
title_lbl.add_theme_font_size_override("font_size", 20)
title_lbl.add_theme_color_override("font_color", Color(1.0, 0.95, 0.8, 1.0))
vbox.add_child(title_lbl)
_play_sound()
# Wait one frame so the panel has its final size before animating
await get_tree().process_frame
_animate()
func _play_sound() -> void:
if not ResourceLoader.exists(_SFX_PATH):
return
var sfx := AudioStreamPlayer.new()
sfx.stream = load(_SFX_PATH)
sfx.volume_db = -2.0
sfx.process_mode = Node.PROCESS_MODE_ALWAYS
add_child(sfx)
sfx.play()
sfx.finished.connect(sfx.queue_free)
func _animate() -> void:
var pw := _panel.size.x
var target_x := _VP_W - pw - _MARGIN
_panel.position.x = _VP_W + 8.0
var tween := create_tween()
tween.set_pause_mode(Tween.TWEEN_PAUSE_PROCESS)
tween.tween_property(_panel, "position:x", target_x, 0.4)\
.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUART)
tween.tween_interval(3.5)
tween.tween_property(_panel, "position:x", _VP_W + 8.0, 0.3)\
.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_QUART)
tween.tween_callback(queue_free)

View File

@ -0,0 +1 @@
uid://dsitid33q7lcx

View File

@ -50,10 +50,12 @@ func end_run(died: bool) -> void:
func on_kill(enemy_type: String) -> void:
if data["kills"].has(enemy_type):
data["kills"][enemy_type] += 1
AchievementManager.call_deferred("check_all")
func on_spell_cast(spell_type: String) -> void:
if data["spells_cast"].has(spell_type):
data["spells_cast"][spell_type] += 1
AchievementManager.call_deferred("check_all")
func _save() -> void:
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)