improve jstris skin
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
background-image: var(--skin-url);
|
background-image: var(--skin-url);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result {
|
.result {
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
.selection {
|
.selection {
|
||||||
background-position-x: calc(-1 * var(--sprite-pos) * var(--cell-size));
|
background-position-x: calc(-1 * var(--sprite-pos) * var(--cell-size));
|
||||||
--nb-sprites: 4;
|
--nb-sprites: 4;
|
||||||
--sprite-pos: 3;
|
--sprite-pos: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.minoes-table {
|
.minoes-table {
|
||||||
@@ -31,7 +32,6 @@
|
|||||||
background-image: url(jstris-skin/jstris-grid.png);
|
background-image: url(jstris-skin/jstris-grid.png);
|
||||||
background-position: bottom;
|
background-position: bottom;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-blend-mode: screen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.matrix td:not(.mino) {
|
tr.matrix td:not(.mino) {
|
||||||
@@ -76,13 +76,19 @@ tr.matrix td:not(.mino) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ghost {
|
.ghost {
|
||||||
--sprite-pos: 0;
|
--sprite-pos: 1;
|
||||||
|
opacity: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
--sprite-pos: 1;
|
--sprite-pos: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.locking.mino {
|
.locking.mino {
|
||||||
filter: saturate(60%) brightness(180%);
|
filter: saturate(60%) brightness(180%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#holdTable .mino,
|
||||||
|
#nextTable .mino {
|
||||||
|
box-shadow: 4px 4px 10px #0002;
|
||||||
|
}
|
||||||
33
index.html
33
index.html
@@ -76,19 +76,10 @@
|
|||||||
<label for="dasInput" class="col-2 col-form-label"><abbr title="Delayed AutoShift : délai initial avant répétition">DAS</abbr></label>
|
<label for="dasInput" class="col-2 col-form-label"><abbr title="Delayed AutoShift : délai initial avant répétition">DAS</abbr></label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Interface</legend>
|
<fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Interface</legend>
|
||||||
<label for="sfxVolumeRange" class="col-2 col-form-label">Volume</label>
|
|
||||||
<div class="col-4 d-flex align-items-baseline"><input name="sfxVolumeRange" id="sfxVolumeRange" class="form-range" type="range" min="0" max="1" step="any" value="0.7"></div>
|
|
||||||
<div class="col-4">
|
|
||||||
<div class="form-check form-switch text-start">
|
|
||||||
<input id="fullscreenCheckbox" type="checkbox" role="switch" class="form-check-input" tabindex="0">
|
|
||||||
<label for="fullscreenCheckbox" class="form-check-label">Plein écran</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-2"></div>
|
|
||||||
<label for="stylesheetSelect" class="col-2 col-form-label">Thème</label>
|
<label for="stylesheetSelect" class="col-2 col-form-label">Thème</label>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<select name="stylesheet" id="stylesheetSelect" class="form-select"
|
<select name="stylesheet" id="stylesheetSelect" class="form-select"
|
||||||
oninput="selectedStyleSheet.href = this.value; skinURLdiv.style.setProperty('display', this.value === 'css/tetrio-skin.css' || this.value === 'css/jstris-skin.css' ? 'flex' : 'none')">
|
oninput="selectedStyleSheet.href = this.value; skinURLSelect.disabled = this.value !== 'css/jstris-skin.css';">
|
||||||
<option value="css/classic.css" selected>Classique</option>
|
<option value="css/classic.css" selected>Classique</option>
|
||||||
<option value="css/neo-classic.css" selected>Néo-classique</option>
|
<option value="css/neo-classic.css" selected>Néo-classique</option>
|
||||||
<option value="css/synthwave.css">Synthwave</option>
|
<option value="css/synthwave.css">Synthwave</option>
|
||||||
@@ -100,15 +91,21 @@
|
|||||||
<option value="css/jstris-skin.css">Sample...</option>
|
<option value="css/jstris-skin.css">Sample...</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="skinURLdiv" class="col-6" style="display: none;">
|
<div class="col-4">
|
||||||
<div class="col-8">
|
<select name="skinURL" id="skinURLSelect" class="form-select" disabled
|
||||||
<select name="skinURL" id="skinURLInput" class="form-select" value="https://i.imgur.com/0l7LFMT.png"
|
oninput="document.documentElement.style.setProperty('--skin-url', `url(${this.value})`)">
|
||||||
oninput="document.documentElement.style.setProperty('--skin-url', `url(${this.value})`)">
|
</select>
|
||||||
</select>
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
<label for="skinURLInput" class="col-4 col-form-label">Sample</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
<label for="skinURLSelect" class="col-2 col-form-label">Sample</label>
|
||||||
|
<label for="sfxVolumeRange" class="col-2 col-form-label">Volume</label>
|
||||||
|
<div class="col-4 d-flex align-items-baseline"><input name="sfxVolumeRange" id="sfxVolumeRange" class="form-range" type="range" min="0" max="1" step="any" value="0.7"></div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="form-check form-switch text-start">
|
||||||
|
<input id="fullscreenCheckbox" type="checkbox" role="switch" class="form-check-input" tabindex="0">
|
||||||
|
<label for="fullscreenCheckbox" class="form-check-label">Plein écran</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2"></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Partie</legend>
|
<fieldset class="row g-2 mb-3 align-items-center text-center"><legend class="text-start">Partie</legend>
|
||||||
<label for="levelInput" class="col-2 col-form-label text-center">Niveau</label>
|
<label for="levelInput" class="col-2 col-form-label text-center">Niveau</label>
|
||||||
|
|||||||
516
js/interface.js
516
js/interface.js
@@ -1,384 +1,404 @@
|
|||||||
const KEY_NAMES = new Proxy({
|
const KEY_NAMES = new Proxy(
|
||||||
["ArrowLeft"] : "←",
|
{
|
||||||
["←"] : "ArrowLeft",
|
['ArrowLeft']: '←',
|
||||||
["ArrowRight"] : "→",
|
['←']: 'ArrowLeft',
|
||||||
["→"] : "ArrowRight",
|
['ArrowRight']: '→',
|
||||||
["ArrowUp"] : "↑",
|
['→']: 'ArrowRight',
|
||||||
["↑"] : "ArrowUp",
|
['ArrowUp']: '↑',
|
||||||
["ArrowDown"] : "↓",
|
['↑']: 'ArrowUp',
|
||||||
["↓"] : "ArrowDown",
|
['ArrowDown']: '↓',
|
||||||
[" "] : "Espace",
|
['↓']: 'ArrowDown',
|
||||||
["Espace"] : " ",
|
[' ']: 'Espace',
|
||||||
["Escape"] : "Échap.",
|
['Espace']: ' ',
|
||||||
["Échap."] : "Escape",
|
['Escape']: 'Échap.',
|
||||||
["Backspace"] : "Ret. arrière",
|
['Échap.']: 'Escape',
|
||||||
["Ret. arrière"]: "Backspace",
|
['Backspace']: 'Ret. arrière',
|
||||||
["Enter"] : "Entrée",
|
['Ret. arrière']: 'Backspace',
|
||||||
["Entrée"] : "Enter",
|
['Enter']: 'Entrée',
|
||||||
}, {
|
['Entrée']: 'Enter',
|
||||||
get(target, key) {
|
},
|
||||||
return key in target? target[key] : key
|
{
|
||||||
}
|
get(target, key) {
|
||||||
})
|
return key in target ? target[key] : key;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.form = settingsForm
|
this.form = settingsForm;
|
||||||
this.load()
|
this.load();
|
||||||
this.modal = new bootstrap.Modal('#settingsModal')
|
this.modal = new bootstrap.Modal('#settingsModal');
|
||||||
settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus())
|
settingsModal.addEventListener('shown.bs.modal', () => resumeButton.focus());
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
this.form.querySelectorAll("[name]").forEach(element => {
|
this.form.querySelectorAll('[name]').forEach(element => {
|
||||||
if (element.name in localStorage)
|
if (element.name in localStorage) element.value = localStorage[element.name];
|
||||||
element.value = localStorage[element.name]
|
|
||||||
})
|
|
||||||
selectedStyleSheet.href = stylesheetSelect.value
|
|
||||||
if (stylesheetSelect.value === "css/tetrio-skin.css" || stylesheetSelect.value === "css/jstris-skin.css") {
|
|
||||||
skinURLdiv.style.setProperty('display', 'flex')
|
|
||||||
} else {
|
|
||||||
skinURLdiv.style.setProperty('display', 'none')
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch('https://konsola5.github.io/jstris-customization-database/jstrisCustomizationDatabase.json')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(json => {
|
|
||||||
for (const group in json) {
|
|
||||||
const optgroup = document.createElement('optgroup');
|
|
||||||
optgroup.label = group;
|
|
||||||
json[group].forEach(skin => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = skin.link;
|
|
||||||
option.textContent = `${skin.name} (${skin.author})`;
|
|
||||||
optgroup.appendChild(option);
|
|
||||||
});
|
|
||||||
skinURLInput.appendChild(optgroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#skinURLInput').select2({
|
|
||||||
templateResult: state => state.id ? $(`<span class="option result" style="--skin-url: url(${state.id})" title="${state.text}"></span>`): state.text,
|
|
||||||
templateSelection: state => state.id ? $(`<span class="option selection" style="--skin-url: url(${state.id})" title="${state.text}"></span>`): state.text,
|
|
||||||
theme: 'bootstrap-5',
|
|
||||||
width: '100%',
|
|
||||||
selectionCssClass: 'form-select',
|
|
||||||
dropdownAutoWidth: true,
|
|
||||||
dropdownParent: $('#settingsModal'),
|
|
||||||
placeholder: "URL de l'image",
|
|
||||||
tags: true,
|
|
||||||
createTag: function(params) {
|
|
||||||
return {
|
|
||||||
id: params.term,
|
|
||||||
text: "Ajouté manuellement",
|
|
||||||
newTag: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (localStorage["skinURL"]) {
|
|
||||||
if ($('#skinURLInput').find("option[value='" + localStorage["skinURL"] + "']").length) {
|
|
||||||
$('#skinURLInput').val(localStorage["skinURL"]).trigger('change');
|
|
||||||
} else {
|
|
||||||
var option = new Option(localStorage["skinURL"], "Ajouté manuellement", localStorage["skinURL"], true, true);
|
|
||||||
$('#skinURLInput').append(option).trigger('change');
|
|
||||||
}
|
|
||||||
document.documentElement.style.setProperty('--skin-url', `url(${localStorage["skinURL"]})`)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
selectedStyleSheet.href = stylesheetSelect.value;
|
||||||
|
skinURLSelect.disabled = stylesheetSelect.value !== 'css/jstris-skin.css';
|
||||||
|
|
||||||
|
fetch(
|
||||||
|
'https://konsola5.github.io/jstris-customization-database/jstrisCustomizationDatabase.json',
|
||||||
|
)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
for (const group in json) {
|
||||||
|
const optgroup = document.createElement('optgroup');
|
||||||
|
optgroup.label = group;
|
||||||
|
json[group].forEach(skin => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = skin.link;
|
||||||
|
option.textContent = `${skin.name} (${skin.author})`;
|
||||||
|
optgroup.appendChild(option);
|
||||||
|
});
|
||||||
|
skinURLSelect.appendChild(optgroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#skinURLSelect').select2({
|
||||||
|
templateResult: state =>
|
||||||
|
state.id
|
||||||
|
? $(`<img class="option result" src="${state.id}" title="${state.text}" loading="lazy"/>`)
|
||||||
|
: state.text,
|
||||||
|
templateSelection: state =>
|
||||||
|
state.id
|
||||||
|
? $(`<span class="option selection" style="--skin-url: url(${state.id})" title="${state.text}" loading="lazy"></span>`)
|
||||||
|
: state.text,
|
||||||
|
theme: 'bootstrap-5',
|
||||||
|
width: '100%',
|
||||||
|
selectionCssClass: 'form-select',
|
||||||
|
dropdownAutoWidth: true,
|
||||||
|
dropdownParent: $('#settingsModal'),
|
||||||
|
placeholder: "URL de l'image",
|
||||||
|
tags: true,
|
||||||
|
});
|
||||||
|
if (localStorage['skinURL']) {
|
||||||
|
if (
|
||||||
|
$('#skinURLSelect').find("option[value='" + localStorage['skinURL'] + "']")
|
||||||
|
.length
|
||||||
|
) {
|
||||||
|
$('#skinURLSelect').val(localStorage['skinURL']).trigger('change');
|
||||||
|
} else {
|
||||||
|
var option = new Option(
|
||||||
|
localStorage['skinURL'],
|
||||||
|
'Ajouté manuellement',
|
||||||
|
localStorage['skinURL'],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
$('#skinURLSelect').append(option).trigger('change');
|
||||||
|
}
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
'--skin-url',
|
||||||
|
`url(${localStorage['skinURL']})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.form.querySelectorAll("[name]").forEach(element => localStorage[element.name] = element.value)
|
this.form
|
||||||
|
.querySelectorAll('[name]')
|
||||||
|
.forEach(element => (localStorage[element.name] = element.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.form.onsubmit = newGame
|
this.form.onsubmit = newGame;
|
||||||
levelInput.name = "startLevel"
|
levelInput.name = 'startLevel';
|
||||||
levelInput.disabled = false
|
levelInput.disabled = false;
|
||||||
titleHeader.innerHTML = "QUATUOR"
|
titleHeader.innerHTML = 'QUATUOR';
|
||||||
resumeButton.innerHTML = "Jouer"
|
resumeButton.innerHTML = 'Jouer';
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
resumeButton.disabled = false
|
resumeButton.disabled = false;
|
||||||
settings.form.classList.remove('was-validated')
|
settings.form.classList.remove('was-validated');
|
||||||
settings.modal.show()
|
settings.modal.show();
|
||||||
settings.form.reportValidity()
|
settings.form.reportValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
getInputs() {
|
getInputs() {
|
||||||
for (let input of this.form.querySelectorAll("input[type='text']")) {
|
for (let input of this.form.querySelectorAll("input[type='text']")) {
|
||||||
this[input.name] = KEY_NAMES[input.value]
|
this[input.name] = KEY_NAMES[input.value];
|
||||||
}
|
}
|
||||||
for (let input of this.form.querySelectorAll("input[type='number'], input[type='range']")) {
|
for (let input of this.form.querySelectorAll("input[type='number'], input[type='range']")) {
|
||||||
this[input.name] = input.valueAsNumber
|
this[input.name] = input.valueAsNumber;
|
||||||
}
|
}
|
||||||
for (let input of this.form.querySelectorAll("input[type='checkbox']")) {
|
for (let input of this.form.querySelectorAll("input[type='checkbox']")) {
|
||||||
this[input.name] = input.checked == true
|
this[input.name] = input.checked == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.keyBind = new Proxy({}, {
|
this.keyBind = new Proxy(
|
||||||
get: (target, key) => target[key.toLowerCase()],
|
{},
|
||||||
set: (target, key, value) => target[key.toLowerCase()] = value,
|
{
|
||||||
has: (target, key) => key.toLowerCase() in target
|
get: (target, key) => target[key.toLowerCase()],
|
||||||
|
set: (target, key, value) => (target[key.toLowerCase()] = value),
|
||||||
})
|
has: (target, key) => key.toLowerCase() in target,
|
||||||
|
},
|
||||||
|
);
|
||||||
for (let actionName in playerActions) {
|
for (let actionName in playerActions) {
|
||||||
this.keyBind[settings[actionName]] = playerActions[actionName]
|
this.keyBind[settings[actionName]] = playerActions[actionName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeKey(input) {
|
function changeKey(input) {
|
||||||
prevValue = input.value
|
prevValue = input.value;
|
||||||
input.value = ""
|
input.value = '';
|
||||||
keyInputs = Array.from(input.form.querySelectorAll("input[type='text']"))
|
keyInputs = Array.from(input.form.querySelectorAll("input[type='text']"));
|
||||||
input.onkeydown = function (event) {
|
input.onkeydown = function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
input.value = KEY_NAMES[event.key]
|
input.value = KEY_NAMES[event.key];
|
||||||
keyInputs.forEach(input => {
|
keyInputs.forEach(input => {
|
||||||
input.setCustomValidity("")
|
input.setCustomValidity('');
|
||||||
input.classList.remove("is-invalid")
|
input.classList.remove('is-invalid');
|
||||||
})
|
});
|
||||||
keyInputs.sort((input1, input2) => {
|
keyInputs.sort((input1, input2) => {
|
||||||
if(input1.value == input2.value) {
|
if (input1.value == input2.value) {
|
||||||
input1.setCustomValidity("Touche déjà utilisée")
|
input1.setCustomValidity('Touche déjà utilisée');
|
||||||
input1.classList.add("is-invalid")
|
input1.classList.add('is-invalid');
|
||||||
input2.setCustomValidity("Touche déjà utilisée")
|
input2.setCustomValidity('Touche déjà utilisée');
|
||||||
input2.classList.add("is-invalid")
|
input2.classList.add('is-invalid');
|
||||||
}
|
}
|
||||||
return input1.value > input2.value
|
return input1.value > input2.value;
|
||||||
})
|
});
|
||||||
if (input.checkValidity()) {
|
if (input.checkValidity()) {
|
||||||
input.blur()
|
input.blur();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
input.onblur = function (event) {
|
input.onblur = function (event) {
|
||||||
if (!input.value) input.value = prevValue
|
if (!input.value) input.value = prevValue;
|
||||||
input.onkeydown = null
|
input.onkeydown = null;
|
||||||
input.onblur = null
|
input.onblur = null;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Stats {
|
class Stats {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.modal = new bootstrap.Modal('#statsModal')
|
this.modal = new bootstrap.Modal('#statsModal');
|
||||||
this.load()
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
this.highScore = Number(localStorage["highScore"]) || 0
|
this.highScore = Number(localStorage['highScore']) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
levelInput.value = localStorage["startLevel"] || 1
|
levelInput.value = localStorage['startLevel'] || 1;
|
||||||
this.score = 0
|
this.score = 0;
|
||||||
this.goal = 0
|
this.goal = 0;
|
||||||
this.combo = 0
|
this.combo = 0;
|
||||||
this.b2b = 0
|
this.b2b = 0;
|
||||||
this.startTime = new Date()
|
this.startTime = new Date();
|
||||||
this.lockDelay = DELAY.LOCK
|
this.lockDelay = DELAY.LOCK;
|
||||||
this.totalClearedLines = 0
|
this.totalClearedLines = 0;
|
||||||
this.nbQuatuors = 0
|
this.nbQuatuors = 0;
|
||||||
this.nbTSpin = 0
|
this.nbTSpin = 0;
|
||||||
this.maxCombo = 0
|
this.maxCombo = 0;
|
||||||
this.maxB2B = 0
|
this.maxB2B = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
set score(score) {
|
set score(score) {
|
||||||
this._score = score
|
this._score = score;
|
||||||
scoreCell.innerText = score.toLocaleString()
|
scoreCell.innerText = score.toLocaleString();
|
||||||
if (score > this.highScore) {
|
if (score > this.highScore) {
|
||||||
this.highScore = score
|
this.highScore = score;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get score() {
|
get score() {
|
||||||
return this._score
|
return this._score;
|
||||||
}
|
}
|
||||||
|
|
||||||
set highScore(highScore) {
|
set highScore(highScore) {
|
||||||
this._highScore = highScore
|
this._highScore = highScore;
|
||||||
highScoreCell.innerText = highScore.toLocaleString()
|
highScoreCell.innerText = highScore.toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
get highScore() {
|
get highScore() {
|
||||||
return this._highScore
|
return this._highScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
set level(level) {
|
set level(level) {
|
||||||
this._level = level
|
this._level = level;
|
||||||
this.goal += level * 5
|
this.goal += level * 5;
|
||||||
if (level <= 20){
|
if (level <= 20) {
|
||||||
this.fallPeriod = 1000 * Math.pow(0.8 - ((level - 1) * 0.007), level - 1)
|
this.fallPeriod = 1000 * Math.pow(0.8 - (level - 1) * 0.007, level - 1);
|
||||||
}
|
}
|
||||||
if (level > 15)
|
if (level > 15) this.lockDelay = 500 * Math.pow(0.9, level - 15);
|
||||||
this.lockDelay = 500 * Math.pow(0.9, level - 15)
|
levelInput.value = level;
|
||||||
levelInput.value = level
|
levelCell.innerText = level;
|
||||||
levelCell.innerText = level
|
messagesSpan.addNewChild('div', {
|
||||||
messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>` })
|
className: 'show-level-animation',
|
||||||
|
innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get level() {
|
get level() {
|
||||||
return this._level
|
return this._level;
|
||||||
}
|
}
|
||||||
|
|
||||||
set goal(goal) {
|
set goal(goal) {
|
||||||
this._goal = goal
|
this._goal = goal;
|
||||||
goalCell.innerText = goal
|
goalCell.innerText = goal;
|
||||||
}
|
}
|
||||||
|
|
||||||
get goal() {
|
get goal() {
|
||||||
return this._goal
|
return this._goal;
|
||||||
}
|
}
|
||||||
|
|
||||||
set combo(combo) {
|
set combo(combo) {
|
||||||
this._combo = combo
|
this._combo = combo;
|
||||||
if (combo > this.maxCombo) this.maxCombo = combo
|
if (combo > this.maxCombo) this.maxCombo = combo;
|
||||||
}
|
}
|
||||||
|
|
||||||
get combo() {
|
get combo() {
|
||||||
return this._combo
|
return this._combo;
|
||||||
}
|
}
|
||||||
|
|
||||||
set b2b(b2b) {
|
set b2b(b2b) {
|
||||||
this._b2b = b2b
|
this._b2b = b2b;
|
||||||
if (b2b > this.maxB2B) this.maxB2B = b2b
|
if (b2b > this.maxB2B) this.maxB2B = b2b;
|
||||||
}
|
}
|
||||||
|
|
||||||
get b2b() {
|
get b2b() {
|
||||||
return this._b2b
|
return this._b2b;
|
||||||
}
|
}
|
||||||
|
|
||||||
set time(time) {
|
set time(time) {
|
||||||
this.startTime = new Date() - time
|
this.startTime = new Date() - time;
|
||||||
ticktack()
|
ticktack();
|
||||||
}
|
}
|
||||||
|
|
||||||
get time() {
|
get time() {
|
||||||
return new Date() - this.startTime
|
return new Date() - this.startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockDown(tSpin, nbClearedLines) {
|
lockDown(tSpin, nbClearedLines) {
|
||||||
this.totalClearedLines += nbClearedLines
|
this.totalClearedLines += nbClearedLines;
|
||||||
if (nbClearedLines == 4) this.nbQuatuors++
|
if (nbClearedLines == 4) this.nbQuatuors++;
|
||||||
if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++
|
if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++;
|
||||||
|
|
||||||
// Cleared lines & T-Spin
|
// Cleared lines & T-Spin
|
||||||
let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]
|
let awardedLineClears = AWARDED_LINE_CLEARS[tSpin][nbClearedLines];
|
||||||
let patternScore = 100 * this.level * awardedLineClears
|
let patternScore = 100 * this.level * awardedLineClears;
|
||||||
if (tSpin) messagesSpan.addNewChild("div", {
|
if (tSpin)
|
||||||
className: "rotate-in-animation",
|
messagesSpan.addNewChild('div', {
|
||||||
innerHTML: tSpin
|
className: 'rotate-in-animation',
|
||||||
})
|
innerHTML: tSpin,
|
||||||
if (nbClearedLines) messagesSpan.addNewChild("div", {
|
});
|
||||||
className: "zoom-in-animation",
|
if (nbClearedLines)
|
||||||
innerHTML: CLEARED_LINES_NAMES[nbClearedLines]
|
messagesSpan.addNewChild('div', {
|
||||||
})
|
className: 'zoom-in-animation',
|
||||||
|
innerHTML: CLEARED_LINES_NAMES[nbClearedLines],
|
||||||
|
});
|
||||||
if (patternScore) {
|
if (patternScore) {
|
||||||
messagesSpan.addNewChild("div", {
|
messagesSpan.addNewChild('div', {
|
||||||
className: "zoom-in-animation",
|
className: 'zoom-in-animation',
|
||||||
style: "animation-delay: .2s",
|
style: 'animation-delay: .2s',
|
||||||
innerHTML: patternScore
|
innerHTML: patternScore,
|
||||||
})
|
});
|
||||||
this.score += patternScore
|
this.score += patternScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combo
|
// Combo
|
||||||
if (nbClearedLines) {
|
if (nbClearedLines) {
|
||||||
this.combo++
|
this.combo++;
|
||||||
if (this.combo >= 1) {
|
if (this.combo >= 1) {
|
||||||
let comboScore = (nbClearedLines == 1 ? 20 : 50) * this.combo * this.level
|
let comboScore = (nbClearedLines == 1 ? 20 : 50) * this.combo * this.level;
|
||||||
if (this.combo == 1) {
|
if (this.combo == 1) {
|
||||||
messagesSpan.addNewChild("div", {
|
messagesSpan.addNewChild('div', {
|
||||||
className: "zoom-in-animation",
|
className: 'zoom-in-animation',
|
||||||
style: "animation-delay: .4s",
|
style: 'animation-delay: .4s',
|
||||||
innerHTML: `COMBO<br/>${comboScore}`
|
innerHTML: `COMBO<br/>${comboScore}`,
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
messagesSpan.addNewChild("div", {
|
messagesSpan.addNewChild('div', {
|
||||||
className: "zoom-in-animation",
|
className: 'zoom-in-animation',
|
||||||
style: "animation-delay: .4s",
|
style: 'animation-delay: .4s',
|
||||||
innerHTML: `COMBO x${this.combo}<br/>${comboScore}`
|
innerHTML: `COMBO x${this.combo}<br/>${comboScore}`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
this.score += comboScore
|
this.score += comboScore;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.combo = -1
|
this.combo = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Back to back sequence
|
// Back to back sequence
|
||||||
if ((nbClearedLines == 4) || (tSpin && nbClearedLines)) {
|
if (nbClearedLines == 4 || (tSpin && nbClearedLines)) {
|
||||||
this.b2b++
|
this.b2b++;
|
||||||
if (this.b2b >= 1) {
|
if (this.b2b >= 1) {
|
||||||
let b2bScore = patternScore / 2
|
let b2bScore = patternScore / 2;
|
||||||
if (this.b2b == 1) {
|
if (this.b2b == 1) {
|
||||||
messagesSpan.addNewChild("div", {
|
messagesSpan.addNewChild('div', {
|
||||||
className: "zoom-in-animation",
|
className: 'zoom-in-animation',
|
||||||
style: "animation-delay: .4s",
|
style: 'animation-delay: .4s',
|
||||||
innerHTML: `BOUT À BOUT<br/>${b2bScore}`
|
innerHTML: `BOUT À BOUT<br/>${b2bScore}`,
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
messagesSpan.addNewChild("div", {
|
messagesSpan.addNewChild('div', {
|
||||||
className: "zoom-in-animation",
|
className: 'zoom-in-animation',
|
||||||
style: "animation-delay: .4s",
|
style: 'animation-delay: .4s',
|
||||||
innerHTML: `BOUT À BOUT x${this.b2b}<br/>${b2bScore}`
|
innerHTML: `BOUT À BOUT x${this.b2b}<br/>${b2bScore}`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
this.score += b2bScore
|
this.score += b2bScore;
|
||||||
}
|
}
|
||||||
} else if (nbClearedLines && !tSpin ) {
|
} else if (nbClearedLines && !tSpin) {
|
||||||
if (this.b2b >= 1) {
|
if (this.b2b >= 1) {
|
||||||
messagesSpan.addNewChild("div", {
|
messagesSpan.addNewChild('div', {
|
||||||
className: "zoom-in-animation",
|
className: 'zoom-in-animation',
|
||||||
style: "animation-delay: .4s",
|
style: 'animation-delay: .4s',
|
||||||
innerHTML: `FIN DU BOUT À BOUT`
|
innerHTML: `FIN DU BOUT À BOUT`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
this.b2b = -1
|
this.b2b = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
if (sfxVolumeRange.value) {
|
if (sfxVolumeRange.value) {
|
||||||
if (nbClearedLines == 4) playSound(quatuorSound, this.combo)
|
if (nbClearedLines == 4) playSound(quatuorSound, this.combo);
|
||||||
else if (nbClearedLines) playSound(lineClearSound, this.combo)
|
else if (nbClearedLines) playSound(lineClearSound, this.combo);
|
||||||
if (tSpin) playSound(tSpinSound, this.combo)
|
if (tSpin) playSound(tSpinSound, this.combo);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.goal -= awardedLineClears
|
this.goal -= awardedLineClears;
|
||||||
if (this.goal <= 0) this.level++
|
if (this.goal <= 0) this.level++;
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
let time = stats.time
|
let time = stats.time;
|
||||||
statsModalScoreCell.innerText = this.score.toLocaleString()
|
statsModalScoreCell.innerText = this.score.toLocaleString();
|
||||||
statsModalHighScoreCell.innerText = this.highScore.toLocaleString()
|
statsModalHighScoreCell.innerText = this.highScore.toLocaleString();
|
||||||
statsModalLevelCell.innerText = this.level
|
statsModalLevelCell.innerText = this.level;
|
||||||
statsModalTimeCell.innerText = this.timeFormat.format(time)
|
statsModalTimeCell.innerText = this.timeFormat.format(time);
|
||||||
statsModaltotalClearedLines.innerText = this.totalClearedLines
|
statsModaltotalClearedLines.innerText = this.totalClearedLines;
|
||||||
statsModaltotalClearedLinesPM.innerText = (stats.totalClearedLines * 60000 / time).toFixed(2)
|
statsModaltotalClearedLinesPM.innerText = (
|
||||||
statsModalNbQuatuors.innerText = this.nbQuatuors
|
(stats.totalClearedLines * 60000) /
|
||||||
statsModalNbTSpin.innerText = this.nbTSpin
|
time
|
||||||
statsModalMaxCombo.innerText = this.maxCombo
|
).toFixed(2);
|
||||||
statsModalMaxB2B.innerText = this.maxB2B
|
statsModalNbQuatuors.innerText = this.nbQuatuors;
|
||||||
this.modal.show()
|
statsModalNbTSpin.innerText = this.nbTSpin;
|
||||||
|
statsModalMaxCombo.innerText = this.maxCombo;
|
||||||
|
statsModalMaxB2B.innerText = this.maxB2B;
|
||||||
|
this.modal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
localStorage["highScore"] = this.highScore
|
localStorage['highScore'] = this.highScore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stats.prototype.timeFormat = new Intl.DateTimeFormat("fr-FR", {
|
Stats.prototype.timeFormat = new Intl.DateTimeFormat('fr-FR', {
|
||||||
hour: "numeric",
|
hour: 'numeric',
|
||||||
minute: "2-digit",
|
minute: '2-digit',
|
||||||
second: "2-digit",
|
second: '2-digit',
|
||||||
timeZone: "UTC"
|
timeZone: 'UTC',
|
||||||
})
|
});
|
||||||
|
|
||||||
function playSound(sound, note=0) {
|
function playSound(sound, note = 0) {
|
||||||
sound.currentTime = 0
|
sound.currentTime = 0;
|
||||||
sound.playbackRate = Math.pow(5/4, note)
|
sound.playbackRate = Math.pow(5 / 4, note);
|
||||||
sound.play()
|
sound.play();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user