Add option to disable flag hints
This commit is contained in:
parent
7af33026a4
commit
6d93d3394f
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "minesweeper",
|
||||
"version": "0.81.7",
|
||||
"version": "0.81.8",
|
||||
"description": "Just Minesweeper!",
|
||||
"author": "Felix W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -148,6 +148,9 @@
|
|||
<form id="preferencesForm">
|
||||
<label for="preferencesEnableMarks">Enable question marks</label>
|
||||
<input type="checkbox" id="preferencesEnableMarks" />
|
||||
|
||||
<label for="preferencesShowTooManyFlagsHints">Highlight squares with too many flags around them</label>
|
||||
<input type="checkbox" id="preferencesShowTooManyFlagsHints" />
|
||||
<br /><br />
|
||||
|
||||
<button>Save</button>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import confetti from "canvas-confetti";
|
||||
import {formatTime, range} from "./Common";
|
||||
import {Field, Square} from "./Field";
|
||||
import {Preferences} from "./Game";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -12,7 +13,7 @@ export class Display {
|
|||
private readonly minSquareWidth: number = 6;
|
||||
|
||||
private readonly canvas: HTMLCanvasElement;
|
||||
private readonly font: IconFont;
|
||||
private readonly preferences: Preferences;
|
||||
|
||||
private field: Field | null = null;
|
||||
private winTime: number | null = null;
|
||||
|
@ -38,11 +39,11 @@ export class Display {
|
|||
*
|
||||
* @param canvas the canvas to draw the field in
|
||||
* @param field the field to draw
|
||||
* @param font the font to draw symbols with
|
||||
* @param preferences the player's preferences; may be changed at any time
|
||||
*/
|
||||
constructor(canvas: HTMLCanvasElement, field: Field | null, font: IconFont = new BasicIconFont()) {
|
||||
constructor(canvas: HTMLCanvasElement, field: Field | null, preferences: Preferences) {
|
||||
this.canvas = canvas;
|
||||
this.font = font;
|
||||
this.preferences = preferences;
|
||||
|
||||
this.setField(field);
|
||||
}
|
||||
|
@ -52,6 +53,7 @@ export class Display {
|
|||
*/
|
||||
initSymbols(): void {
|
||||
let ctx: CanvasRenderingContext2D;
|
||||
const font = this.preferences.font;
|
||||
const createCanvas = (width: number, height: number) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
|
@ -73,21 +75,21 @@ export class Display {
|
|||
|
||||
this.flagSymbol = createCanvas(this.scale, this.scale);
|
||||
ctx = this.flagSymbol.getContext("2d")!;
|
||||
fillText(this.font.flag, this.font.fontFace, "#f00");
|
||||
fillText(font.flag, font.fontFace, "#f00");
|
||||
|
||||
this.markSymbol = createCanvas(this.scale, this.scale);
|
||||
ctx = this.markSymbol.getContext("2d")!;
|
||||
fillText(this.font.mark, this.font.fontFace, "#00f");
|
||||
fillText(font.mark, font.fontFace, "#00f");
|
||||
|
||||
this.uncoveredMineSymbol = createCanvas(this.scale, this.scale);
|
||||
ctx = this.uncoveredMineSymbol.getContext("2d")!;
|
||||
ctx.fillStyle = "#f00";
|
||||
ctx.fillRect(1, 1, this.scale - 2, this.scale - 2);
|
||||
fillText(this.font.uncoveredMine, this.font.fontFace, "#000");
|
||||
fillText(font.uncoveredMine, font.fontFace, "#000");
|
||||
|
||||
this.mineSymbol = createCanvas(this.scale, this.scale);
|
||||
ctx = this.mineSymbol.getContext("2d")!;
|
||||
fillText(this.font.mine, this.font.fontFace, "#00007b");
|
||||
fillText(font.mine, font.fontFace, "#00007b");
|
||||
|
||||
const digitColors =
|
||||
["", "#0000ff", "#007b00", "#ff0000", "#00007b", "#7b0000", "#007b7b", "#000000", "#7b7b7b"];
|
||||
|
@ -100,15 +102,15 @@ export class Display {
|
|||
|
||||
this.clockSymbol = createCanvas(this.scale, this.scale);
|
||||
ctx = this.clockSymbol.getContext("2d")!;
|
||||
fillText(this.font.clock, this.font.fontFace);
|
||||
fillText(font.clock, font.fontFace);
|
||||
|
||||
this.deathsSymbolA = createCanvas(this.scale, this.scale);
|
||||
ctx = this.deathsSymbolA.getContext("2d")!;
|
||||
fillText(this.font.deaths, this.font.fontFace, "#fff");
|
||||
fillText(font.deaths, font.fontFace, "#fff");
|
||||
|
||||
this.deathsSymbolB = createCanvas(this.scale, this.scale);
|
||||
ctx = this.deathsSymbolB.getContext("2d")!;
|
||||
fillText(this.font.deaths, this.font.fontFace, "#f00");
|
||||
fillText(font.deaths, font.fontFace, "#f00");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -299,6 +301,7 @@ export class Display {
|
|||
private drawHints(ctx: CanvasRenderingContext2D): void {
|
||||
if (this.field === null) return;
|
||||
|
||||
if (this.preferences.showTooManyFlagsHints) {
|
||||
ctx.save();
|
||||
ctx.fillStyle = "rgba(255, 0, 0, 0.3)";
|
||||
this.field.squareList
|
||||
|
@ -306,6 +309,7 @@ export class Display {
|
|||
.filter(it => it.getNeighborCount(it => it.hasMine) < it.getNeighborCount(it => it.hasFlag))
|
||||
.forEach(square => ctx.fillRect(square.x * this.scale, square.y * this.scale, this.scale, this.scale));
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
if (this.hintSquare !== null) {
|
||||
ctx.save();
|
||||
|
|
|
@ -4,7 +4,7 @@ import {$} from "@fwdekker/template";
|
|||
import alea from "alea";
|
||||
import {blurActiveElement, stringToHash} from "./Common";
|
||||
import {customDifficulty, defaultDifficulty, difficulties} from "./Difficulty";
|
||||
import {BasicIconFont, Display, ForkAwesomeFont} from "./Display";
|
||||
import {BasicIconFont, Display, ForkAwesomeFont, IconFont} from "./Display";
|
||||
import {Field} from "./Field";
|
||||
import {Solver} from "./Solver";
|
||||
import {LocalStatistics} from "./Statistics";
|
||||
|
@ -37,6 +37,7 @@ export class Game {
|
|||
private readonly solvableInput: HTMLInputElement;
|
||||
private readonly preferencesOverlay: Overlay;
|
||||
private readonly enableMarksInput: HTMLInputElement;
|
||||
private readonly showTooManyFlagsHintsInput: HTMLInputElement;
|
||||
private readonly preferencesOpenForm: HTMLFormElement;
|
||||
private readonly statisticsOverlay: Overlay;
|
||||
private readonly statisticsDiv: HTMLDivElement;
|
||||
|
@ -57,14 +58,12 @@ export class Game {
|
|||
* Constructs and starts a new game of Minesweeper.
|
||||
*
|
||||
* @param preferences the preferences to play the game under; may be changed during gameplay
|
||||
* @param withForkAwesome whether ForkAwesome can be used
|
||||
*/
|
||||
constructor(preferences: Preferences, withForkAwesome: boolean = true) {
|
||||
constructor(preferences: Preferences) {
|
||||
this.canvas = $("#canvas");
|
||||
|
||||
this.field = null; // Placeholder until `initNewField`
|
||||
this.display =
|
||||
new Display(this.canvas, this.field, withForkAwesome ? new ForkAwesomeFont() : new BasicIconFont());
|
||||
this.display = new Display(this.canvas, this.field, preferences);
|
||||
this.display.startDrawLoop();
|
||||
this.canvas.style.visibility = "unset";
|
||||
|
||||
|
@ -240,6 +239,7 @@ export class Game {
|
|||
|
||||
// Preferences
|
||||
this.enableMarksInput = $("#preferencesEnableMarks");
|
||||
this.showTooManyFlagsHintsInput = $("#preferencesShowTooManyFlagsHints");
|
||||
this.preferencesOpenForm = $("#preferencesOpenForm");
|
||||
this.preferencesOpenForm.addEventListener(
|
||||
"submit",
|
||||
|
@ -247,6 +247,7 @@ export class Game {
|
|||
event.preventDefault();
|
||||
|
||||
this.enableMarksInput.checked = preferences.marksEnabled;
|
||||
this.showTooManyFlagsHintsInput.checked = preferences.showTooManyFlagsHints;
|
||||
this.preferencesOverlay.show();
|
||||
blurActiveElement();
|
||||
}
|
||||
|
@ -255,7 +256,10 @@ export class Game {
|
|||
$("#preferencesOverlay"),
|
||||
$("#preferencesForm"),
|
||||
$("#preferencesCancelForm"),
|
||||
() => preferences.marksEnabled = this.enableMarksInput.checked
|
||||
() => {
|
||||
preferences.marksEnabled = this.enableMarksInput.checked;
|
||||
preferences.showTooManyFlagsHints = this.showTooManyFlagsHintsInput.checked;
|
||||
}
|
||||
);
|
||||
|
||||
// Statistics
|
||||
|
@ -451,11 +455,18 @@ export class Game {
|
|||
|
||||
/**
|
||||
* The player's preferences.
|
||||
*
|
||||
* Contains a mixture of persistent and transient preferences.
|
||||
*/
|
||||
export class Preferences {
|
||||
private readonly storage = new Storage("/tools/minesweeper//preferences");
|
||||
|
||||
|
||||
/**
|
||||
* The font to be used when drawing the display.
|
||||
*/
|
||||
font: IconFont = new BasicIconFont();
|
||||
|
||||
get marksEnabled(): boolean {
|
||||
return this.storage.getBoolean("marksEnabled", true);
|
||||
}
|
||||
|
@ -463,4 +474,12 @@ export class Preferences {
|
|||
set marksEnabled(value: boolean) {
|
||||
this.storage.setBoolean("marksEnabled", value);
|
||||
}
|
||||
|
||||
get showTooManyFlagsHints(): boolean {
|
||||
return this.storage.getBoolean("showTooManyFlagsHints", true);
|
||||
}
|
||||
|
||||
set showTooManyFlagsHints(value: boolean) {
|
||||
this.storage.setBoolean("showTooManyFlagsHints", value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import "../css/main.css";
|
|||
// @ts-ignore
|
||||
import {$, doAfterLoad, footer, header, nav} from "@fwdekker/template";
|
||||
import {waitForForkAwesome} from "./Common";
|
||||
import {BasicIconFont, ForkAwesomeFont} from "./Display";
|
||||
import {Game, Preferences} from "./Game";
|
||||
|
||||
|
||||
|
@ -23,11 +24,16 @@ doAfterLoad(() => {
|
|||
|
||||
|
||||
// Start game
|
||||
const preferences = new Preferences();
|
||||
waitForForkAwesome(
|
||||
() => new Game(new Preferences()),
|
||||
() => {
|
||||
preferences.font = new ForkAwesomeFont();
|
||||
new Game(preferences);
|
||||
},
|
||||
() => {
|
||||
alert("External font could not be loaded. Using fallback font. Is a browser extension blocking fonts?");
|
||||
new Game(new Preferences(), false);
|
||||
preferences.font = new BasicIconFont();
|
||||
new Game(preferences);
|
||||
},
|
||||
3000
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue