feat(achievements): implement achievements screen
This commit is contained in:
91
app/components/Achievements/BottomScreen.vue
Normal file
91
app/components/Achievements/BottomScreen.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
const { onRender, onClick } = useScreen();
|
||||
const store = useAchievementsScreen();
|
||||
const achievementsStore = useAchievementsStore();
|
||||
const { assets } = useAssets();
|
||||
|
||||
const QUIT_SIZE = assets.images.achievements.quit.rect.width;
|
||||
const QUIT_X = Math.floor(LOGICAL_WIDTH / 2 - QUIT_SIZE / 2);
|
||||
const QUIT_Y = 135;
|
||||
|
||||
useKeyDown((key) => {
|
||||
if (store.isIntro || store.isOutro) return;
|
||||
|
||||
switch (key) {
|
||||
case "NDS_B":
|
||||
case "NDS_A":
|
||||
case "NDS_START": {
|
||||
store.animateOutro();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onClick((x, y) => {
|
||||
if (store.isIntro || store.isOutro) return;
|
||||
|
||||
if (rectContains([QUIT_X, QUIT_Y, QUIT_SIZE, QUIT_SIZE], [x, y])) {
|
||||
store.animateOutro();
|
||||
}
|
||||
});
|
||||
|
||||
onRender((ctx) => {
|
||||
assets.images.home.bottomScreen.background.draw(ctx, 0, 0);
|
||||
|
||||
ctx.globalAlpha = store.isIntro
|
||||
? store.intro.stage1Opacity
|
||||
: store.isOutro
|
||||
? store.outro.stage1Opacity
|
||||
: 1;
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.fillRect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT);
|
||||
|
||||
// achievement list (reversed iteration because they appear in cascade)
|
||||
ctx.globalAlpha = store.isIntro
|
||||
? store.intro.stage2Opacity
|
||||
: store.isOutro
|
||||
? store.outro.stage2Opacity
|
||||
: 1;
|
||||
ctx.font = "7px NDS7";
|
||||
ctx.textBaseline = "top";
|
||||
for (
|
||||
let i = ACHIEVEMENTS.length - 1;
|
||||
i >= ACHIEVEMENTS_TOP_SCREEN_COUNT;
|
||||
i--
|
||||
) {
|
||||
const achievement = ACHIEVEMENTS[i]!;
|
||||
const isUnlocked = achievementsStore.isUnlocked(achievement);
|
||||
|
||||
const offset = store.getItemOffset(i);
|
||||
if (offset === -ACHIEVEMENTS_LINE_HEIGHT) continue;
|
||||
|
||||
const baseY =
|
||||
ACHIEVEMENTS_BOTTOM_START_Y +
|
||||
(i - ACHIEVEMENTS_TOP_SCREEN_COUNT) * ACHIEVEMENTS_LINE_HEIGHT;
|
||||
const y = baseY + offset;
|
||||
|
||||
// needed for the sliding animation
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.fillRect(0, y, LOGICAL_WIDTH, ACHIEVEMENTS_LINE_HEIGHT);
|
||||
|
||||
// TODO: draw ??? for secret ones
|
||||
ctx.fillStyle = isUnlocked ? "#ffffff" : "#666666";
|
||||
drawCheckbox(ctx, ACHIEVEMENTS_X, y, isUnlocked);
|
||||
ctx.fillText(
|
||||
$t(`achievements.${achievement}`).replace("\n", " "),
|
||||
ACHIEVEMENTS_X + CHECKBOX_SIZE + CHECKBOX_TEXT_GAP,
|
||||
y,
|
||||
);
|
||||
}
|
||||
|
||||
ctx.globalAlpha = store.isIntro
|
||||
? store.intro.stage2Opacity
|
||||
: store.isOutro
|
||||
? store.outro.stage3Opacity
|
||||
: 1;
|
||||
assets.images.achievements.quit.draw(ctx, QUIT_X, QUIT_Y);
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
render: () => null,
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user