diff --git a/index.html b/index.html
index c314879..659d432 100644
--- a/index.html
+++ b/index.html
@@ -218,8 +218,9 @@
-
-
+
+
+
diff --git a/app.js b/js/app.js
similarity index 100%
rename from app.js
rename to js/app.js
diff --git a/classes.js b/js/game_logic.js
similarity index 60%
rename from classes.js
rename to js/game_logic.js
index 905cb22..c291b2a 100644
--- a/classes.js
+++ b/js/game_logic.js
@@ -453,300 +453,4 @@ Z.prototype.minoesPosition = [
[[ 1, -1], [1, 0], [0, 0], [ 0, 1]],
[[-1, 0], [0, 0], [0, 1], [ 1, 1]],
[[ 0, -1], [-1, 0], [0, 0], [-1, 1]]
-]
-
-
-class Settings {
- constructor() {
- this.form = settingsForm
- this.load()
- this.modal = new bootstrap.Modal('#settingsModal')
- settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus())
- }
-
- load() {
- for (let element of settingsForm.elements) {
- if (element.name) {
- if (localStorage[element.name]) element.value = localStorage[element.name]
- }
- }
- window.document.selectedStyleSheetSet = stylesheetSelect.value
- }
-
- save() {
- for (let element of settingsForm.elements) {
- if (element.name) {
- localStorage[element.name] = element.value
- }
- }
- }
-
- init() {
- this.form.onsubmit = newGame
- levelInput.name = "startLevel"
- levelInput.disabled = false
- titleHeader.innerHTML = "QUATUOR"
- resumeButton.innerHTML = "Jouer"
- }
-
- show() {
- resumeButton.disabled = false
- settings.form.classList.remove('was-validated')
- settings.modal.show()
- settings.form.reportValidity()
- }
-
- getInputs() {
- for (let input of this.form.querySelectorAll("input[type='text']")) {
- this[input.name] = KEY_NAMES[input.value]
- }
- for (let input of this.form.querySelectorAll("input[type='number'], input[type='range']")) {
- this[input.name] = input.valueAsNumber
- }
- for (let input of this.form.querySelectorAll("input[type='checkbox']")) {
- this[input.name] = input.checked == true
- }
-
- this.keyBind = {}
- for (let actionName in playerActions) {
- this.keyBind[settings[actionName]] = playerActions[actionName]
- }
- }
-}
-
-function changeKey(input) {
- prevValue = input.value
- input.value = ""
- input.onkeydown = function (event) {
- event.preventDefault()
- input.value = KEY_NAMES[event.key]
- input.blur()
- }
- input.onblur = function (event) {
- if (input.value == "") input.value = prevValue
- input.onkeydown = null
- input.onblur = null
- }
-}
-
-
-class Stats {
- constructor() {
- this.modal = new bootstrap.Modal('#statsModal')
- this.load()
- }
-
- load() {
- this.highScore = Number(localStorage["highScore"]) || 0
- }
-
- init() {
- levelInput.value = localStorage["startLevel"] || 1
- this.score = 0
- this.goal = 0
- this.combo = 0
- this.b2b = 0
- this.startTime = new Date()
- this.lockDelay = DELAY.LOCK
- this.totalClearedLines = 0
- this.nbQuatuors = 0
- this.nbTSpin = 0
- this.maxCombo = 0
- this.maxB2B = 0
- }
-
- set score(score) {
- this._score = score
- scoreCell.innerText = score.toLocaleString()
- if (score > this.highScore) {
- this.highScore = score
- }
- }
-
- get score() {
- return this._score
- }
-
- set highScore(highScore) {
- this._highScore = highScore
- highScoreCell.innerText = highScore.toLocaleString()
- }
-
- get highScore() {
- return this._highScore
- }
-
- set level(level) {
- this._level = level
- this.goal += level * 5
- if (level <= 20){
- this.fallPeriod = 1000 * Math.pow(0.8 - ((level - 1) * 0.007), level - 1)
- }
- if (level > 15)
- this.lockDelay = 500 * Math.pow(0.9, level - 15)
- levelInput.value = level
- levelCell.innerText = level
- messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `
NIVEAU
${this.level}
` })
- }
-
- get level() {
- return this._level
- }
-
- set goal(goal) {
- this._goal = goal
- goalCell.innerText = goal
- }
-
- get goal() {
- return this._goal
- }
-
- set combo(combo) {
- this._combo = combo
- if (combo > this.maxCombo) this.maxCombo = combo
- }
-
- get combo() {
- return this._combo
- }
-
- set b2b(b2b) {
- this._b2b = b2b
- if (b2b > this.maxB2B) this.maxB2B = b2b
- }
-
- get b2b() {
- return this._b2b
- }
-
- set time(time) {
- this.startTime = new Date() - time
- ticktack()
- }
-
- get time() {
- return new Date() - this.startTime
- }
-
- lockDown(nbClearedLines, tSpin) {
- this.totalClearedLines += nbClearedLines
- if (nbClearedLines == 4) this.nbQuatuors++
- if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++
-
- // Sound
- if (sfxVolumeRange.value) {
- if (nbClearedLines == 4 || (nbClearedLines && tSpin)) playSound(quatuorSound)
- else if (nbClearedLines) playSound(lineClearSound)
- else if (tSpin) playSound(tSpinSound)
- }
-
- // Cleared lines & T-Spin
- let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]
- let patternScore = 100 * this.level * awardedLineClears
- if (tSpin) messagesSpan.addNewChild("div", {
- className: "rotate-in-animation",
- innerHTML: tSpin
- })
- if (nbClearedLines) messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- innerHTML: CLEARED_LINES_NAMES[nbClearedLines]
- })
- if (patternScore) {
- messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- style: "animation-delay: .2s",
- innerHTML: patternScore
- })
- this.score += patternScore
- }
-
- // Combo
- if (nbClearedLines) {
- this.combo++
- if (this.combo >= 1) {
- let comboScore = (nbClearedLines == 1 ? 20 : 50) * this.combo * this.level
- if (this.combo == 1) {
- messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- style: "animation-delay: .4s",
- innerHTML: `COMBO
${comboScore}`
- })
- } else {
- messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- style: "animation-delay: .4s",
- innerHTML: `COMBO x${this.combo}
${comboScore}`
- })
- }
- this.score += comboScore
- }
- } else {
- this.combo = -1
- }
-
- // Back to back sequence
- if ((nbClearedLines == 4) || (tSpin && nbClearedLines)) {
- this.b2b++
- if (this.b2b >= 1) {
- let b2bScore = patternScore / 2
- if (this.b2b == 1) {
- messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- style: "animation-delay: .4s",
- innerHTML: `BOUT EN BOUT
${b2bScore}`
- })
- } else {
- messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- style: "animation-delay: .4s",
- innerHTML: `BOUT EN BOUT x${this.b2b}
${b2bScore}`
- })
- }
- this.score += b2bScore
- }
- } else if (nbClearedLines && !tSpin ) {
- if (this.b2b >= 1) {
- messagesSpan.addNewChild("div", {
- className: "zoom-in-animation",
- style: "animation-delay: .4s",
- innerHTML: `FIN DU BOUT EN BOUT`
- })
- }
- this.b2b = -1
- }
-
- this.goal -= awardedLineClears
- if (this.goal <= 0) this.level++
- }
-
- show() {
- let time = stats.time
- statsModalScoreCell.innerText = this.score.toLocaleString()
- statsModalHighScoreCell.innerText = this.highScore.toLocaleString()
- statsModalLevelCell.innerText = this.level
- statsModalTimeCell.innerText = this.timeFormat.format(time)
- statsModaltotalClearedLines.innerText = this.totalClearedLines
- statsModaltotalClearedLinesPM.innerText = (stats.totalClearedLines * 60000 / time).toFixed(2)
- statsModalNbQuatuors.innerText = this.nbQuatuors
- statsModalNbTSpin.innerText = this.nbTSpin
- statsModalMaxCombo.innerText = this.maxCombo
- statsModalMaxB2B.innerText = this.maxB2B
- this.modal.show()
- }
-
- save() {
- localStorage["highScore"] = this.highScore
- }
-}
-Stats.prototype.timeFormat = new Intl.DateTimeFormat("fr-FR", {
- hour: "numeric",
- minute: "2-digit",
- second: "2-digit",
- timeZone: "UTC"
-})
-
-function playSound(sound) {
- sound.currentTime = 0
- sound.play()
-}
\ No newline at end of file
+]
\ No newline at end of file
diff --git a/js/interface.js b/js/interface.js
new file mode 100644
index 0000000..d4b7176
--- /dev/null
+++ b/js/interface.js
@@ -0,0 +1,297 @@
+
+
+
+class Settings {
+ constructor() {
+ this.form = settingsForm
+ this.load()
+ this.modal = new bootstrap.Modal('#settingsModal')
+ settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus())
+ }
+
+ load() {
+ for (let element of settingsForm.elements) {
+ if (element.name) {
+ if (localStorage[element.name]) element.value = localStorage[element.name]
+ }
+ }
+ window.document.selectedStyleSheetSet = stylesheetSelect.value
+ }
+
+ save() {
+ for (let element of settingsForm.elements) {
+ if (element.name) {
+ localStorage[element.name] = element.value
+ }
+ }
+ }
+
+ init() {
+ this.form.onsubmit = newGame
+ levelInput.name = "startLevel"
+ levelInput.disabled = false
+ titleHeader.innerHTML = "QUATUOR"
+ resumeButton.innerHTML = "Jouer"
+ }
+
+ show() {
+ resumeButton.disabled = false
+ settings.form.classList.remove('was-validated')
+ settings.modal.show()
+ settings.form.reportValidity()
+ }
+
+ getInputs() {
+ for (let input of this.form.querySelectorAll("input[type='text']")) {
+ this[input.name] = KEY_NAMES[input.value]
+ }
+ for (let input of this.form.querySelectorAll("input[type='number'], input[type='range']")) {
+ this[input.name] = input.valueAsNumber
+ }
+ for (let input of this.form.querySelectorAll("input[type='checkbox']")) {
+ this[input.name] = input.checked == true
+ }
+
+ this.keyBind = {}
+ for (let actionName in playerActions) {
+ this.keyBind[settings[actionName]] = playerActions[actionName]
+ }
+ }
+}
+
+function changeKey(input) {
+ prevValue = input.value
+ input.value = ""
+ input.onkeydown = function (event) {
+ event.preventDefault()
+ input.value = KEY_NAMES[event.key]
+ input.blur()
+ }
+ input.onblur = function (event) {
+ if (input.value == "") input.value = prevValue
+ input.onkeydown = null
+ input.onblur = null
+ }
+}
+
+
+class Stats {
+ constructor() {
+ this.modal = new bootstrap.Modal('#statsModal')
+ this.load()
+ }
+
+ load() {
+ this.highScore = Number(localStorage["highScore"]) || 0
+ }
+
+ init() {
+ levelInput.value = localStorage["startLevel"] || 1
+ this.score = 0
+ this.goal = 0
+ this.combo = 0
+ this.b2b = 0
+ this.startTime = new Date()
+ this.lockDelay = DELAY.LOCK
+ this.totalClearedLines = 0
+ this.nbQuatuors = 0
+ this.nbTSpin = 0
+ this.maxCombo = 0
+ this.maxB2B = 0
+ }
+
+ set score(score) {
+ this._score = score
+ scoreCell.innerText = score.toLocaleString()
+ if (score > this.highScore) {
+ this.highScore = score
+ }
+ }
+
+ get score() {
+ return this._score
+ }
+
+ set highScore(highScore) {
+ this._highScore = highScore
+ highScoreCell.innerText = highScore.toLocaleString()
+ }
+
+ get highScore() {
+ return this._highScore
+ }
+
+ set level(level) {
+ this._level = level
+ this.goal += level * 5
+ if (level <= 20){
+ this.fallPeriod = 1000 * Math.pow(0.8 - ((level - 1) * 0.007), level - 1)
+ }
+ if (level > 15)
+ this.lockDelay = 500 * Math.pow(0.9, level - 15)
+ levelInput.value = level
+ levelCell.innerText = level
+ messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `NIVEAU
${this.level}
` })
+ }
+
+ get level() {
+ return this._level
+ }
+
+ set goal(goal) {
+ this._goal = goal
+ goalCell.innerText = goal
+ }
+
+ get goal() {
+ return this._goal
+ }
+
+ set combo(combo) {
+ this._combo = combo
+ if (combo > this.maxCombo) this.maxCombo = combo
+ }
+
+ get combo() {
+ return this._combo
+ }
+
+ set b2b(b2b) {
+ this._b2b = b2b
+ if (b2b > this.maxB2B) this.maxB2B = b2b
+ }
+
+ get b2b() {
+ return this._b2b
+ }
+
+ set time(time) {
+ this.startTime = new Date() - time
+ ticktack()
+ }
+
+ get time() {
+ return new Date() - this.startTime
+ }
+
+ lockDown(nbClearedLines, tSpin) {
+ this.totalClearedLines += nbClearedLines
+ if (nbClearedLines == 4) this.nbQuatuors++
+ if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++
+
+ // Sound
+ if (sfxVolumeRange.value) {
+ if (nbClearedLines == 4 || (nbClearedLines && tSpin)) playSound(quatuorSound)
+ else if (nbClearedLines) playSound(lineClearSound)
+ else if (tSpin) playSound(tSpinSound)
+ }
+
+ // Cleared lines & T-Spin
+ let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]
+ let patternScore = 100 * this.level * awardedLineClears
+ if (tSpin) messagesSpan.addNewChild("div", {
+ className: "rotate-in-animation",
+ innerHTML: tSpin
+ })
+ if (nbClearedLines) messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ innerHTML: CLEARED_LINES_NAMES[nbClearedLines]
+ })
+ if (patternScore) {
+ messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ style: "animation-delay: .2s",
+ innerHTML: patternScore
+ })
+ this.score += patternScore
+ }
+
+ // Combo
+ if (nbClearedLines) {
+ this.combo++
+ if (this.combo >= 1) {
+ let comboScore = (nbClearedLines == 1 ? 20 : 50) * this.combo * this.level
+ if (this.combo == 1) {
+ messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ style: "animation-delay: .4s",
+ innerHTML: `COMBO
${comboScore}`
+ })
+ } else {
+ messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ style: "animation-delay: .4s",
+ innerHTML: `COMBO x${this.combo}
${comboScore}`
+ })
+ }
+ this.score += comboScore
+ }
+ } else {
+ this.combo = -1
+ }
+
+ // Back to back sequence
+ if ((nbClearedLines == 4) || (tSpin && nbClearedLines)) {
+ this.b2b++
+ if (this.b2b >= 1) {
+ let b2bScore = patternScore / 2
+ if (this.b2b == 1) {
+ messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ style: "animation-delay: .4s",
+ innerHTML: `BOUT EN BOUT
${b2bScore}`
+ })
+ } else {
+ messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ style: "animation-delay: .4s",
+ innerHTML: `BOUT EN BOUT x${this.b2b}
${b2bScore}`
+ })
+ }
+ this.score += b2bScore
+ }
+ } else if (nbClearedLines && !tSpin ) {
+ if (this.b2b >= 1) {
+ messagesSpan.addNewChild("div", {
+ className: "zoom-in-animation",
+ style: "animation-delay: .4s",
+ innerHTML: `FIN DU BOUT EN BOUT`
+ })
+ }
+ this.b2b = -1
+ }
+
+ this.goal -= awardedLineClears
+ if (this.goal <= 0) this.level++
+ }
+
+ show() {
+ let time = stats.time
+ statsModalScoreCell.innerText = this.score.toLocaleString()
+ statsModalHighScoreCell.innerText = this.highScore.toLocaleString()
+ statsModalLevelCell.innerText = this.level
+ statsModalTimeCell.innerText = this.timeFormat.format(time)
+ statsModaltotalClearedLines.innerText = this.totalClearedLines
+ statsModaltotalClearedLinesPM.innerText = (stats.totalClearedLines * 60000 / time).toFixed(2)
+ statsModalNbQuatuors.innerText = this.nbQuatuors
+ statsModalNbTSpin.innerText = this.nbTSpin
+ statsModalMaxCombo.innerText = this.maxCombo
+ statsModalMaxB2B.innerText = this.maxB2B
+ this.modal.show()
+ }
+
+ save() {
+ localStorage["highScore"] = this.highScore
+ }
+}
+Stats.prototype.timeFormat = new Intl.DateTimeFormat("fr-FR", {
+ hour: "numeric",
+ minute: "2-digit",
+ second: "2-digit",
+ timeZone: "UTC"
+})
+
+function playSound(sound) {
+ sound.currentTime = 0
+ sound.play()
+}
\ No newline at end of file