parent
0e9c1816c7
commit
6adc3de1c4
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "minesweeper",
|
"name": "minesweeper",
|
||||||
"version": "0.80.0",
|
"version": "0.80.1",
|
||||||
"description": "Just Minesweeper!",
|
"description": "Just Minesweeper!",
|
||||||
"author": "Felix W. Dekker",
|
"author": "Felix W. Dekker",
|
||||||
"browser": "dist/bundle.js",
|
"browser": "dist/bundle.js",
|
||||||
|
|
|
@ -151,6 +151,6 @@
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<!--suppress HtmlUnknownTarget -->
|
<!--suppress HtmlUnknownTarget -->
|
||||||
<script src="bundle.js"></script>
|
<script src="bundle.js?v=%%VERSION_NUMBER%%"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -54,10 +54,12 @@ export function range(length: number, beginAt: number = 0): number[] {
|
||||||
*
|
*
|
||||||
* Taken from https://stackoverflow.com/a/35572620/ (CC BY-SA 3.0).
|
* Taken from https://stackoverflow.com/a/35572620/ (CC BY-SA 3.0).
|
||||||
*
|
*
|
||||||
* @param callback the function to invoke once the font has loaded
|
* @param onSuccess the function to invoke once the font has loaded
|
||||||
|
* @param onFailure the function to invoke if the font cannot be loaded
|
||||||
* @param timeout the maximum time in milliseconds to wait for the font to load
|
* @param timeout the maximum time in milliseconds to wait for the font to load
|
||||||
*/
|
*/
|
||||||
export function waitForForkAwesome(callback: () => void, timeout: number | undefined = undefined): void {
|
export function waitForForkAwesome(onSuccess: () => void, onFailure: () => void,
|
||||||
|
timeout: number | undefined = undefined): void {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
const fontSize = 36;
|
const fontSize = 36;
|
||||||
|
@ -81,9 +83,9 @@ export function waitForForkAwesome(callback: () => void, timeout: number | undef
|
||||||
*/
|
*/
|
||||||
function fontOnload(time: number): void {
|
function fontOnload(time: number): void {
|
||||||
const currentCount = getPixelCount();
|
const currentCount = getPixelCount();
|
||||||
if (failTime !== undefined && time > failTime) alert(`ForkAwesome failed to load after ${timeout}ms.`);
|
if (failTime !== undefined && time > failTime) onFailure();
|
||||||
else if (currentCount < targetPixelCount) requestAnimationFrame(fontOnload);
|
else if (currentCount < targetPixelCount) requestAnimationFrame(fontOnload);
|
||||||
else callback();
|
else onSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,7 +10,9 @@ import {Field, Square} from "./Field";
|
||||||
export class Display {
|
export class Display {
|
||||||
private readonly scale: number = 30;
|
private readonly scale: number = 30;
|
||||||
private readonly minSquareWidth: number = 6;
|
private readonly minSquareWidth: number = 6;
|
||||||
|
|
||||||
private readonly canvas: HTMLCanvasElement;
|
private readonly canvas: HTMLCanvasElement;
|
||||||
|
private readonly font: IconFont;
|
||||||
|
|
||||||
private field: Field | null = null;
|
private field: Field | null = null;
|
||||||
private winTime: number | null = null;
|
private winTime: number | null = null;
|
||||||
|
@ -22,8 +24,8 @@ export class Display {
|
||||||
|
|
||||||
private coverSymbol: HTMLCanvasElement | undefined;
|
private coverSymbol: HTMLCanvasElement | undefined;
|
||||||
private flagSymbol: HTMLCanvasElement | undefined;
|
private flagSymbol: HTMLCanvasElement | undefined;
|
||||||
private mineSymbol: HTMLCanvasElement | undefined;
|
|
||||||
private uncoveredMineSymbol: HTMLCanvasElement | undefined;
|
private uncoveredMineSymbol: HTMLCanvasElement | undefined;
|
||||||
|
private mineSymbol: HTMLCanvasElement | undefined;
|
||||||
private digitSymbols: HTMLCanvasElement[] | undefined;
|
private digitSymbols: HTMLCanvasElement[] | undefined;
|
||||||
private clockSymbol: HTMLCanvasElement | undefined;
|
private clockSymbol: HTMLCanvasElement | undefined;
|
||||||
private deathsSymbolA: HTMLCanvasElement | undefined;
|
private deathsSymbolA: HTMLCanvasElement | undefined;
|
||||||
|
@ -35,9 +37,12 @@ export class Display {
|
||||||
*
|
*
|
||||||
* @param canvas the canvas to draw the field in
|
* @param canvas the canvas to draw the field in
|
||||||
* @param field the field to draw
|
* @param field the field to draw
|
||||||
|
* @param font the font to draw symbols with
|
||||||
*/
|
*/
|
||||||
constructor(canvas: HTMLCanvasElement, field: Field | null) {
|
constructor(canvas: HTMLCanvasElement, field: Field | null, font: IconFont = new BasicIconFont()) {
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
|
this.font = font;
|
||||||
|
|
||||||
this.setField(field);
|
this.setField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +63,7 @@ export class Display {
|
||||||
ctx.font = `${Math.floor(this.scale * 0.55)}px ${font}`;
|
ctx.font = `${Math.floor(this.scale * 0.55)}px ${font}`;
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
ctx.fillText(text, Math.floor(this.scale / 2), Math.floor(this.scale / 2));
|
ctx.fillText(text, Math.floor(this.scale / 2), Math.floor(this.scale / 2), this.scale);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,17 +72,17 @@ export class Display {
|
||||||
|
|
||||||
this.flagSymbol = createCanvas(this.scale, this.scale);
|
this.flagSymbol = createCanvas(this.scale, this.scale);
|
||||||
ctx = this.flagSymbol.getContext("2d")!;
|
ctx = this.flagSymbol.getContext("2d")!;
|
||||||
fillText("\uf024", "ForkAwesome", "#f00");
|
fillText(this.font.flag, this.font.fontFace, "#f00");
|
||||||
|
|
||||||
this.mineSymbol = createCanvas(this.scale, this.scale);
|
|
||||||
ctx = this.mineSymbol.getContext("2d")!;
|
|
||||||
fillText("\uf1e2", "ForkAwesome", "#00007b");
|
|
||||||
|
|
||||||
this.uncoveredMineSymbol = createCanvas(this.scale, this.scale);
|
this.uncoveredMineSymbol = createCanvas(this.scale, this.scale);
|
||||||
ctx = this.uncoveredMineSymbol.getContext("2d")!;
|
ctx = this.uncoveredMineSymbol.getContext("2d")!;
|
||||||
ctx.fillStyle = "#f00";
|
ctx.fillStyle = "#f00";
|
||||||
ctx.fillRect(1, 1, this.scale - 2, this.scale - 2);
|
ctx.fillRect(1, 1, this.scale - 2, this.scale - 2);
|
||||||
fillText("\uf1e2", "ForkAwesome", "#000");
|
fillText(this.font.uncoveredMine, this.font.fontFace, "#000");
|
||||||
|
|
||||||
|
this.mineSymbol = createCanvas(this.scale, this.scale);
|
||||||
|
ctx = this.mineSymbol.getContext("2d")!;
|
||||||
|
fillText(this.font.mine, this.font.fontFace, "#00007b");
|
||||||
|
|
||||||
const digitColors =
|
const digitColors =
|
||||||
["", "#0000ff", "#007b00", "#ff0000", "#00007b", "#7b0000", "#007b7b", "#000000", "#7b7b7b"];
|
["", "#0000ff", "#007b00", "#ff0000", "#00007b", "#7b0000", "#007b7b", "#000000", "#7b7b7b"];
|
||||||
|
@ -90,15 +95,15 @@ export class Display {
|
||||||
|
|
||||||
this.clockSymbol = createCanvas(this.scale, this.scale);
|
this.clockSymbol = createCanvas(this.scale, this.scale);
|
||||||
ctx = this.clockSymbol.getContext("2d")!;
|
ctx = this.clockSymbol.getContext("2d")!;
|
||||||
fillText("\uf017", "ForkAwesome");
|
fillText(this.font.clock, this.font.fontFace);
|
||||||
|
|
||||||
this.deathsSymbolA = createCanvas(this.scale, this.scale);
|
this.deathsSymbolA = createCanvas(this.scale, this.scale);
|
||||||
ctx = this.deathsSymbolA.getContext("2d")!;
|
ctx = this.deathsSymbolA.getContext("2d")!;
|
||||||
fillText("\uf0f9", "ForkAwesome", "#fff");
|
fillText(this.font.deaths, this.font.fontFace, "#fff");
|
||||||
|
|
||||||
this.deathsSymbolB = createCanvas(this.scale, this.scale);
|
this.deathsSymbolB = createCanvas(this.scale, this.scale);
|
||||||
ctx = this.deathsSymbolB.getContext("2d")!;
|
ctx = this.deathsSymbolB.getContext("2d")!;
|
||||||
fillText("\uf0f9", "ForkAwesome", "#f00");
|
fillText(this.font.deaths, this.font.fontFace, "#f00");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -424,3 +429,43 @@ export class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A font that can be used to display icons for the display.
|
||||||
|
*/
|
||||||
|
export interface IconFont {
|
||||||
|
fontFace: string;
|
||||||
|
|
||||||
|
flag: string;
|
||||||
|
uncoveredMine: string;
|
||||||
|
mine: string;
|
||||||
|
clock: string;
|
||||||
|
deaths: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic font that can be displayed in any browser.
|
||||||
|
*/
|
||||||
|
export class BasicIconFont implements IconFont {
|
||||||
|
fontFace = "Courier New";
|
||||||
|
|
||||||
|
flag = "F";
|
||||||
|
uncoveredMine = "X";
|
||||||
|
mine = "Mines";
|
||||||
|
clock = "Time";
|
||||||
|
deaths = "Deaths";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ForkAwesome, which can be used on any browser that does not block external fonts.
|
||||||
|
*/
|
||||||
|
export class ForkAwesomeFont implements IconFont {
|
||||||
|
fontFace = "ForkAwesome";
|
||||||
|
|
||||||
|
flag = "\uf024";
|
||||||
|
uncoveredMine = "\uf1e2";
|
||||||
|
mine = "\uf1e2";
|
||||||
|
clock = "\uf017";
|
||||||
|
deaths = "\uf0f9";
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {$} from "@fwdekker/template";
|
||||||
import alea from "alea";
|
import alea from "alea";
|
||||||
import {stringToHash} from "./Common";
|
import {stringToHash} from "./Common";
|
||||||
import {customDifficulty, defaultDifficulty, difficulties} from "./Difficulty";
|
import {customDifficulty, defaultDifficulty, difficulties} from "./Difficulty";
|
||||||
import {Display} from "./Display";
|
import {BasicIconFont, Display, ForkAwesomeFont} from "./Display";
|
||||||
import {Field} from "./Field";
|
import {Field} from "./Field";
|
||||||
import {Solver} from "./Solver";
|
import {Solver} from "./Solver";
|
||||||
import {LocalStatistics} from "./Statistics";
|
import {LocalStatistics} from "./Statistics";
|
||||||
|
@ -51,12 +51,15 @@ export class Game {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs and starts a new game of Minesweeper.
|
* Constructs and starts a new game of Minesweeper.
|
||||||
|
*
|
||||||
|
* @param withForkAwesome whether ForkAwesome can be used
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor(withForkAwesome: boolean = true) {
|
||||||
this.canvas = $("#canvas");
|
this.canvas = $("#canvas");
|
||||||
|
|
||||||
this.field = null; // Placeholder until `initNewField`
|
this.field = null; // Placeholder until `initNewField`
|
||||||
this.display = new Display(this.canvas, this.field);
|
this.display =
|
||||||
|
new Display(this.canvas, this.field, withForkAwesome ? new ForkAwesomeFont() : new BasicIconFont());
|
||||||
this.display.startDrawLoop();
|
this.display.startDrawLoop();
|
||||||
this.canvas.style.visibility = "unset";
|
this.canvas.style.visibility = "unset";
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,12 @@ doAfterLoad(() => {
|
||||||
|
|
||||||
|
|
||||||
// Start game
|
// Start game
|
||||||
waitForForkAwesome(() => new Game(), 3000);
|
waitForForkAwesome(
|
||||||
|
() => new Game(),
|
||||||
|
() => {
|
||||||
|
alert("External font could not be loaded. Using fallback font. Is a browser extension blocking fonts?");
|
||||||
|
new Game(false);
|
||||||
|
},
|
||||||
|
3000
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue