brackeys-12/components/Achievements/achievements.gd
2024-09-08 13:34:41 -04:00

263 lines
7.5 KiB
GDScript

extends Base
## Achievement handler to check and unlock achievements
##
## An achievement handler that will check the available achievements defined in
## both the game files and the mods. When called with triggers will check to see
## if that trigger value unlocks achievements.
signal achievement_unlocked(achievement: String)
const CACHE_TIME = 60000
const POPUP_POP_TIME = 1000
const POPUP_LINGER_TIME = 5000
const POPUP_HIDE_TIME = 1000
const POPUP_BREAK = 1000
const POPUP_HOVER_TIME = 250
@onready var popup = $"CanvasLayer/Popup"
@onready var popup_title = $"CanvasLayer/Popup/Popup/Title"
@onready var popup_body = $"CanvasLayer/Popup/Popup/Body"
@onready var audio = $"AudioStreamPlayer"
@onready var audio2 = $"AudioStreamPlayer2"
enum AchievementState {
ENTERING,
SHOWN,
HOVERED,
EXITING,
HIDDEN
}
var queue = []
var cached_percents = {}
var last_percent_pull: int
var showing_popup = false
var starting_popup_pos
var starting_popup_size
var popup_tween
var popup_timer = 0
var popup_time = 6
#var entering = false
#var exiting = false
#var hovered = false
var achievement_state = AchievementState.HIDDEN
var hovered = false
func _ready():
#_log_category = "ACH"
#_log_icon = "res://components/Logger/scroll-text.svg"
#_log_color = "#a166a1"
super()
_info("Achievements is active")
starting_popup_pos = popup.position
starting_popup_size = popup.size
popup.position.x = starting_popup_pos.x + starting_popup_size.x
popup.modulate = Color.TRANSPARENT
popup.scale = Vector2(0.5, 0.5)
if _triggerer:
_triggerer.listen("any", _on_trigger)
func _on_trigger(data: Dictionary) -> void:
_check_trigger(data.trigger, "Trigger")
func _process(delta):
if Persister.get_value("paused") or not Persister.get_value("active"):
return
popup_timer += delta
if popup_timer > popup_time and achievement_state == AchievementState.SHOWN:
_exit_popup()
if not queue.is_empty() and not showing_popup:
_show_popup(queue.pop_front())
if popup_tween and not popup_tween.is_running():
if hovered and achievement_state == AchievementState.SHOWN:
achievement_state = AchievementState.HOVERED
popup_tween = create_tween()
popup_tween.set_parallel()
popup_tween.set_trans(Tween.TRANS_QUAD)
popup_tween.tween_property(popup, "scale", Vector2(1.05, 1.05), float(POPUP_HOVER_TIME) / 1000)
popup_tween.tween_property(popup, "position:x", starting_popup_pos.x - 20, float(POPUP_HOVER_TIME) / 1000)
if not hovered and achievement_state == AchievementState.HOVERED:
achievement_state = AchievementState.SHOWN
popup_tween = create_tween()
popup_tween.set_parallel()
popup_tween.set_trans(Tween.TRANS_QUAD)
popup_tween.tween_property(popup, "scale", Vector2(1, 1), float(POPUP_HOVER_TIME) / 1000)
popup_tween.tween_property(popup, "position:x", starting_popup_pos.x, float(POPUP_HOVER_TIME) / 1000)
func _exit_popup():
achievement_state = AchievementState.EXITING
audio2.play()
if popup_tween: popup_tween.kill()
popup_tween = create_tween()
popup_tween.set_ease(Tween.EASE_IN)
popup_tween.set_trans(Tween.TRANS_BACK)
popup_tween.set_parallel()
popup_tween.tween_property(popup, "modulate", Color.TRANSPARENT, POPUP_POP_TIME / 1000)
popup_tween.tween_property(popup, "scale", Vector2(0.5, 0.5), POPUP_POP_TIME / 1000)
popup_tween.tween_property(popup, "position:x", starting_popup_pos.x + starting_popup_size.x, POPUP_HIDE_TIME / 1000)
popup_tween.tween_interval(POPUP_BREAK / 1000)
popup_tween.chain()
popup_tween.tween_callback(func(): showing_popup = false)
_info("Hiding achievement popup")
func _on_data_persisted(key: String, value, category: PersisterEnums.Scope):
_check_trigger(key, "Data", value)
## Get all of the achievement info
func get_achievements() -> Dictionary:
if not _data:
if (_logger): _logger.warn("Could not find data autoload when getting achievements")
return {}
if not _data.data.has("achievements"):
if (_logger): _logger.warn("Could not find achievement data")
return {}
var local_ach_data = _data.data.achievements.duplicate()
var percents = _get_achievement_percents()
for achievement in _data.data.achievements:
if percents.keys().has(achievement):
local_ach_data.percent = percents[achievement]
return _data.data.achievements
## Get achievement info given the achievement slug
func get_achievement(achievement: String) -> Dictionary:
if not _data:
if _logger: _logger.warn("Could not find data autoload when getting achievement %s" % [achievement])
return {}
if not _data.data.has("achievements"):
if _logger: _logger.warn("Could not find achievement data")
return {}
if not _data.data.achievements.has(achievement):
if _logger: _logger.warn("Could not find achievement %s" % [achievement])
return {}
var achievement_data = _data.data.achievements[achievement].duplicate()
# TODO: Add in achievement percent
return achievement_data
func _show_popup(achievement: String) -> void:
var achievement_data = get_achievement(achievement)
achievement_state = AchievementState.ENTERING
popup_timer = 0
if achievement_data.is_empty():
if _logger: _logger.error("Could not get achievement %s info when trying to show" % [achievement])
return
popup_title.text = achievement_data.name
popup_body.text = achievement_data.description
showing_popup = true
popup.position.x = starting_popup_pos.x + starting_popup_size.x
popup.modulate = Color.TRANSPARENT
popup.scale = Vector2(0.5, 0.5)
#popup.position.y = starting_popup_pos.y + starting_popup_size.y
if popup_tween:
popup_tween.kill()
audio.play()
popup_tween = create_tween()
popup_tween.set_parallel()
popup_tween.set_ease(Tween.EASE_OUT)
popup_tween.set_trans(Tween.TRANS_BACK)
popup_tween.tween_property(popup, "modulate", Color.WHITE, POPUP_POP_TIME / 1000)
popup_tween.tween_property(popup, "scale", Vector2(1, 1), POPUP_POP_TIME / 1000)
popup_tween.tween_property(popup, "position:x", starting_popup_pos.x, POPUP_POP_TIME / 1000)
popup_tween.chain()
popup_tween.tween_callback(func():
achievement_state = AchievementState.SHOWN
)
_info("Showing achievement %s popup" % [achievement])
func _get_achievement_percents() -> Dictionary:
if not last_percent_pull:
return _get_percents_from_db()
if last_percent_pull + CACHE_TIME <= Time.get_ticks_msec():
return cached_percents
return _get_percents_from_db()
func _get_percents_from_db() -> Dictionary:
last_percent_pull = Time.get_ticks_msec()
return {}
func _send_to_db(achievement: String) -> void:
pass
func _check_trigger(key: String, type: String, value = null):
if not _data:
if (_logger): _logger.warn("Could not find data autoload when checking trigger")
return
if not _data.data.has("achievements"):
return
for achievement in _data.data.achievements:
# If the persister does not have the achievement already
if not _persister.get_value(achievement):
var ach_data = _data.data.achievements[achievement]
var valid = false
match type:
"Data":
if ach_data.has("key") and ach_data.key == key:
valid = ach_data.value <= value
"Trigger":
if ach_data.has("trigger") and ach_data.trigger == key:
valid = true
if valid:
achievement_unlocked.emit(achievement)
_persister.persist_data(achievement, true, PersisterEnums.Scope.PERMANENT)
_info("Added achievement %s to queue" % [achievement])
queue.push_back(achievement)
_send_to_db(achievement)
func _on_mouse_handler_hovered():
hovered = true
func _on_mouse_handler_clicked():
if achievement_state != AchievementState.EXITING:
_exit_popup()
func _on_mouse_handler_unhovered():
hovered = false