214 lines
4.8 KiB
Vue
214 lines
4.8 KiB
Vue
<script setup lang="ts">
|
|
import gsap from "gsap";
|
|
|
|
const { locales, locale, setLocale } = useI18n();
|
|
const store = useSettingsStore();
|
|
const confirmationModal = useConfirmationModal();
|
|
const achievements = useAchievementsStore();
|
|
const { assets } = useAssets();
|
|
const { onRender } = useScreen();
|
|
|
|
const BUTTON_KEYS = [
|
|
"english",
|
|
"german",
|
|
"french",
|
|
"spanish",
|
|
"italian",
|
|
"japanese",
|
|
] as const;
|
|
|
|
const BUTTON_POSITIONS = [
|
|
[15, 32],
|
|
[143, 32],
|
|
[15, 80],
|
|
[143, 80],
|
|
[15, 128],
|
|
[143, 128],
|
|
] as const;
|
|
|
|
const { selected, selectorPosition } = useButtonNavigation({
|
|
buttons: {
|
|
english: [10, 27, 106, 41],
|
|
german: [138, 27, 106, 41],
|
|
french: [10, 75, 106, 41],
|
|
spanish: [138, 75, 106, 41],
|
|
italian: [10, 123, 106, 41],
|
|
japanese: [138, 123, 106, 41],
|
|
},
|
|
initialButton:
|
|
BUTTON_KEYS[locales.value.findIndex((l) => l.code === locale.value)] ??
|
|
BUTTON_KEYS[0]!,
|
|
navigation: {
|
|
english: {
|
|
right: "german",
|
|
down: "french",
|
|
},
|
|
german: {
|
|
left: "english",
|
|
down: "spanish",
|
|
},
|
|
french: {
|
|
up: "english",
|
|
right: "spanish",
|
|
down: "italian",
|
|
},
|
|
spanish: {
|
|
up: "german",
|
|
left: "french",
|
|
down: "japanese",
|
|
},
|
|
italian: {
|
|
up: "french",
|
|
right: "japanese",
|
|
},
|
|
japanese: {
|
|
up: "spanish",
|
|
left: "italian",
|
|
},
|
|
},
|
|
selectorAnimation: {
|
|
duration: 0.1,
|
|
ease: "power2.out",
|
|
},
|
|
});
|
|
|
|
const SLIDE_OFFSET = 48;
|
|
const SLIDE_DURATION = 0.15;
|
|
const OUTRO_OFFSET = 96;
|
|
const OUTRO_DURATION = 0.25;
|
|
const ROW_STAGGER = SLIDE_DURATION + 0.075;
|
|
const ROW_COUNT = 3;
|
|
|
|
const selectedRow = computed(() => {
|
|
const index = BUTTON_KEYS.indexOf(selected.value);
|
|
return Math.floor(index / 2);
|
|
});
|
|
|
|
const animation = reactive({
|
|
rowOffsetY: Array(ROW_COUNT).fill(SLIDE_OFFSET) as number[],
|
|
rowOpacity: Array(ROW_COUNT).fill(0) as number[],
|
|
});
|
|
|
|
const animateIntro = async () => {
|
|
const timeline = gsap.timeline();
|
|
for (let i = 0; i < ROW_COUNT; i++) {
|
|
timeline
|
|
.to(
|
|
animation.rowOffsetY,
|
|
{ [i]: 0, duration: SLIDE_DURATION, ease: "none" },
|
|
i * ROW_STAGGER,
|
|
)
|
|
.to(
|
|
animation.rowOpacity,
|
|
{ [i]: 1, duration: SLIDE_DURATION, ease: "none" },
|
|
i * ROW_STAGGER,
|
|
);
|
|
}
|
|
await timeline;
|
|
};
|
|
|
|
const animateOutro = async () => {
|
|
const timeline = gsap.timeline();
|
|
for (let i = 0; i < ROW_COUNT; i++) {
|
|
timeline
|
|
.to(
|
|
animation.rowOffsetY,
|
|
{ [i]: OUTRO_OFFSET, duration: OUTRO_DURATION, ease: "none" },
|
|
0,
|
|
)
|
|
.to(
|
|
animation.rowOpacity,
|
|
{ [i]: 0, duration: OUTRO_DURATION, ease: "none" },
|
|
0,
|
|
);
|
|
}
|
|
await timeline;
|
|
};
|
|
|
|
onMounted(() => {
|
|
animateIntro();
|
|
});
|
|
|
|
const handleCancel = async () => {
|
|
await animateOutro();
|
|
store.closeSubMenu();
|
|
};
|
|
|
|
const handleConfirm = () => {
|
|
const selectedLocale = locales.value[BUTTON_KEYS.indexOf(selected.value)]!;
|
|
|
|
setLocale(selectedLocale.code);
|
|
|
|
if (!achievements.advancement.languages.includes(selectedLocale.code)) {
|
|
achievements.advancement.languages.push(selectedLocale.code);
|
|
if (achievements.advancement.languages.length === locales.value.length) {
|
|
achievements.unlock("settings_language_try_all");
|
|
}
|
|
}
|
|
|
|
confirmationModal.open({
|
|
text: $t(
|
|
"settings.options.language.confirmation",
|
|
{},
|
|
{ locale: selectedLocale.code },
|
|
),
|
|
onClosed: async () => {
|
|
await animateOutro();
|
|
store.closeSubMenu();
|
|
},
|
|
timeout: 2000,
|
|
});
|
|
};
|
|
|
|
onRender((ctx) => {
|
|
ctx.font = "10px NDS10";
|
|
ctx.fillStyle = "#010101";
|
|
|
|
for (let i = 0; i < locales.value.length; i += 1) {
|
|
const row = Math.floor(i / 2);
|
|
const [x, y] = BUTTON_POSITIONS[i]!;
|
|
const isSelected = selected.value === BUTTON_KEYS[i];
|
|
const buttonImage = isSelected
|
|
? assets.images.settings.bottomScreen.options.languageButtonActive
|
|
: assets.images.settings.bottomScreen.options.languageButton;
|
|
|
|
ctx.save();
|
|
ctx.globalAlpha = animation.rowOpacity[row]!;
|
|
ctx.translate(0, animation.rowOffsetY[row]!);
|
|
|
|
buttonImage.draw(ctx, x, y, isSelected ? { colored: true } : undefined);
|
|
const name = locales.value[i]?.name;
|
|
if (!name) {
|
|
throw new Error(`Missing locale or locale name: ${BUTTON_KEYS[i]}`);
|
|
}
|
|
fillTextHCentered(ctx, name, x, y + 20, 96);
|
|
|
|
ctx.restore();
|
|
}
|
|
});
|
|
|
|
defineOptions({
|
|
render: () => null,
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<CommonButtons
|
|
:y-offset="0"
|
|
:b-label="$t('common.cancel')"
|
|
:a-label="$t('common.confirm')"
|
|
@activate-b="handleCancel"
|
|
@activate-a="handleConfirm"
|
|
/>
|
|
|
|
<CommonButtonSelector
|
|
:rect="[
|
|
selectorPosition[0],
|
|
selectorPosition[1] + animation.rowOffsetY[selectedRow]!,
|
|
selectorPosition[2],
|
|
selectorPosition[3],
|
|
]"
|
|
:opacity="animation.rowOpacity[selectedRow]!"
|
|
/>
|
|
</template>
|