Close #9 : Ajout des sons dans le jeu

This commit is contained in:
JonathanMM 2022-01-12 19:25:51 +01:00
parent 1802f87f9f
commit 0c44ca57a0
10 changed files with 202 additions and 24 deletions

View file

@ -10,7 +10,17 @@
</head>
<body>
<div id="contenu">
<h1>SUTOM</h1>
<header>
<div></div>
<h1>SUTOM</h1>
<div>
<a href="#" id="configuration-audio-bouton">
<svg id="configuration-audio-icone">
<use href="#icone-son-active" fill="var(--couleur-icone)"></use>
</svg>
</a>
</div>
</header>
<div id="notification"> </div>
<div id="fin-de-partie-panel">
<div id="victoire-panel">
@ -79,6 +89,56 @@
Merci à Emmanuel pour l'aide sur le dictionnaire.
</p>
</div>
<div style="display: hidden">
<audio preload src="sons/lettre-non-trouve.wav" id="son-lettre-non-trouve"></audio>
<audio preload src="sons/lettre-mal-place.wav" id="son-lettre-mal-place"></audio>
<audio preload src="sons/lettre-bien-place.wav" id="son-lettre-bien-place"></audio>
<svg>
<symbol id="icone-son-active" width="36.471008" height="31.999998" viewBox="0 0 36.471009 31.999998">
<path
d="M 12.341228,3.8810322 7.0667848,7.7592153 v 8.2407827 8.237934 l 1.7467496,1.285129 c 0.9574354,0.703829 3.3310776,2.450578 5.2744426,3.881032 1.940517,1.427605 3.539092,2.595904 3.553339,2.595904 0.01425,0 0.02565,-6.813179 0.02565,-15.999999 0,-9.3292955 -0.0114,-15.9999984 -0.02565,-15.9999984 -0.01425,0 -2.399287,1.7467496 -5.300088,3.8810326 z"
style="stroke: none; stroke-width: 0.00284951"
/>
<path
d="m 29.814423,1.835084 c -0.390383,0.128228 -0.612645,0.4359751 -0.615494,0.843455 0,0.2934995 0.06839,0.4274265 0.40748,0.7836153 0.92894,0.9830809 1.541585,1.7439001 2.174176,2.7070344 1.518789,2.3109527 2.453428,4.8783613 2.795369,7.6651823 0.09403,0.757969 0.11683,1.165449 0.11683,2.165627 0,1.855031 -0.202315,3.308281 -0.69813,5.029385 -0.806411,2.798219 -2.26536,5.288691 -4.396794,7.514158 -0.333392,0.35049 -0.398931,0.473019 -0.398931,0.777916 0,0.262155 0.09118,0.481568 0.270703,0.64114 0.185219,0.170971 0.341942,0.227961 0.606946,0.225112 0.327694,-0.0029 0.421728,-0.05699 0.852004,-0.490116 2.481923,-2.527516 4.282813,-5.847195 5.060729,-9.332145 C 36.835616,16.5699 36.562063,12.603382 35.205696,9.0186987 34.288154,6.5994648 32.948884,4.4594827 31.136596,2.5161169 30.717718,2.0658944 30.566694,1.9319674 30.390024,1.8550306 30.264646,1.8008899 29.954049,1.7894919 29.814423,1.835084 Z"
style="stroke: none; stroke-width: 0.00284951"
/>
<path
d="m 25.072838,6.5624211 c -0.381834,0.1424755 -0.601246,0.4445236 -0.604096,0.8320569 -0.0057,0.2991986 0.07694,0.4502226 0.46447,0.8605521 1.672663,1.7638469 2.695637,3.9408719 3.011933,6.4056989 0.07979,0.621193 0.07124,2.142831 -0.01425,2.766874 -0.339092,2.461976 -1.330721,4.547818 -3.011932,6.331611 -0.373286,0.396082 -0.453072,0.549955 -0.453072,0.846304 0.0057,0.692431 0.769368,1.097062 1.356367,0.720926 0.156723,-0.09688 0.732324,-0.718076 1.125556,-1.211041 1.413357,-1.769546 2.365093,-3.940873 2.687088,-6.149243 0.11398,-0.775067 0.128228,-0.991629 0.128228,-1.966162 0,-0.974532 -0.01425,-1.191095 -0.128228,-1.966162 C 29.3699,12.224397 28.697415,10.460551 27.677291,8.8961698 27.039,7.9187879 26.038822,6.7504888 25.716828,6.6080133 c -0.17667,-0.076937 -0.495815,-0.099733 -0.64399,-0.045592 z"
style="stroke: none; stroke-width: 0.00284951"
/>
<path
d="M 0,15.999998 V 24.24933 L 2.5018698,24.24363 5.00089,24.23513 V 15.999998 7.7649144 L 2.5018698,7.7563658 0,7.7506668 Z"
style="stroke: none; stroke-width: 0.00284951"
/>
<path
d="m 20.373996,11.28121 c -0.515761,0.159572 -0.783615,0.726625 -0.564202,1.202493 0.04274,0.09688 0.165271,0.262155 0.307747,0.413179 0.789314,0.854853 1.202493,1.923419 1.202493,3.103116 0,1.219591 -0.421728,2.27106 -1.265183,3.168655 -0.22796,0.242209 -0.319145,0.447373 -0.319145,0.712378 0.0028,0.46447 0.35049,0.826358 0.823509,0.857702 0.387533,0.0228 0.626892,-0.133927 1.119857,-0.740872 1.270882,-1.564381 1.715405,-3.644524 1.202493,-5.624933 -0.265004,-1.028673 -0.837756,-2.0374 -1.581478,-2.79252 -0.153873,-0.156723 -0.236509,-0.216563 -0.364737,-0.265004 -0.168121,-0.05984 -0.418878,-0.07694 -0.561354,-0.03419 z"
style="stroke: none; stroke-width: 0.00284951"
/>
</symbol>
<symbol id="icone-son-desactive" width="33.355648" height="31.999998" viewBox="0 0 33.355647 31.999997">
<g transform="translate(10.205845,-122.61621)">
<path
style="stroke-linecap: round"
d="m 12.986328,132.82227 a 1.42465,1.42465 0 0 0 -1.007812,0.41796 1.42465,1.42465 0 0 0 0,2.01368 l 8.740234,8.73828 a 1.42465,1.42465 0 0 0 2.013672,0 1.42465,1.42465 0 0 0 0,-2.01367 l -8.738281,-8.73829 a 1.42465,1.42465 0 0 0 -1.007813,-0.41796 z"
/>
<path
style="stroke-linecap: round"
d="m 21.724609,132.82227 a 1.42465,1.42465 0 0 0 -1.005859,0.41796 l -8.740234,8.73829 a 1.42465,1.42465 0 0 0 0,2.01367 1.42465,1.42465 0 0 0 2.015625,0 l 8.738281,-8.73828 a 1.42465,1.42465 0 0 0 0,-2.01368 1.42465,1.42465 0 0 0 -1.007813,-0.41796 z"
/>
<path
d="m 2.1353828,126.49724 -5.274443,3.87818 v 8.24079 8.23793 l 1.7467496,1.28513 c 0.95743539,0.70383 3.3310772,2.45058 5.274443,3.88103 1.9405163,1.42761 3.5390915,2.5959 3.553339,2.5959 0.014248,0 0.025646,-6.81317 0.025646,-15.99999 0,-9.3293 -0.011398,-16 -0.025646,-16 -0.014247,0 -2.3992874,1.74675 -5.3000886,3.88103 z"
style="stroke: none; stroke-width: 0.00284951"
/>
<path
d="m -10.205845,138.61621 v 8.24933 l 2.5018698,-0.006 2.4990203,-0.009 v -8.23508 -8.23509 l -2.4990203,-0.009 -2.5018698,-0.006 z"
style="stroke: none; stroke-width: 0.00284951"
/>
</g>
</symbol>
</svg>
</div>
</div>
<script type="text/javascript">
requirejs(["main"], function (Main) {

View file

@ -6,6 +6,7 @@
--couleur-mal-place: #ffbd00;
--couleur-fond-grille: #0077c7;
--couleur-non-trouve: rgb(112, 112, 112);
--couleur-icone: rgb(200, 200, 200);
}
@font-face {
@ -40,11 +41,26 @@ body {
}
}
h1 {
header {
display: grid;
grid-template-columns: 1fr 6fr 1fr;
align-items: center;
justify-content: space-between;
width: 100%;
border-bottom: 1px solid white;
margin-top: 0.1em;
margin-bottom: 0.25em;
}
h1 {
margin: 0;
}
#configuration-audio-icone {
height: 32px;
width: 40px;
}
#grille {
margin-left: auto;
margin-right: auto;

Binary file not shown.

Binary file not shown.

Binary file not shown.

76
ts/audioPanel.ts Normal file
View file

@ -0,0 +1,76 @@
import Configuration from "./configuration";
import Sauvegardeur from "./sauvegardeur";
export default class AudioPanel {
private readonly _configAudioBouton: HTMLElement;
private readonly _iconeAudio: HTMLElement;
private readonly _audioLettreBienPlace: HTMLAudioElement;
private readonly _audioLettreMalPlace: HTMLAudioElement;
private readonly _audioLettreNonTrouve: HTMLAudioElement;
private _hasAudio: boolean = false;
public constructor(configuration: Configuration) {
this._configAudioBouton = document.getElementById("configuration-audio-bouton") as HTMLElement;
this._iconeAudio = document.getElementById("configuration-audio-icone") as HTMLElement;
this._audioLettreBienPlace = document.getElementById("son-lettre-bien-place") as HTMLAudioElement;
this._audioLettreMalPlace = document.getElementById("son-lettre-mal-place") as HTMLAudioElement;
this._audioLettreNonTrouve = document.getElementById("son-lettre-non-trouve") as HTMLAudioElement;
this.toggleSon(configuration.hasAudio, true);
this._configAudioBouton.addEventListener(
"click",
((event: MouseEvent) => {
event.stopPropagation();
this.toggleSon(!this._hasAudio);
Sauvegardeur.sauvegarderConfig({
...(Sauvegardeur.chargerConfig() ?? Configuration.Default),
hasAudio: this._hasAudio,
});
this._configAudioBouton.blur();
}).bind(this)
);
}
private toggleSon(hasAudio: boolean, chargement: boolean = false): void {
this._hasAudio = hasAudio;
if (!hasAudio) {
this._iconeAudio.innerHTML = '<use href="#icone-son-desactive" fill="var(--couleur-icone)"></use>';
} else {
this._iconeAudio.innerHTML = '<use href="#icone-son-active" fill="var(--couleur-icone)"></use>';
this._audioLettreBienPlace.preload = "auto";
if (!chargement) this.jouerSonLettreBienPlace();
this._audioLettreMalPlace.preload = "auto";
this._audioLettreNonTrouve.preload = "auto";
}
}
public jouerSonLettreBienPlace(callback?: () => void): void {
this.jouerSon(this._audioLettreBienPlace, callback);
}
public jouerSonLettreMalPlace(callback?: () => void): void {
this.jouerSon(this._audioLettreMalPlace, callback);
}
public jouerSonLettreNonTrouve(callback?: () => void): void {
this.jouerSon(this._audioLettreNonTrouve, callback);
}
private jouerSon(baliseAudio: HTMLAudioElement, callback?: () => void): void {
if (!this._hasAudio) {
if (callback) setTimeout(callback, 250);
return;
}
baliseAudio.currentTime = 0;
if (callback) baliseAudio.addEventListener("ended", callback, { once: true });
baliseAudio.play().catch(
(() => {
this._hasAudio = false;
if (callback) setTimeout(callback, 250);
}).bind(this)
);
}
}

5
ts/configuration.ts Normal file
View file

@ -0,0 +1,5 @@
export default class Configuration {
public static Default: Configuration = { hasAudio: false };
hasAudio: boolean = false;
}

View file

@ -7,12 +7,12 @@ import FinDePartiePanel from "./finDePartiePanel";
import NotificationMessage from "./notificationMessage";
import SauvegardeStats from "./sauvegardeStats";
import Sauvegardeur from "./sauvegardeur";
import Configuration from "./configuration";
export default class Gestionnaire {
private readonly _dictionnaire: Dictionnaire;
private readonly _grille: Grille;
private readonly _input: Input;
private readonly _sauvegardeur: Sauvegardeur;
private readonly _victoirePanel: FinDePartiePanel;
private readonly _propositions: Array<string>;
private readonly _resultats: Array<Array<LettreResultat>>;
@ -22,13 +22,14 @@ export default class Gestionnaire {
private _maxNbPropositions: number = 6;
private _datePartieEnCours: Date | undefined;
private _stats: SauvegardeStats = { partiesJouees: 0, partiesGagnees: 0 };
private _config: Configuration = Configuration.Default;
public constructor() {
this._config = Sauvegardeur.chargerConfig() ?? this._config;
this._dictionnaire = new Dictionnaire();
this._motATrouver = this.choisirMot();
this._grille = new Grille(this._motATrouver.length, this._maxNbPropositions, this._motATrouver[0]);
this._grille = new Grille(this._motATrouver.length, this._maxNbPropositions, this._motATrouver[0], this._config);
this._input = new Input(this, this._motATrouver.length);
this._sauvegardeur = new Sauvegardeur();
this._victoirePanel = new FinDePartiePanel();
this._propositions = new Array<string>();
this._resultats = new Array<Array<LettreResultat>>();
@ -38,14 +39,14 @@ export default class Gestionnaire {
}
private chargerSauvegarde(): void {
let sauvegardePartieEnCours = this._sauvegardeur.chargerSauvegardePartieEnCours();
let sauvegardePartieEnCours = Sauvegardeur.chargerSauvegardePartieEnCours();
if (sauvegardePartieEnCours) {
this._datePartieEnCours = sauvegardePartieEnCours.datePartie;
for (let mot of sauvegardePartieEnCours.propositions) {
this.verifierMot(mot, true);
}
}
this._stats = this._sauvegardeur.chargerSauvegardeStats() ?? { partiesJouees: 0, partiesGagnees: 0 };
this._stats = Sauvegardeur.chargerSauvegardeStats() ?? { partiesJouees: 0, partiesGagnees: 0 };
}
private enregistrerPartieDansStats(): void {
@ -53,13 +54,13 @@ export default class Gestionnaire {
if (this._resultats.some((resultat) => resultat.every((item) => item.statut === LettreStatut.BienPlace))) this._stats.partiesGagnees++;
this._stats.dernierePartie = this._datePartieEnCours;
this._sauvegardeur.sauvegarderStats(this._stats);
Sauvegardeur.sauvegarderStats(this._stats);
}
private sauvegarderPartieEnCours(): void {
let datePartieEnCours = this._datePartieEnCours ?? new Date();
this._sauvegardeur.sauvegarderPartieEnCours(this._propositions, datePartieEnCours);
Sauvegardeur.sauvegarderPartieEnCours(this._propositions, datePartieEnCours);
}
private choisirMot(): string {

View file

@ -1,3 +1,5 @@
import AudioPanel from "./audioPanel";
import Configuration from "./configuration";
import LettreResultat from "./lettreResultat";
import { LettreStatut } from "./lettreStatut";
@ -7,12 +9,14 @@ export default class Grille {
private readonly _resultats: Array<Array<LettreResultat>>;
private readonly _longueurMot: number;
private readonly _maxPropositions: number;
private readonly _audioPanel: AudioPanel;
private _indice: Array<string | undefined>;
private _motActuel: number;
public constructor(longueurMot: number, maxPropositions: number, indice: string) {
public constructor(longueurMot: number, maxPropositions: number, indice: string, configuration: Configuration) {
this._grille = document.getElementById("grille") as HTMLElement;
//console.log("Chargement de la grille");
this._audioPanel = new AudioPanel(configuration);
this._longueurMot = longueurMot;
this._maxPropositions = maxPropositions;
@ -119,19 +123,20 @@ export default class Grille {
let cellule = td[numLettre];
let resultat = resultats[numLettre];
cellule.innerHTML = resultat.lettre;
let callback = (() => this.animerLettre(td, resultats, numLettre + 1)).bind(this);
switch (resultat.statut) {
case LettreStatut.BienPlace:
cellule.classList.add("bien-place", "resultat");
this._audioPanel.jouerSonLettreBienPlace(callback);
break;
case LettreStatut.MalPlace:
cellule.classList.add("mal-place", "resultat");
this._audioPanel.jouerSonLettreMalPlace(callback);
break;
default:
cellule.classList.add("non-trouve", "resultat");
this._audioPanel.jouerSonLettreNonTrouve(callback);
}
setTimeout((() => this.animerLettre(td, resultats, numLettre + 1)).bind(this), 250);
}
private mettreAJourIndice(resultats: Array<LettreResultat>): void {

View file

@ -1,31 +1,34 @@
import Configuration from "./configuration";
import SauvegardePartie from "./sauvegardePartie";
import SauvegardeStats from "./sauvegardeStats";
export default class Sauvegardeur {
public constructor() {}
private static readonly _cleStats = "stats";
private static readonly _clePartieEnCours = "partieEnCours";
private static readonly _cleConfiguration = "configuration";
public sauvegarderStats(stats: SauvegardeStats): void {
localStorage.setItem("stats", JSON.stringify(stats));
public static sauvegarderStats(stats: SauvegardeStats): void {
localStorage.setItem(this._cleStats, JSON.stringify(stats));
}
public chargerSauvegardeStats(): SauvegardeStats | undefined {
let dataStats = localStorage.getItem("stats");
public static chargerSauvegardeStats(): SauvegardeStats | undefined {
let dataStats = localStorage.getItem(this._cleStats);
if (!dataStats) return;
let stats = JSON.parse(dataStats) as SauvegardeStats;
return stats;
}
public sauvegarderPartieEnCours(propositions: Array<string>, datePartie: Date): void {
public static sauvegarderPartieEnCours(propositions: Array<string>, datePartie: Date): void {
let partieEnCours: SauvegardePartie = {
propositions: propositions,
datePartie,
};
localStorage.setItem("partieEnCours", JSON.stringify(partieEnCours));
localStorage.setItem(this._clePartieEnCours, JSON.stringify(partieEnCours));
}
public chargerSauvegardePartieEnCours(): { propositions: Array<string>; datePartie: Date } | undefined {
let dataPartieEnCours = localStorage.getItem("partieEnCours");
public static chargerSauvegardePartieEnCours(): { propositions: Array<string>; datePartie: Date } | undefined {
let dataPartieEnCours = localStorage.getItem(this._clePartieEnCours);
if (!dataPartieEnCours) return;
let partieEnCours = JSON.parse(dataPartieEnCours) as SauvegardePartie;
@ -36,7 +39,7 @@ export default class Sauvegardeur {
aujourdhui.getMonth() !== datePartieEnCours.getMonth() ||
aujourdhui.getFullYear() !== datePartieEnCours.getFullYear()
) {
localStorage.removeItem("partieEnCours");
localStorage.removeItem(this._clePartieEnCours);
return;
}
return {
@ -44,4 +47,16 @@ export default class Sauvegardeur {
propositions: partieEnCours.propositions,
};
}
public static sauvegarderConfig(config: Configuration): void {
localStorage.setItem(this._cleConfiguration, JSON.stringify(config));
}
public static chargerConfig(): Configuration | null {
let dataConfig = localStorage.getItem(this._cleConfiguration);
if (!dataConfig) return null;
let config = JSON.parse(dataConfig) as Configuration;
return config;
}
}