diff --git a/TODO b/TODO index e2e3cee..4ffefb3 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,5 @@ -* [ ] Clean up /src/lib directory +* [X] Clean up /src/lib directory * [ ] Actual README.md @@ -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 diff --git a/src/components/Game.svelte b/src/components/Game.svelte index e6f6628..cdc69b4 100644 --- a/src/components/Game.svelte +++ b/src/components/Game.svelte @@ -1,147 +1,36 @@ { - keys.add(ev.key); - }} - on:keyup={ev => { - keys.delete(ev.key); - }} on:beforeunload={() => { save(saveSlot, world); }} /> - { - 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} + { + 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} diff --git a/src/components/GithubCorner.svelte b/src/components/GithubCorner.svelte new file mode 100644 index 0000000..c4544cf --- /dev/null +++ b/src/components/GithubCorner.svelte @@ -0,0 +1,38 @@ + + + +{#if animation} + +{/if} + + + + diff --git a/src/components/InfoModal.svelte b/src/components/InfoModal.svelte new file mode 100644 index 0000000..b533707 --- /dev/null +++ b/src/components/InfoModal.svelte @@ -0,0 +1,81 @@ + + + + +
+
+ {#each tabNames as tabName} + + {/each} +
+
+
+ {#if currentTab == 'Information'} +
+
+ Infinite Minesweeper +
+ An infinite twist on Minesweeper that adds biomes that change the rules of the game. +
+
+ + *Source code available on + GitHub + +
+
+ {:else if currentTab == 'Biomes'} +
+ +
+ {:else if currentTab == 'Controls'} +
+ + Left Click: Reveal tile +
+ Right Click: Flag tile +
+ Middle Click: Move view +
+ Scroll Wheel: Zoom view +
+
+ Arrow Keys: Move view +
+ Open Square Bracket '[': View zoom In +
+ Close Square Bracket ']': View zoom Out +
+
+
+ {/if} +
+
diff --git a/src/components/InfoModalBiomes.svelte b/src/components/InfoModalBiomes.svelte new file mode 100644 index 0000000..93129d1 --- /dev/null +++ b/src/components/InfoModalBiomes.svelte @@ -0,0 +1,133 @@ + + + + +
+
+ + {#if currentBiome == 'Vanilla'} +
+ Vanilla Biome Screenshot +
+

Vanilla

+
+ The standard Minesweeper rules. +
+
+
+ {:else if currentBiome == 'Chocolate'} +
+ Chocolate Biome Screenshot +
+

Chocolate

+
+ The standard Minesweeper rules. +
+ Much more mines than Vanilla biome. +
+
+
+ {:else if currentBiome == 'Waffle'} +
+ Waffle Biome Screenshot +
+

Waffle

+
+ 2x2 checkers of tiles.
+ Dark checkered sections have 3 mines.
+ Light checkered sections have 1 mine. +
+
+
+ {:else if currentBiome == 'Stroopwafel'} +
+ Stroopwafel Biome Screenshot +
+

Stroopwafel

+
+ 3x3 checkers of tiles.
+ Dark checkered sections have 8 mines.
+ Light checkered sections have 1 mine. +
+
+
+ {/if} +
+
+ + +
+
diff --git a/src/components/Modal.svelte b/src/components/Modal.svelte index d1b9819..be4fd37 100644 --- a/src/components/Modal.svelte +++ b/src/components/Modal.svelte @@ -1,44 +1,67 @@ - - + + + {#if visible} -
-
-
- {#if closable} -
-
-
- - {title} - - {#if closable} - - {/if} -
-
- -
+ diff --git a/src/components/Renderer.svelte b/src/components/Renderer.svelte new file mode 100644 index 0000000..8133e7d --- /dev/null +++ b/src/components/Renderer.svelte @@ -0,0 +1,142 @@ + + + { + keys.add(ev.key); + }} + on:keyup={ev => { + keys.delete(ev.key); + }} +/> + + { + 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); + dispatcher('action', { type: 'reveal', pos }); + needsRerender = true; + } else if(ev.button == 2) { + ev.preventDefault(); + const pos = renderer.cameraPos(ev.offsetX, ev.offsetY); + dispatcher('action', { type: 'flag', pos }); + needsRerender = true; + } else if(ev.button == 3) { + // DEBUG: Reset tile + ev.preventDefault(); + const pos = renderer.cameraPos(ev.offsetX, ev.offsetY); + dispatcher('action', { type: 'reset', pos }); + needsRerender = true; + } + }} + 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(); + }} +/> diff --git a/src/lib/game/Chunk.ts b/src/lib/game/Chunk.ts index 9ac63bb..381b292 100644 --- a/src/lib/game/Chunk.ts +++ b/src/lib/game/Chunk.ts @@ -2,7 +2,7 @@ import Pako from "pako"; import { BitIO } from "../BitIO"; import { CHUNK_SIZE } from "./Constants"; -import { getTileType } from "./Generator"; +import { generateTile, getTileType } from "./Generator"; import type { World } from "./World"; import type { ValidTile } from "./tile/Tile"; @@ -65,6 +65,14 @@ export class GeneratedChunk extends Chunk { return this.tiles[chunkTileX + chunkTileY * CHUNK_SIZE]!; } + public resetTileAbsolute(tileX: number, tileY: number): void { + return this.resetTile(tileX - this.chunkX * CHUNK_SIZE, tileY - this.chunkY * CHUNK_SIZE); + } + + public resetTile(chunkTileX: number, chunkTileY: number): void { + this.tiles[chunkTileX + chunkTileY * CHUNK_SIZE] = generateTile(this.world, this.chunkX * CHUNK_SIZE + chunkTileX, this.chunkY * CHUNK_SIZE + chunkTileY); + } + public save(): ArrayBuffer { const io = new BitIO(2048); for(const tile of this.tiles) { diff --git a/src/lib/game/World.ts b/src/lib/game/World.ts index 1ce6734..04f3367 100644 --- a/src/lib/game/World.ts +++ b/src/lib/game/World.ts @@ -123,6 +123,12 @@ export class World { return false; } + public reset(x: number, y: number): void { + const chunk = this.getChunk(Math.floor(x / CHUNK_SIZE), Math.floor(y / CHUNK_SIZE)); + if(!chunk.isGenerated()) return; + chunk.resetTileAbsolute(x, y); + } + public closest0(offsetX: number, offsetY: number): { x: number, y: number } { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b47686b..c77a74d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,4 +1,5 @@ @@ -10,3 +11,5 @@ + + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 742b361..de21921 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,10 +1,10 @@
{#if saveSlot} - world = ev.detail} on:rendererChange={ev => renderer = ev.detail} /> + {/if}
-
-
-
+
{ + layout = (width > height) ? 'vertical' : 'horizontal'; + layoutSide = (width > height) ? 'end' : 'start'; + }} + > +
+
-
-
-
- {#if world} - - Seed: {world.seed} -
- {/if} - - Frame {debugNumFrames} - {Math.round(debugFrameTime * 10) / 10}ms - -
-
-
- - -

- Infinite Minesweeper -

-

CONTROLS

- Left Click: Reveal tile -
- Right Click: Flag tile -
- Middle Click / Arrow Keys: Move view -
- Scroll Wheel: Zoom view -
- Open Bracket '[': Zoom In -
- Close Bracket ']': Zoom Out -
-
+ + + diff --git a/src/style.scss b/src/style.scss index c85d9ab..f2d1d40 100644 --- a/src/style.scss +++ b/src/style.scss @@ -5,6 +5,18 @@ +@layer utilities { + .force-overlap { + @apply grid grid-cols-1 grid-rows-1; + + & > * { + @apply w-full h-full col-start-1 col-end-1 row-start-1 row-end-1; + } + } +} + + + @font-face { font-family: 'Caveat'; src: url("https://fonts.gstatic.com/s/caveat/v18/Wnz6HAc5bAfYB2Q7ZjYY.woff2"); @@ -19,10 +31,14 @@ body { a { - @apply text-blue-500; + @apply text-blue-800; &:visited { - @apply text-purple-500; + @apply text-purple-800; } } + +*::selection { + @apply bg-purple-500 bg-opacity-70 text-white; +} diff --git a/static/biome_chocolate_screenshot.png b/static/biome_chocolate_screenshot.png new file mode 100644 index 0000000..d41a787 Binary files /dev/null and b/static/biome_chocolate_screenshot.png differ diff --git a/static/biome_stroopwafel_screenshot.png b/static/biome_stroopwafel_screenshot.png new file mode 100644 index 0000000..a894781 Binary files /dev/null and b/static/biome_stroopwafel_screenshot.png differ diff --git a/static/biome_vanilla_screenshot.png b/static/biome_vanilla_screenshot.png new file mode 100644 index 0000000..b33fd38 Binary files /dev/null and b/static/biome_vanilla_screenshot.png differ diff --git a/static/biome_waffle_screenshot.png b/static/biome_waffle_screenshot.png new file mode 100644 index 0000000..a824f0a Binary files /dev/null and b/static/biome_waffle_screenshot.png differ diff --git a/tailwind.config.js b/tailwind.config.js index 95118ec..944b2c7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,6 +6,11 @@ export default { fontFamily: { caveat: 'Caveat', segoe: [ 'Segoe UI', 'Tahoma', 'Geneva', 'Verdana', 'sans-serif' ] + }, + boxShadow: { + 'vignette-heavy': '0 0 min(100vw, 100vh) inset black', + 'vignette-medium': '0 0 min(100vw, 100vh) inset rgba(0, 0, 0, 0.7)', + 'vignette-light': '0 0 min(100vw, 100vh) inset rgba(0, 0, 0, 0.5)' } } },