Skip to content

Commit

Permalink
new UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Vulae committed Apr 26, 2024
1 parent 945a543 commit 8ba188d
Show file tree
Hide file tree
Showing 17 changed files with 537 additions and 209 deletions.
3 changes: 2 additions & 1 deletion TODO
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

* [ ] Clean up /src/lib directory
* [X] Clean up /src/lib directory

* [ ] Actual README.md

Expand All @@ -18,6 +18,7 @@

* Control methods
* [X] Mouse control method
* [ ] Remove middle click for moving, just use left click instead.
* [ ] Full keyboard control method
* [ ] Mobile control method

Expand Down
133 changes: 11 additions & 122 deletions src/components/Game.svelte
Original file line number Diff line number Diff line change
@@ -1,147 +1,36 @@
<script lang="ts">
import { WorldRenderer } from "$lib/game/Renderer";
import { load, save } from "$lib/game/Save";
import { World } from "$lib/game/World";
import { resize } from "$lib/actions/Resize";
import { onMount, onDestroy } from "svelte";
import { createEventDispatcher } from "svelte";
const dispatcher = createEventDispatcher();
import Renderer from "./Renderer.svelte";
export let saveSlot: string;
export let debugNumFrames: number = 0;
export let debugFrameTime: number = 0;
let canvas: HTMLCanvasElement;
let firstCanvasResize: boolean = true;
let world: World;
let renderer: WorldRenderer;
$: if(world) dispatcher('worldChange', world);
$: if(renderer) dispatcher('rendererChange', renderer);
let needsRerender: boolean = false;
let animFrame: number = -1;
const render = () => {
cancelAnimationFrame(animFrame);
animFrame = requestAnimationFrame(render);
if(needsRerender) {
needsRerender = false;
debugNumFrames++;
const start = performance.now();
renderer.render();
debugFrameTime = performance.now() - start;
}
}
let keys: Set<string> = new Set();
let keysInterval: number = -1;
onMount(async () => {
world = load(saveSlot);
renderer = new WorldRenderer(world, canvas);
// TODO: Clean Up!
clearInterval(keysInterval);
keysInterval = setInterval(() => {
let change: boolean = false;
if(keys.has('[')) {
if(renderer.cameraZoom != renderer.cameraScale(1.04)) {
change = true;
}
}
if(keys.has(']')) {
if(renderer.cameraZoom != renderer.cameraScale(0.96)) {
change = true;
}
}
if(keys.has('ArrowUp')) { renderer.cameraTranslate(0, 10); change = true; }
if(keys.has('ArrowDown')) { renderer.cameraTranslate(0, -10); change = true; }
if(keys.has('ArrowLeft')) { renderer.cameraTranslate(10, 0); change = true; }
if(keys.has('ArrowRight')) { renderer.cameraTranslate(-10, 0); change = true; }
if(change) {
needsRerender = true;
}
}, 1000 / 60);
// FIXME: Why is this not always accurate.
// Sometimes renderer.init() does not load theme fully before returning.
await renderer.init();
setTimeout(() => {
render();
}, 100);
});
onDestroy(() => {
cancelAnimationFrame(animFrame);
clearInterval(keysInterval);
save(saveSlot, world);
});
</script>

<svelte:window
on:keydown={ev => {
keys.add(ev.key);
}}
on:keyup={ev => {
keys.delete(ev.key);
}}
on:beforeunload={() => {
save(saveSlot, world);
}}
/>

<canvas
class="w-full h-full cursor-pointer"
bind:this={canvas}
use:resize={(width, height) => {
canvas.width = width;
canvas.height = height;
renderer.cameraScale(1);
if(firstCanvasResize) {
renderer.cameraTranslate(canvas.width / 2, canvas.height / 2);
firstCanvasResize = false;
}
needsRerender = true;
}}
on:mousedown={ev => {
if(document.pointerLockElement == canvas) return;
if(ev.button == 1) {
canvas.requestPointerLock();
ev.preventDefault();
} else if(ev.button == 0) {
ev.preventDefault();
const pos = renderer.cameraPos(ev.offsetX, ev.offsetY);
world.reveal(pos.x, pos.y);
needsRerender = true;
} else if(ev.button == 2) {
ev.preventDefault();
const pos = renderer.cameraPos(ev.offsetX, ev.offsetY);
world.flag(pos.x, pos.y);
needsRerender = true;
{#if world}
<Renderer {world} on:action={ev => {
if(ev.detail.type == 'reveal') {
world.reveal(ev.detail.pos.x, ev.detail.pos.y);
} else if(ev.detail.type == 'flag') {
world.flag(ev.detail.pos.x, ev.detail.pos.y);
} else if(ev.detail.type == 'reset') {
world.reset(ev.detail.pos.x, ev.detail.pos.y);
}
}}
on:mouseup={ev => {
if(document.pointerLockElement != canvas) return;
if(ev.button != 1) return;
document.exitPointerLock();
}}
on:mousemove={ev => {
if(document.pointerLockElement != canvas) return;
renderer.cameraTranslate(ev.movementX, ev.movementY);
needsRerender = true;
}}
on:wheel|passive={ev => {
const scale = ev.deltaY > 0 ? 0.9 : 1.1;
if(renderer.cameraZoom != renderer.cameraScale(scale)) {
needsRerender = true;
}
}}
on:contextmenu={ev => {
ev.preventDefault();
}}
/>
}} />
{/if}
38 changes: 38 additions & 0 deletions src/components/GithubCorner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!-- https://tholman.com/github-corners/ -->
<script lang="ts">
export let animation: boolean = true;
export let url: string;
export let newTab: boolean = false;
export let colorBg: string = '#151513';
export let colorCat: string = '#FFF';
</script>

{#if animation}
<style lang="scss">
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-25deg);
}
40%, 80% {
transform: rotate(10deg);
}
}
</style>
{/if}

<a href={url} target={newTab ? "_blank" : "_self"} class="github-corner" aria-label="View source on GitHub" title="View source on GitHub">
<svg width="80" height="80" viewBox="0 0 250 250" class="absolute top-0 border-0 right-0 z-[9999]" style:color={colorCat} style:fill={colorBg} aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm" />
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body" />
</svg>
</a>
81 changes: 81 additions & 0 deletions src/components/InfoModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<script lang="ts">
import type { ArrayElement } from "$lib/Util";
import InfoModalBiomes from "./InfoModalBiomes.svelte";
const tabNames = [ 'Information', 'Biomes', 'Controls' ] as const;
let currentTab: ArrayElement<typeof tabNames> = 'Information';
</script>

<style lang="scss">
.tab {
@apply bg-white bg-opacity-0 transition-colors;
&:hover {
@apply bg-opacity-50;
}
&.selected {
@apply bg-opacity-50;
}
}
</style>

<div class="grid px-4 py-2 rounded-2xl bg-white bg-opacity-50">
<div class="px-4 grid grid-flow-col gap-4 items-center font-bold">
{#each tabNames as tabName}
<button
class="tab w-full flex justify-center py-1 px-4 rounded-md"
class:selected={tabName == currentTab}
disabled={tabName == currentTab}
on:click={() => currentTab = tabName}
>
{tabName}
</button>
{/each}
</div>
<div class="w-full h-1 bg-black bg-opacity-50 my-2 rounded-full" />
<div class="w-[32rem] h-64 py-2 font-semibold">
{#if currentTab == 'Information'}
<div class="px-4 w-full h-full flex flex-col justify-between">
<div>
<a class="text-2xl font-bold" href="https://vulae.github.io/infinite-minesweeper">Infinite Minesweeper</a>
<br />
An infinite twist on Minesweeper that adds biomes that change the rules of the game.
</div>
<div class="pt-4">
<span class="float-right text-xs font-extrabold">
*Source code available on
<a class="underline" href="https://github.com/Vulae/infinite-minesweeper">GitHub</a>
</span>
</div>
</div>
{:else if currentTab == 'Biomes'}
<div class="w-full h-full inline">
<InfoModalBiomes />
</div>
{:else if currentTab == 'Controls'}
<div class="px-4">
<span>
Left Click: Reveal tile
<br />
Right Click: Flag tile
<br />
Middle Click: Move view
<br />
Scroll Wheel: Zoom view
<br />
<br />
Arrow Keys: Move view
<br />
Open Square Bracket '[': View zoom In
<br />
Close Square Bracket ']': View zoom Out
<br />
</span>
</div>
{/if}
</div>
</div>
Loading

1 comment on commit 8ba188d

@SeaBear1015
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice!

Please sign in to comment.