Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export type UIContext = {
cut: number;
undo: number;
redo: number;
paused: boolean;
};
32 changes: 23 additions & 9 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Application, Container } from "pixi.js";
import { Application, Container, type Ticker } from "pixi.js";
import type { Writable } from "svelte/store";
import type { Context, UIContext } from "./context.ts";
import { Grid } from "./grid.ts";
Expand All @@ -11,6 +11,16 @@ export async function setup(
stageDefinition: StageDefinition,
uiContext: Writable<UIContext>,
) {
let paused = false;
uiContext.subscribe((v) => {
paused = v.paused;
});
const unlessPaused = (f: (ticker: Ticker) => void) => (ticker: Ticker) => {
if (!paused) {
f(ticker);
}
};

function tick() {
// highlight is re-rendered every tick
const highlight = player.createHighlight(cx);
Expand Down Expand Up @@ -50,19 +60,23 @@ export async function setup(
elapsed: 0,
uiContext,
};
app.ticker.add((ticker) => {
cx.elapsed += ticker.deltaTime;
});
app.ticker.add(
unlessPaused((ticker) => {
cx.elapsed += ticker.deltaTime;
}),
);

const player = new Player(cx, bunnyTexture);
app.ticker.add((ticker) => player.tick(cx, ticker));
app.ticker.add(unlessPaused((ticker) => player.tick(cx, ticker)));
app.stage.addChild(player.sprite);

let cleanup: undefined | (() => void) = undefined;
app.ticker.add(() => {
if (cleanup) cleanup();
cleanup = tick();
});
app.ticker.add(
unlessPaused(() => {
if (cleanup) cleanup();
cleanup = tick();
}),
);

// Append the application canvas to the document body
el.appendChild(app.canvas);
Expand Down
8 changes: 6 additions & 2 deletions src/ui-components/Ability.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
import Key from "./Key.svelte";
type Props = { name: string; key: string; num: number };
const { name, num, key }: Props = $props();
let isMacOS: boolean = $state(false);
$effect(() => {
isMacOS = navigator.userAgent.includes("Mac OS X");
});
</script>
<span style="color: {num > 0 ? "black" : "gray"}">
<Key {key} enabled={num > 0} />
<span style="color: {num > 0 ? "black" : "gray"};">
<Key key={isMacOS ? "⌘+" + key : "Ctrl+" + key} enabled={num > 0} />
<span style="font-size: 1.5rem;">{name}</span>
<span style="font-size: 1.5rem;">✕</span>
<span style="font-size: 2rem; margin-right: 1rem;">{isFinite(num) ? num : "∞"}</span>
Expand Down
51 changes: 50 additions & 1 deletion src/ui-components/Game.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<script lang="ts">
import { goto } from "$app/navigation";
// client-only.
import { Block } from "@/constants.ts";
import type { UIContext } from "@/context.ts";
import { setup } from "@/main.ts";
import type { StageDefinition } from "@/stages.ts";
import Ability from "@/ui-components/Ability.svelte";
import Key from "@/ui-components/Key.svelte";
import { writable } from "svelte/store";

type Props = { stageNum: string; stage: StageDefinition };
Expand All @@ -18,12 +20,24 @@ const uiContext = writable<UIContext>({
paste: 0,
undo: 0,
redo: 0,
paused: false,
});
$effect(() => {
if (container) {
setup(container, stage, uiContext);
}
});

$effect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
event.preventDefault();
uiContext.update((prev) => ({ ...prev, paused: !prev.paused }));
}
};
document.addEventListener("keydown", onKeyDown);
return () => document.removeEventListener("keydown", onKeyDown);
});
</script>

<div bind:this={container} class="container">
Expand All @@ -32,7 +46,9 @@ $effect(() => {
style="position: fixed; left: 0; top: 0; right: 0; display: flex; align-items: baseline;"
>
<span style="font-size: 2rem; margin-right:0.5rem;">Stage:</span>
<span style="font-size: 2.5rem">{stageNum}</span>
<span style="font-size: 2.5rem; margin-right: 1.5rem;">{stageNum}</span>
<Key key="Esc" enabled />
<span style="font-size: 1.5rem; margin-left: 0.5rem;">to pause</span>
<span style="flex-grow: 1"></span>
<span style="font-size: 1.5rem;">Clipboard:</span>
<div class="inventory">
Expand Down Expand Up @@ -62,6 +78,24 @@ $effect(() => {
<Ability key="Z" name="Undo" num={$uiContext.undo} />
<Ability key="Y" name="Redo" num={$uiContext.redo} />
</div>
{#if $uiContext.paused}
<div class="uiBackground menu">
<span style="font-size: 2rem;">Paused</span>
<!-- todo: ボタンのスタイル -->
<button
style="font-size: 1.5rem;"
onclick={() => uiContext.update((prev) => ({ ...prev, paused: false }))}
>Resume</button
>
<button
style="font-size: 1.5rem;"
onclick={() => window.location.reload()}>Restart</button
>
<button style="font-size: 1.5rem;" onclick={() => goto("/")}
>Back to Stage Select</button
>
</div>
{/if}
</div>

<style>
Expand All @@ -85,4 +119,19 @@ $effect(() => {
border-radius: 0.5rem;
border-color: oklch(87.9% 0.169 91.605);
}
.menu {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
margin: auto;
align-items: center;
width: max-content;
height: max-content;
gap: 0.5rem;
border-radius: 1rem;
}
</style>
6 changes: 1 addition & 5 deletions src/ui-components/Key.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
<script lang="ts">
type Props = { key: string; enabled: boolean };
const { key, enabled }: Props = $props();
let isMacOS: boolean = $state(false);
$effect(() => {
isMacOS = navigator.userAgent.includes("Mac OS X");
});
</script>
<span class="key" style="border-color: {enabled ? "black" : "gray"}; background: {enabled ? "white" : "lightgray"};">
{isMacOS ? "⌘+" + key : "Ctrl+" + key}
{key}
</span>
<style>
.key{
Expand Down