From ae1fa3e4ad45e5ac139ba1e0cd5564072d0acb90 Mon Sep 17 00:00:00 2001 From: adrien <adrien@malingrey.fr> Date: Tue, 4 Jul 2023 23:48:57 +0200 Subject: [PATCH] refactoring --- app.js | 312 +-------------------- gui.html | 159 ----------- jsm/common.js | 28 ++ service-worker.js => jsm/service-worker.js | 0 jsm/settings.js | 82 ++++++ jsm/stats.js | 166 +++++++++++ jsm/utils.js | 29 ++ 7 files changed, 310 insertions(+), 466 deletions(-) delete mode 100644 gui.html create mode 100644 jsm/common.js rename service-worker.js => jsm/service-worker.js (100%) create mode 100644 jsm/settings.js create mode 100644 jsm/stats.js create mode 100644 jsm/utils.js diff --git a/app.js b/app.js index 9fe9c76..6f1fb1e 100644 --- a/app.js +++ b/app.js @@ -2,6 +2,10 @@ import * as THREE from 'three' import { OrbitControls } from 'three/addons/controls/OrbitControls.js' import { GUI } from 'three/addons/libs/lil-gui.module.min.js' import * as FPS from 'three/addons/libs/stats.module.js'; +import { T_SPIN } from './jsm/common.js' +import { Settings } from './jsm/settings.js' +import { Stats } from './jsm/stats.js' +import { Scheduler } from './jsm/utils.js' let P = (x, y, z = 0) => new THREE.Vector3(x, y, z) @@ -22,11 +26,6 @@ const ROWS = 24 const SKYLINE = 20 const COLUMNS = 10 -const DELAY = { - LOCK: 500, - FALL: 1000, -} - const COLORS = { I: 0xafeff9, J: 0xb8b4ff, @@ -56,57 +55,6 @@ const ROTATION = { CCW: -1, // CounterClockWise } -const T_SPIN = { - NONE: "", - MINI: "PETITE<br/>PIROUETTE", - T_SPIN: "PIROUETTE" -} - -// score = AWARDED_LINE_CLEARS[tSpin][nbClearedLines] -const AWARDED_LINE_CLEARS = { - [T_SPIN.NONE]: [0, 1, 3, 5, 8], - [T_SPIN.MINI]: [1, 2], - [T_SPIN.T_SPIN]: [4, 8, 12, 16] -} - -const CLEARED_LINES_NAMES = [ - "", - "SOLO", - "DUO", - "TRIO", - "TETRA", -] - - -/* Classes */ - -class Scheduler { - constructor() { - this.intervalTasks = new Map() - this.timeoutTasks = new Map() - } - - setInterval(func, delay, ...args) { - this.intervalTasks.set(func, window.setInterval(func, delay, ...args)) - } - - setTimeout(func, delay, ...args) { - this.timeoutTasks.set(func, window.setTimeout(func, delay, ...args)) - } - - clearInterval(func) { - if (this.intervalTasks.has(func)) - window.clearInterval(this.intervalTasks.get(func)) - this.intervalTasks.delete(func) - } - - clearTimeout(func) { - if (this.timeoutTasks.has(func)) - window.clearTimeout(this.timeoutTasks.get(func)) - this.timeoutTasks.delete(func) - } -} - class Matrix extends THREE.Group { constructor() { @@ -231,23 +179,17 @@ Mino.prototype.geometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSe class MinoMaterial extends THREE.MeshBasicMaterial { - constructor(color) { super({ side: THREE.DoubleSide, color: color, envMap: minoRenderTarget.texture, reflectivity: 0.9, - //roughness: 0, - //metalness: 0.85, - }) } - } class GhostMaterial extends THREE.MeshBasicMaterial { - constructor(color) { super({ side: THREE.DoubleSide, @@ -258,7 +200,6 @@ class GhostMaterial extends THREE.MeshBasicMaterial { opacity: 0.25 }) } - } @@ -477,249 +418,6 @@ Ghost.prototype.minoesPosition = [ ] -class Stats { - constructor() { - this.clock = new THREE.Clock(false) - this.clock.timeFormat = new Intl.DateTimeFormat("fr-FR", { - hour: "numeric", - minute: "2-digit", - second: "2-digit", - timeZone: "UTC" - }) - this.elapsedTime = 0 - - this.init() - } - - init() { - this._level = 0 - this._score = 0 - this.goal = 0 - this.highScore = Number(localStorage["teTraHighScore"]) || 0 - this.combo = 0 - this.b2b = 0 - this.startTime = new Date() - this.lockDelay = DELAY.LOCK - this.totalClearedLines = 0 - this.nbTetra = 0 - this.nbTSpin = 0 - this.maxCombo = 0 - this.maxB2B = 0 - } - - set score(score) { - this._score = score - if (score > this.highScore) { - this.highScore = score - } - } - - get score() { - return this._score - } - - 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) - messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>` }) - } - - get level() { - return this._level - } - - 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 - } - - get time() { - return this.clock.timeFormat.format(this.clock.elapsedTime * 1000) - } - - lockDown(nbClearedLines, tSpin) { - this.totalClearedLines += nbClearedLines - if (nbClearedLines == 4) this.nbTetra++ - if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++ - - // 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<br/>${comboScore}` - }) - } else { - messagesSpan.addNewChild("div", { - className: "zoom-in-animation", - style: "animation-delay: .4s", - innerHTML: `COMBO x${this.combo}<br/>${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 À BOUT<br/>${b2bScore}` - }) - } else { - messagesSpan.addNewChild("div", { - className: "zoom-in-animation", - style: "animation-delay: .4s", - innerHTML: `BOUT À BOUT x${this.b2b}<br/>${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 À BOUT` - }) - } - this.b2b = -1 - } - - this.goal -= awardedLineClears - if (this.goal <= 0) this.level++ - } -} - - - -let jsKeyRenamer = new Proxy({ - ["←"] : "ArrowLeft", - ["→"] : "ArrowRight", - ["↑"] : "ArrowUp", - ["↓"] : "ArrowDown", - ["Espace"] : " ", - ["Échap."] : "Escape", - ["Ret. arrière"]: "Backspace", - ["Entrée"] : "Enter", -}, { - get(obj, keyName) { - return keyName in obj? obj[keyName] : keyName - } -}) -let friendyKeyRenamer = new Proxy({ - ["ArrowLeft"] : "←", - ["ArrowRight"] : "→", - ["ArrowUp"] : "↑", - ["ArrowDown"] : "↓", - [" "] : "Espace", - ["Escape"] : "Échap.", - ["Backspace"] : "Ret. arrière", - ["Enter"] : "Entrée", -}, { - get(obj, keyName) { - return keyName in obj? obj[keyName] : keyName - } -}) - -class Settings { - constructor() { - this.startLevel = 1 - - let keyMaps = { - key: {}, - action: {} - } - - this.key = new Proxy(keyMaps, { - set(km, action, key) { - km.action[key] = action - return km.key[action] = jsKeyRenamer[key] - }, - has(km, action) { - return action in km.key - }, - get(km, action) { - return friendyKeyRenamer[km.key[action]] - } - }) - this.action = new Proxy(keyMaps, { - set(km, key, action) { - km.key[action] = key - return km.action[key] = action - }, - has(km, key) { - return key in km.action - }, - get(km, key) { - return km.action[key] - } - }) - - this.key.moveLeft = "ArrowLeft" - this.key.moveRight = "ArrowRight" - this.key.rotateCCW = "w" - this.key.rotateCW = "ArrowUp" - this.key.softDrop = "ArrowDown" - this.key.hardDrop = " " - this.key.hold = "c" - this.key.pause = "Escape" - - this.arrDelay = 50 - this.dasDelay = 300 - - this.musicVolume = 50 - this.sfxVolume = 50 - } -} - - class TetraGUI extends GUI { constructor(game, settings, stats, debug=false) { super({title: "teTra"}) @@ -1288,5 +986,5 @@ window.onbeforeunload = function (event) { if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('service-worker.js'); + navigator.serviceWorker.register('jsm/service-worker.js'); } \ No newline at end of file diff --git a/gui.html b/gui.html deleted file mode 100644 index 77eb617..0000000 --- a/gui.html +++ /dev/null @@ -1,159 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> - <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css"> - <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> - <script type="importmap"> - { - "imports": { - "three": "https://unpkg.com/three@0.152.2/build/three.module.js?module", - "three/addons/": "https://unpkg.com/three@0.152.2/examples/jsm/" - } - } - </script> - <style> - body { - background: url(https://adrien.malingrey.fr/jeux/.assets/themes/clouds/background.jpg); - } - .lil-gui { - --background-color: rgba(33, 37, 41, 30%); - backdrop-filter: blur(15px); - } - .lil-gui.autoPlace { - left: 15px; - } - .lil-gui .controller.disabled { - opacity: .8; - } - i { - display: inline-block; - width: 100%; - text-align: center; - } - </style> -</head> -<body> - <script type="module"> - import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; - - var game = { - startLevel: 1, - start: () => { - gui.gameFolder.hide() - gui.statsFolder.show() - gui.statsFolder.open() - gui.settingsFolder.close() - }, - } - - var stats = { - level: 1, - goal: 0, - score: 0, - highScore:0, - time: "00:00:00", - } - - var settings = { - moveLeftKey : "ArrowLeft", - moveRightKey: "ArrowRight", - rotateCCWKey: "w", - rotateCWKey : "ArrowUp", - softDropKey : "ArrowDown", - hardDropKey : " ", - holdKey : "c", - pauseKey : "Escape", - - arrDelay: 50, - dasDelay: 300, - - musicVolume: 50, - sfxVolume : 50, - }; - - const KEY_NAMES = { - ["ArrowLeft"] : "←", - ["ArrowRight"] : "→", - ["ArrowUp"] : "↑", - ["ArrowDown"] : "↓", - [" "] : "Espace", - ["Escape"] : "Échap.", - ["Backspace"] : "Ret. arrière", - ["Enter"] : "Entrée", - ["←"] : "ArrowLeft", - ["→"] : "ArrowRight", - ["↑"] : "ArrowUp", - ["↓"] : "ArrowDown", - ["Espace"] : " ", - ["Échap."] : "Escape", - ["Ret. arrière"]: "Backspace", - ["Entrée"] : "Enter", - } - - function changeKey(event) { - const input = event.target - let prevValue = input.value - input.value = "" - input.onkeydown = function (event) { - event.preventDefault() - input.value = KEY_NAMES[event.key] || event.key - input.blur() - } - input.onblur = function (event) { - if (input.value == "") input.value = prevValue - input.onkeydown = null - input.onblur = null - } - } - - class Gui extends GUI { - constructor() { - super({title: "teTra"}); - - this.gameFolder = this.addFolder("Partie") - this.gameFolder.add(game, "startLevel").name("Niveau").min(1).max(15).step(1) - this.gameFolder.add(game, "start").name("Commencer") - this.gameFolder.open() - - this.statsFolder = this.addFolder("Stats") - this.statsFolder.add(stats,"level").name("Niveau").disable() - this.statsFolder.add(stats,"goal").name("Objectif").disable() - this.statsFolder.add(stats,"score").name("Score").disable() - this.statsFolder.add(stats,"highScore").name("Meilleur score").disable() - this.statsFolder.add(stats,"time").name("Temps").disable() - this.statsFolder.hide() - - this.settingsFolder = this.addFolder("Options"); - this.settingsFolder.close() - - this.settingsFolder.keyMapping = this.settingsFolder.addFolder("Commandes") - this.settingsFolder.keyMapping.add(settings,"moveLeftKey").name('<i class="bi bi-arrow-left"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"moveRightKey").name('<i class="bi bi-arrow-right"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"rotateCCWKey").name('<i class="bi bi-arrow-counterclockwise"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"rotateCWKey").name('<i class="bi bi-arrow-clockwise"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"softDropKey").name('<i class="bi bi-arrow-down-short"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"hardDropKey").name('<i class="bi bi-download"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"holdKey").name('<i class="bi bi-arrow-left-right"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.add(settings,"pauseKey").name('<i class="bi bi-pause"></i>').domElement.onclick = changeKey - this.settingsFolder.keyMapping.open() - - this.settingsFolder.delayFolder = this.settingsFolder.addFolder("Répétition automatique") - this.settingsFolder.delayFolder.add(settings,"arrDelay").name("ARR (ms)").min(2).max(200); - this.settingsFolder.delayFolder.add(settings,"dasDelay").name("DAS (ms)").min(100).max(500).step(5); - this.settingsFolder.delayFolder.open() - - this.settingsFolder.volumeFolder = this.settingsFolder.addFolder("Volume") - this.settingsFolder.volumeFolder.add(settings,"musicVolume").name("Musique").min(0).max(100); - this.settingsFolder.volumeFolder.add(settings,"sfxVolume").name("SFX").min(0).max(100) - this.settingsFolder.volumeFolder.open() - - } - } - - var gui = new Gui(); - - </script> -</body> -</html> diff --git a/jsm/common.js b/jsm/common.js new file mode 100644 index 0000000..121918d --- /dev/null +++ b/jsm/common.js @@ -0,0 +1,28 @@ +const DELAY = { + LOCK: 500, + FALL: 1000, +} + +const T_SPIN = { + NONE: "", + MINI: "PETITE<br/>PIROUETTE", + T_SPIN: "PIROUETTE" +} + +// score = AWARDED_LINE_CLEARS[tSpin][nbClearedLines] +const AWARDED_LINE_CLEARS = { + [T_SPIN.NONE]: [0, 1, 3, 5, 8], + [T_SPIN.MINI]: [1, 2], + [T_SPIN.T_SPIN]: [4, 8, 12, 16] +} + +const CLEARED_LINES_NAMES = [ + "", + "SOLO", + "DUO", + "TRIO", + "TETRA", +] + + +export { DELAY, T_SPIN, AWARDED_LINE_CLEARS, CLEARED_LINES_NAMES } \ No newline at end of file diff --git a/service-worker.js b/jsm/service-worker.js similarity index 100% rename from service-worker.js rename to jsm/service-worker.js diff --git a/jsm/settings.js b/jsm/settings.js new file mode 100644 index 0000000..45ca26b --- /dev/null +++ b/jsm/settings.js @@ -0,0 +1,82 @@ +let jsKeyRenamer = new Proxy({ + ["←"] : "ArrowLeft", + ["→"] : "ArrowRight", + ["↑"] : "ArrowUp", + ["↓"] : "ArrowDown", + ["Espace"] : " ", + ["Échap."] : "Escape", + ["Ret. arrière"]: "Backspace", + ["Entrée"] : "Enter", +}, { + get(obj, keyName) { + return keyName in obj? obj[keyName] : keyName + } +}) +let friendyKeyRenamer = new Proxy({ + ["ArrowLeft"] : "←", + ["ArrowRight"] : "→", + ["ArrowUp"] : "↑", + ["ArrowDown"] : "↓", + [" "] : "Espace", + ["Escape"] : "Échap.", + ["Backspace"] : "Ret. arrière", + ["Enter"] : "Entrée", +}, { + get(obj, keyName) { + return keyName in obj? obj[keyName] : keyName + } +}) + +class Settings { + constructor() { + this.startLevel = 1 + + let keyMaps = { + key: {}, + action: {} + } + + this.key = new Proxy(keyMaps, { + set(km, action, key) { + km.action[key] = action + return km.key[action] = jsKeyRenamer[key] + }, + has(km, action) { + return action in km.key + }, + get(km, action) { + return friendyKeyRenamer[km.key[action]] + } + }) + this.action = new Proxy(keyMaps, { + set(km, key, action) { + km.key[action] = key + return km.action[key] = action + }, + has(km, key) { + return key in km.action + }, + get(km, key) { + return km.action[key] + } + }) + + this.key.moveLeft = "ArrowLeft" + this.key.moveRight = "ArrowRight" + this.key.rotateCCW = "w" + this.key.rotateCW = "ArrowUp" + this.key.softDrop = "ArrowDown" + this.key.hardDrop = " " + this.key.hold = "c" + this.key.pause = "Escape" + + this.arrDelay = 50 + this.dasDelay = 300 + + this.musicVolume = 50 + this.sfxVolume = 50 + } +} + + +export { Settings } \ No newline at end of file diff --git a/jsm/stats.js b/jsm/stats.js new file mode 100644 index 0000000..d9dc60b --- /dev/null +++ b/jsm/stats.js @@ -0,0 +1,166 @@ +import { Clock } from 'three' +import { DELAY, T_SPIN, AWARDED_LINE_CLEARS, CLEARED_LINES_NAMES } from './common.js' + + +class Stats { + constructor() { + this.clock = new Clock(false) + this.clock.timeFormat = new Intl.DateTimeFormat("fr-FR", { + hour: "numeric", + minute: "2-digit", + second: "2-digit", + timeZone: "UTC" + }) + this.elapsedTime = 0 + + this.init() + } + + init() { + this._level = 0 + this._score = 0 + this.goal = 0 + this.highScore = Number(localStorage["teTraHighScore"]) || 0 + this.combo = 0 + this.b2b = 0 + this.startTime = new Date() + this.lockDelay = DELAY.LOCK + this.totalClearedLines = 0 + this.nbTetra = 0 + this.nbTSpin = 0 + this.maxCombo = 0 + this.maxB2B = 0 + } + + set score(score) { + this._score = score + if (score > this.highScore) { + this.highScore = score + } + } + + get score() { + return this._score + } + + 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) + messagesSpan.addNewChild("div", { className: "show-level-animation", innerHTML: `<h1>NIVEAU<br/>${this.level}</h1>` }) + } + + get level() { + return this._level + } + + 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 + } + + get time() { + return this.clock.timeFormat.format(this.clock.elapsedTime * 1000) + } + + lockDown(nbClearedLines, tSpin) { + this.totalClearedLines += nbClearedLines + if (nbClearedLines == 4) this.nbTetra++ + if (tSpin == T_SPIN.T_SPIN) this.nbTSpin++ + + // 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<br/>${comboScore}` + }) + } else { + messagesSpan.addNewChild("div", { + className: "zoom-in-animation", + style: "animation-delay: .4s", + innerHTML: `COMBO x${this.combo}<br/>${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 À BOUT<br/>${b2bScore}` + }) + } else { + messagesSpan.addNewChild("div", { + className: "zoom-in-animation", + style: "animation-delay: .4s", + innerHTML: `BOUT À BOUT x${this.b2b}<br/>${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 À BOUT` + }) + } + this.b2b = -1 + } + + this.goal -= awardedLineClears + if (this.goal <= 0) this.level++ + } +} + + +export { Stats } \ No newline at end of file diff --git a/jsm/utils.js b/jsm/utils.js new file mode 100644 index 0000000..8486faa --- /dev/null +++ b/jsm/utils.js @@ -0,0 +1,29 @@ +class Scheduler { + constructor() { + this.intervalTasks = new Map() + this.timeoutTasks = new Map() + } + + setInterval(func, delay, ...args) { + this.intervalTasks.set(func, window.setInterval(func, delay, ...args)) + } + + setTimeout(func, delay, ...args) { + this.timeoutTasks.set(func, window.setTimeout(func, delay, ...args)) + } + + clearInterval(func) { + if (this.intervalTasks.has(func)) + window.clearInterval(this.intervalTasks.get(func)) + this.intervalTasks.delete(func) + } + + clearTimeout(func) { + if (this.timeoutTasks.has(func)) + window.clearTimeout(this.timeoutTasks.get(func)) + this.timeoutTasks.delete(func) + } +} + + +export { Scheduler } \ No newline at end of file