204 lines
5.2 KiB
Vue
204 lines
5.2 KiB
Vue
<script setup lang="ts">
|
|
import { SettingsBottomScreenNumberInput as NumberInput } from "#components";
|
|
import { ICONS } from "~/utils/icons";
|
|
import gsap from "gsap";
|
|
|
|
const APP_COLOR_TO_FONT_COLOR: Record<string, string> = {
|
|
"#61829a": "#fbfbfb", // cyan
|
|
"#ba4900": "#fbe3d3", // maroon
|
|
"#fb0018": "#fbcbd3", // red
|
|
"#fb8afb": "#fbebfb", // pink
|
|
"#fb9200": "#fbf3e3", // orange
|
|
"#f3e300": "#fbfbdb", // yellow
|
|
"#aafb00": "#fbfbfb", // lime
|
|
"#00fb00": "#fbfbfb", // green
|
|
"#00a238": "#fbfbfb", // dark green
|
|
"#49db8a": "#ebfbf3", // duck
|
|
"#30baf3": "#fbfbfb", // light blue
|
|
"#0059f3": "#dbebfb", // blue
|
|
"#000092": "#ebebfb", // dark blue
|
|
"#8a00d3": "#f3dbfb", // purple
|
|
"#d300eb": "#fbfbfb", // magenta
|
|
"#fb0092": "#ebe3f3", // fuschia
|
|
};
|
|
|
|
const { assets } = useAssets();
|
|
const achievementAssets =
|
|
assets.images.settings.bottomScreen.clock.achievements;
|
|
const { onRender, onClick } = useScreen();
|
|
|
|
const app = useAppStore();
|
|
const store = useSettingsStore();
|
|
const achievements = useAchievementsStore();
|
|
const achievementsScreen = useAchievementsScreen();
|
|
const confirmationModal = useConfirmationModal();
|
|
|
|
const isAnimating = ref(true);
|
|
|
|
const SLIDE_OFFSET = 96;
|
|
const SLIDE_DURATION = 0.25;
|
|
const ARROW_SLIDE_DELAY = 0.15;
|
|
const ARROW_SLIDE_DURATION = 0.167;
|
|
const VIEW_ALL_OFFSET = -20;
|
|
|
|
const animation = reactive({
|
|
offsetY: SLIDE_OFFSET,
|
|
opacity: 0,
|
|
viewAllOffsetY: VIEW_ALL_OFFSET,
|
|
});
|
|
|
|
const obtainedRef =
|
|
useTemplateRef<InstanceType<typeof NumberInput>>("obtained");
|
|
const totalRef = useTemplateRef<InstanceType<typeof NumberInput>>("total");
|
|
|
|
const animateIntro = async () => {
|
|
isAnimating.value = true;
|
|
await Promise.all([
|
|
obtainedRef.value?.animateIntro(),
|
|
totalRef.value?.animateIntro(),
|
|
gsap
|
|
.timeline()
|
|
.to(animation, { offsetY: 0, duration: SLIDE_DURATION, ease: "none" }, 0)
|
|
.to(animation, { opacity: 1, duration: SLIDE_DURATION, ease: "none" }, 0)
|
|
.to(
|
|
animation,
|
|
{ viewAllOffsetY: 0, duration: ARROW_SLIDE_DURATION, ease: "none" },
|
|
SLIDE_DURATION + ARROW_SLIDE_DELAY,
|
|
),
|
|
]);
|
|
isAnimating.value = false;
|
|
};
|
|
|
|
const animateOutro = async () => {
|
|
isAnimating.value = true;
|
|
await Promise.all([
|
|
obtainedRef.value?.animateOutro(),
|
|
totalRef.value?.animateOutro(),
|
|
gsap
|
|
.timeline()
|
|
.to(
|
|
animation,
|
|
{
|
|
viewAllOffsetY: VIEW_ALL_OFFSET,
|
|
duration: ARROW_SLIDE_DURATION,
|
|
ease: "none",
|
|
},
|
|
0,
|
|
)
|
|
.to(
|
|
animation,
|
|
{ offsetY: SLIDE_OFFSET, duration: SLIDE_DURATION, ease: "none" },
|
|
0,
|
|
)
|
|
.to(animation, { opacity: 0, duration: SLIDE_DURATION, ease: "none" }, 0),
|
|
]);
|
|
};
|
|
|
|
onMounted(() => {
|
|
animateIntro();
|
|
});
|
|
|
|
const handleCancel = async () => {
|
|
if (isAnimating.value) return;
|
|
await animateOutro();
|
|
store.closeSubMenu();
|
|
};
|
|
|
|
const handleReset = () => {
|
|
if (isAnimating.value) return;
|
|
confirmationModal.open({
|
|
text: $t("settings.clock.achievements.resetConfirmation"),
|
|
onConfirm: () => {
|
|
achievements.reset();
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleVisitAll = () => {
|
|
if (isAnimating.value) return;
|
|
achievementsScreen.animateFadeToBlackIntro();
|
|
};
|
|
|
|
onClick((x, y) => {
|
|
if (isAnimating.value) return;
|
|
const viewAllRect = achievementAssets.viewAllButton.rect;
|
|
if (rectContains([127, 2, viewAllRect.width, viewAllRect.height], [x, y])) {
|
|
handleVisitAll();
|
|
}
|
|
});
|
|
|
|
useKeyDown((key) => {
|
|
if (isAnimating.value) return;
|
|
if (key === "NDS_X") {
|
|
handleVisitAll();
|
|
}
|
|
});
|
|
|
|
onRender((ctx) => {
|
|
assets.images.home.topScreen.background.draw(ctx, 0, 0);
|
|
|
|
// slash divider
|
|
ctx.globalAlpha = animation.opacity;
|
|
ctx.translate(0, animation.offsetY);
|
|
ctx.textBaseline = "top";
|
|
ctx.fillStyle = "#515151";
|
|
ctx.fillRect(7 * 16 - 1, 4 * 16 - 1, 16 * 3 + 1, 16 * 3 + 1);
|
|
ctx.fillStyle = achievements.allObtained ? app.color.hex : "#797979";
|
|
ctx.fillRect(7 * 16, 4 * 16, 16 * 3 - 1, 16 * 3 - 1);
|
|
ctx.font = "39px NDS39";
|
|
ctx.letterSpacing = "2px";
|
|
ctx.fillStyle = achievements.allObtained
|
|
? APP_COLOR_TO_FONT_COLOR[app.color.hex]!
|
|
: "#fbfbfb";
|
|
ctx.fillText("/", 7 * 16 + 3, 4 * 16 + 4);
|
|
});
|
|
|
|
onRender((ctx) => {
|
|
ctx.translate(0, animation.viewAllOffsetY);
|
|
|
|
achievementAssets.viewAllButton.draw(ctx, 127, 2);
|
|
achievementAssets.smallTrophy.draw(ctx, 131, 6);
|
|
|
|
ctx.font = "10px NDS10";
|
|
ctx.textBaseline = "top";
|
|
ctx.fillStyle = "#010101";
|
|
|
|
const label = `${ICONS.X} ${$t("settings.clock.achievements.viewAll")}`;
|
|
fillTextHCentered(
|
|
ctx,
|
|
label,
|
|
145,
|
|
7,
|
|
achievementAssets.viewAllButton.rect.width - 18,
|
|
);
|
|
}, 1000);
|
|
</script>
|
|
|
|
<template>
|
|
<NumberInput
|
|
ref="obtained"
|
|
:model-value="achievements.unlocked.length"
|
|
:title="$t('settings.clock.achievements.obtained')"
|
|
:x="4 * 16 - 1"
|
|
:selected="achievements.allObtained"
|
|
:disabled="true"
|
|
/>
|
|
|
|
<NumberInput
|
|
ref="total"
|
|
:model-value="ACHIEVEMENTS.length"
|
|
:title="$t('settings.clock.achievements.total')"
|
|
:x="9 * 16 - 1"
|
|
:selected="achievements.allObtained"
|
|
:disabled="true"
|
|
/>
|
|
|
|
<CommonButtons
|
|
:y-offset="confirmationModal.buttonsYOffset"
|
|
:b-label="$t('common.cancel')"
|
|
:a-label="$t('common.reset')"
|
|
@activate-b="handleCancel()"
|
|
@activate-a="handleReset()"
|
|
/>
|
|
</template>
|