Add option to disable flag hints

This commit is contained in:
Florine W. Dekker 2020-08-25 16:36:06 +02:00
parent 7af33026a4
commit 6d93d3394f
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
5 changed files with 59 additions and 27 deletions

View File

@ -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",

View File

@ -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>

View File

@ -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,13 +301,15 @@ export class Display {
private drawHints(ctx: CanvasRenderingContext2D): void {
if (this.field === null) return;
ctx.save();
ctx.fillStyle = "rgba(255, 0, 0, 0.3)";
this.field.squareList
.filter(it => !it.isCovered)
.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.preferences.showTooManyFlagsHints) {
ctx.save();
ctx.fillStyle = "rgba(255, 0, 0, 0.3)";
this.field.squareList
.filter(it => !it.isCovered)
.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();

View File

@ -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);
}
}

View File

@ -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
);