diff --git a/audio/input.js b/audio/input.js index 01df8384..5abdc9bc 100644 --- a/audio/input.js +++ b/audio/input.js @@ -18,7 +18,7 @@ class AudioInput extends EventEmitter { constructor(options) { super(); // this._sampleRate = options._sampleRate || 48000; - const deviceIndex = options.deviceIndex || null; + const deviceIndex = options.deviceIndex ?? null; const profile = options.profile || null; this._args = [ '-u' ]; if (!profile) { @@ -27,7 +27,7 @@ class AudioInput extends EventEmitter { const profilePath = options.profilePath || 'audioprofile.pstats'; this._args.push('-m', 'cProfile', '-o', profilePath, mainScript); } - if (deviceIndex) { + if (deviceIndex !== undefined) { this._args.push('-d', deviceIndex.toString()); } this._subprocess = null; diff --git a/server/setups/shape-mappings/fuego2022.js b/server/setups/shape-mappings/fuego2022.js index 4b593824..57166c84 100644 --- a/server/setups/shape-mappings/fuego2022.js +++ b/server/setups/shape-mappings/fuego2022.js @@ -73,6 +73,7 @@ module.exports = function getShapes() { inv(_.range(T2, T2+SIDE_TRIANGULO)) ]); + // Totems const totemL1 = _.range(T4_end, T4_end+150); const totemL2 = _.range(T4_end+150, T4_end+150*2); @@ -152,12 +153,24 @@ module.exports = function getShapes() { const wingsLeft = _.flatten([totemL1, totemL2, V1L, V2L]); const wingsX = _.flatten([totemL1, totemL2, X, totemR1, totemR2]); + const arc = _.flatten([ + inv(_.range(quiebre_abajo, T2)), + _.range(T2, T2+SIDE_TRIANGULO), + inv(_.range(T3, T3+SIDE_TRIANGULO)), + _.range(T4 + quiebre_abajo, T4_end) + ]); + + const right_mini_w = _.flatten([ + + + ]); return { Warro, WarroOnly, allOfIt, all, mini_w, + arc, pataLeft, pataRight, reloj, diff --git a/server/src/LightController.js b/server/src/LightController.js index d7f728f2..9488e2cf 100644 --- a/server/src/LightController.js +++ b/server/src/LightController.js @@ -17,6 +17,7 @@ const savedPresets = require(savedPresetsFilePath) // TODO: move this to some configuration file const programNames = [ "mix", + "congaCelebration", "congaShooting2", "congaScore", "congaShooting", diff --git a/server/src/index.js b/server/src/index.js index 79f91c60..7767c54a 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -18,23 +18,23 @@ require('./hackProgramReloadOnRestart')(controller); controller.start(); console.log('Available audio devices:\n', listDevices()); -// -const audioInput = new AudioInput({deviceIndex: null}); + +const audioInput = new AudioInput({deviceIndex: 0,}); audioInput.on('audioframe', audioEmitter.updateFrame.bind(audioEmitter)); -// const audioInput2 = new AudioInput({deviceIndex: 3,}); -// audioInput2.on('audioframe', (frame) => { -// audioEmitter.frame2 = frame; -// }); -// audioInput2.start(); -// -// // Second audio input test -// audioInput.on('audioframe', (frame) => { -// audioEmitter.currentFrame = frame; -// audioEmitter.ready = true; -// audioEmitter.emit('audioframe', audioEmitter.currentFrame); -// }); +const audioInput2 = new AudioInput({deviceIndex: 2,}); +audioInput2.on('audioframe', (frame) => { + audioEmitter.frame2 = frame; +}); +audioInput2.start(); + +// Second audio input test +audioInput.on('audioframe', (frame) => { + audioEmitter.currentFrame = {... frame, ... _.mapKeys(audioEmitter.frame2, (v,k) => 'mic2_'+k)}; + audioEmitter.ready = true; + audioEmitter.emit('audioframe', audioEmitter.currentFrame); +}); audioInput.start(); -startServer(controller); +startServer(controller); \ No newline at end of file diff --git a/server/src/light-programs/conga-utils/GlobalGame.js b/server/src/light-programs/conga-utils/GlobalGame.js index 208f8b63..675c91b8 100644 --- a/server/src/light-programs/conga-utils/GlobalGame.js +++ b/server/src/light-programs/conga-utils/GlobalGame.js @@ -1,6 +1,10 @@ +const ColorUtils = require("./../utils/ColorUtils"); + class Game { constructor() { this.score = [0,0]; + this.player1Color = ColorUtils.HSVtoRGB(400, 1, 1); + this.player2Color = ColorUtils.HSVtoRGB(400+0.33, 1, 1); } addPoint(playerIndex, points = 1) { @@ -14,8 +18,22 @@ class Game { restart() { this.score = [0,0]; } + + winner(){ + let winner = false; + if (this.score[0] == this.max()){ + winner = 1; + } + else if(this.score[1] == this.max()){ + winner = 2; + } + if(winner){ + this.restart(); + } + return winner; + } } module.exports = { game: new Game() -} +} \ No newline at end of file diff --git a/server/src/light-programs/programs/congaCelebration.js b/server/src/light-programs/programs/congaCelebration.js new file mode 100644 index 00000000..8087c868 --- /dev/null +++ b/server/src/light-programs/programs/congaCelebration.js @@ -0,0 +1,135 @@ +const LightProgram = require("./../base-programs/LightProgram"); +const ColorUtils = require("./../utils/ColorUtils"); +const GlobalGame = require("./../conga-utils/GlobalGame"); +const _ = require("lodash"); +const programsByShape = require("./../base-programs/ProgramsByShape"); + +module.exports = class CongaCelebratiton extends LightProgram { + init() { + this.past = null; + this.celebrate = 0; + this.winner = false; + this.colors = new Array(this.numberOfLeds).fill(ColorUtils.HSVtoRGB(0, 0, this.config.brillo)); + // Shallow copy of schedule + this.programs = _.map(this.config.programs, config => this.getProgramInstanceFromParam(config)); + } + + getProgramInstanceFromParam({programName, config, shape}) { + let p = null; + // For performance, only use programsByShape if there is a shape + if(shape) { + const programClass = this.lightController.programs[programName].generator; + const byShapeClass = programsByShape({[shape]: [programClass, config || {}]}); + p = new byShapeClass(this.config, this.geometry, this.shapeMapping, this.lightController); + } else { + p = this.lightController.instanciateProgram(programName); + p.updateConfig({...p.config, ...config}) + } + + p.init(); + return p; + } + + getCelebrationBaseColors() { + + + + } + + drawFrame(draw, audio) { + this.colors = new Array(this.numberOfLeds).fill(ColorUtils.HSVtoRGB(0, 0, this.config.brillo/100)); + let winner = GlobalGame.game.winner(); + let combinedColors = this.colors; + if (winner && (this.celebrate == 0 || winner != this.winner)){ + this.winner = winner; + this.celebrate = this.config.celebrationDurationInFrames; + } + if (this.celebrate > 0){ + let winnerColor = this.winner == 1 ? GlobalGame.game.player1Color : GlobalGame.game.player2Color; + this.colors = new Array(this.numberOfLeds).fill(winnerColor); + this.celebrate--; + } + if(!this.config.animateOnlyOnCelebration || this.celebrate > 0){ + combinedColors = this.colors; + + this.extraTime = (this.extraTime || 0) + Math.random() * 10; + + for (const prog of this.programs) { + // Done by ProgramScheduler, has to be replicated here + prog.timeInMs = this.timeInMs; + let globalBrightness = prog.config.globalBrightness || 0; + prog.drawFrame((colors) => { + for (let i = 0; i < this.numberOfLeds; i++) { + let [r, g, b, a] = combinedColors[i] + const [r2, g2, b2, a2] = colors[i]; + if (this.config.multiply) { + // globalBrightness of 0 means "the layer does not darken the other layer" + r = r * ((r2+(255-r2)*(1-globalBrightness)) || 0) / 255; + g = g * ((g2+(255-g2)*(1-globalBrightness)) || 0) / 255; + b = b * ((b2+(255-b2)*(1-globalBrightness)) || 0) / 255; + a = a + (a2 || 0) + } else { + r += (r2 || 0) * globalBrightness; + g += (g2 || 0) * globalBrightness; + b += (b2 || 0) * globalBrightness; + a += a2 || 0; + } + combinedColors[i] = [r, g, b, a]; + } + }, audio) + } + } + draw(combinedColors); + } + + updateConfig(newConfig) { + // TODO: backwards compatibility with previous version of mix + if(newConfig.a && newConfig.b) { + let {a, b, ... other} = newConfig; + newConfig = {... other, programs: [a, b]} + } + + // Override LightProgram version to decide when a program init needs to be called + if (this.programs) { + let updated = newConfig.programs; + let oldConfigs = this.config.programs; + + this.programs = _.map(updated, (newProgDef, i) => { + let oldProgDef = oldConfigs[i]; + + let subprogram = null; + + // Detect if the selected program type is the same or it changed + if (oldProgDef && oldProgDef.programName === newProgDef.programName && oldProgDef.shape === newProgDef.shape) { + subprogram = this.programs[i] + subprogram.updateConfig({ ... subprogram.config, ... newProgDef.config }) + } else { + subprogram = this.getProgramInstanceFromParam(newProgDef) + } + + // Detect if a different preset was selected and apply the default+preset program config + if(oldProgDef && oldProgDef.presetName !== newProgDef.presetName && newProgDef.presetName) { + const presets = this.lightController.getProgramPresets(newProgDef.programName); + const defaults = this.lightController.getProgramDefaultParams(newProgDef.programName); + newProgDef.config = presets[newProgDef.presetName]; + subprogram.updateConfig({ ... defaults, ... presets[newProgDef.presetName] }) + } + + return subprogram + }); + } + + super.updateConfig(newConfig) + } + + // Override and extend config Schema + static configSchema() { + let res = super.configSchema(); + res.brillo = { type: Number, min: 0, max: 1, step: 0.01, default: 0.5 }; + res.celebrationDurationInFrames = { type: Number, min: 0, max: 200, step: 5, default: 500 }; + res.animateOnlyOnCelebration = {type: Boolean, default: false}; + res.programs = {type: 'programs', default: [{programName: 'circles'}]}; + res.multiply = {type: Boolean, default: false}; + return res; + } +}; \ No newline at end of file diff --git a/server/src/light-programs/programs/congaRope.js b/server/src/light-programs/programs/congaRope.js index 142e038b..6cae5e5e 100644 --- a/server/src/light-programs/programs/congaRope.js +++ b/server/src/light-programs/programs/congaRope.js @@ -1,132 +1,132 @@ const LightProgram = require("./../base-programs/LightProgram"); const ColorUtils = require("./../utils/ColorUtils"); const _ = require("lodash"); +const GlobalGame = require("./../conga-utils/GlobalGame"); module.exports = class CongaShooting extends LightProgram { - init() { - this.center = this.numberOfLeds / 2; - this.segmentSize = 60; - this.colors = new Array(this.numberOfLeds); - this.rope = {pos: this.center - this.segmentSize, length: 2 * this.segmentSize}; - this.explosionLevel = 0; - this.time = 0; - this.audioWindow1 = new Array(1000).fill(0); - this.audioWindow2 = new Array(1000).fill(0); - this.maxVolume = 0; - } - - updateAudioWindow(audio){ - let volP1 = (audio[this.config.soundMetricP1] || 0) * this.config.multiplier; - let volP2 = (audio[this.config.soundMetricP2] || 0) * this.config.multiplier; - - this.audioWindow1.unshift(); - this.audioWindow1.push(volP1 > this.config.fireThreshold ? 1 : 0); - - this.audioWindow2.unshift(); - this.audioWindow2.push(volP2 > this.config.fireThreshold ? 1 : 0); - } + init() { + this.center = this.numberOfLeds / 2; + this.segmentSize = 60; + this.colors = new Array(this.numberOfLeds); + this.rope = {pos: this.center - this.segmentSize, length: 2 * this.segmentSize}; + this.explosionLevel = 0; + this.time = 0; + this.audioWindow1 = new Array(600).fill(0); + this.audioWindow2 = new Array(600).fill(0); + this.maxVolume = 0; + } - detectBursts(audio){ - this.updateAudioWindow(audio); - let burstSizeP1 = _.sum(this.audioWindow1); - let burstSizeP2 = _.sum(this.audioWindow2); - console.log(burstSizeP1, burstSizeP2); + updateAudioWindow(audio){ + let volP1 = (audio[this.config.soundMetricP1] || 0) * this.config.multiplier; + let volP2 = (audio[this.config.soundMetricP2] || 0) * this.config.multiplier; - if(burstSizeP1 < this.config.burstThreshold){ - burstSizeP1 = 0; - } - if (burstSizeP1 > this.config.burstMaxLength){ - burstSizeP1 = this.config.burstMaxLength; - } + this.audioWindow1.unshift(); + this.audioWindow1.push(volP1 > this.config.fireThreshold ? volP1 : 0); - if(burstSizeP2 < this.config.burstThreshold){ - burstSizeP2 = 0; - } - if (burstSizeP2 > this.config.burstMaxLength){ - burstSizeP2 = this.config.burstMaxLength; - } - return [burstSizeP1, burstSizeP2]; - } + this.audioWindow2.unshift(); + this.audioWindow2.push(volP2 > this.config.fireThreshold ? volP2 : 0); + } - pushToPlayerSide(player){ - if(player == 'P1'){ - this.rope.pos -= this.config.baseForce; - if (this.rope.pos < 0){ - this.rope.pos = 0; - } - this.resetBurstsForPlayer(player); - } - if(player == 'P2'){ - this.rope.pos += this.config.baseForce; - if(this.rope.pos >= this.numberOfLeds){ - this.rope.pos = this.numberOfLeds; - } - this.resetBurstsForPlayer(player); - } - } - paintAll(){ - let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20); - for (let i = 0; i < this.numberOfLeds; i++) { - this.colors[i] = [255,255,255]; - } - } + detectBursts(audio){ + this.updateAudioWindow(audio); + let burstSizeP1 = _.sum(this.audioWindow1); + let burstSizeP2 = _.sum(this.audioWindow2); - resetBurstsForPlayer(player){ - if (player == 'P1'){ - this.audioWindow1.fill(0); - } - if (player == 'P2'){ - this.audioWindow2.fill(0); - } - } - gameOver(winner){ - this.rope = {pos: this.center - this.segmentSize, length: 2 * this.segmentSize}; - this.paintAll(); - } + if(burstSizeP1 < this.config.burstThreshold){ + burstSizeP1 = 0; + } + if (burstSizeP1 > this.config.burstMaxLength){ + burstSizeP1 = this.config.burstMaxLength; + } - drawFrame(draw, audio) { - audio = audio.currentFrame || {}; - let bursts = this.detectBursts(audio); - let burstP1 = bursts[0]; - let burstP2 = bursts[1]; + if(burstSizeP2 < this.config.burstThreshold){ + burstSizeP2 = 0; + } + if (burstSizeP2 > this.config.burstMaxLength){ + burstSizeP2 = this.config.burstMaxLength; + } + return [burstSizeP1, burstSizeP2]; + } - if (burstP1 > burstP2){ - this.pushToPlayerSide('P1'); - }else if (burstP2 > burstP1){ - this.pushToPlayerSide('P2'); + pushToPlayerSide(player){ + if(player == 'P1'){ + this.rope.pos -= this.config.baseForce; + if (this.rope.pos < 0){ + this.rope.pos = 0; + } + this.resetBurstsForPlayer(player); + } + if(player == 'P2'){ + this.rope.pos += this.config.baseForce; + if(this.rope.pos >= this.numberOfLeds){ + this.rope.pos = this.numberOfLeds; + } + this.resetBurstsForPlayer(player); + } + } + paintAll(){ + let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20); + for (let i = 0; i < this.numberOfLeds; i++) { + this.colors[i] = [255,255,255]; + } } - if (this.rope.pos == 0){ - this.gameOver('P1'); + resetBurstsForPlayer(player){ + if (player == 'P1'){ + this.audioWindow1.fill(0); + } + if (player == 'P2'){ + this.audioWindow2.fill(0); + } } - if(this.rope.pos + this.rope.length == this.numberOfLeds){ - this.gameOver('P2'); + gameOver(winner){ + this.rope = {pos: this.center - this.segmentSize, length: 2 * this.segmentSize}; + GlobalGame.game.score[winner] = GlobalGame.game.max(); } - let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20); - for (let i = 0; i < this.numberOfLeds; i++) { - this.colors[i] = baseColor; - if (i >= this.rope.pos && i < this.rope.pos+this.rope.length){ - this.colors[i] = [255,255,0]; + + drawFrame(draw, audio) { + audio = audio.currentFrame || {}; + let bursts = this.detectBursts(audio); + let burstP1 = bursts[0]; + let burstP2 = bursts[1]; + + if (burstP1 > burstP2){ + this.pushToPlayerSide('P1'); + }else if (burstP2 > burstP1){ + this.pushToPlayerSide('P2'); } + + if (this.rope.pos == 0){ + this.gameOver(0); + } + if(this.rope.pos + this.rope.length == this.numberOfLeds){ + this.gameOver(1); + } + let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20); + for (let i = 0; i < this.numberOfLeds; i++) { + this.colors[i] = baseColor; + if (i >= this.rope.pos && i < this.rope.pos+this.rope.length){ + this.colors[i] = [255,255,0]; + } + } + draw(this.colors); } - draw(this.colors); - } - static presets() { - return {}; - } + static presets() { + return {}; + } - // Override and extend config Schema - static configSchema() { - let res = super.configSchema(); - res.multiplier = { type: Number, min: 0, max: 2, step: 0.01, default: 1 }; - res.soundMetricP1 = {type: 'soundMetric', default: "rms"}; - res.soundMetricP2 = {type: 'soundMetric', default: "mic2_rms"}; - res.fireThreshold = {type: Number, min: 0, max: 1, step: 0.01, default: 0.45}; - res.baseForce = { type: Number, min: 1, max: 10, step: 1, default: 3 }; - res.burstThreshold = {type: Number, min: 0, max: 20, step: 1, default: 3}; - res.burstMaxLength = {type: Number, min: 0, max: 50, step: 1, default: 10}; - return res; - } -}; + // Override and extend config Schema + static configSchema() { + let res = super.configSchema(); + res.multiplier = { type: Number, min: 0, max: 2, step: 0.01, default: 1 }; + res.soundMetricP1 = {type: 'soundMetric', default: "rms"}; + res.soundMetricP2 = {type: 'soundMetric', default: "mic2_rms"}; + res.fireThreshold = {type: Number, min: 0, max: 1, step: 0.01, default: 0.45}; + res.baseForce = { type: Number, min: 1, max: 10, step: 1, default: 3 }; + res.burstThreshold = {type: Number, min: 0, max: 20, step: 1, default: 3}; + res.burstMaxLength = {type: Number, min: 0, max: 50, step: 1, default: 10}; + return res; + } +}; \ No newline at end of file diff --git a/server/src/light-programs/programs/congaShooting2.js b/server/src/light-programs/programs/congaShooting2.js index 03d562c2..dcbb6b90 100644 --- a/server/src/light-programs/programs/congaShooting2.js +++ b/server/src/light-programs/programs/congaShooting2.js @@ -6,122 +6,114 @@ const {Glob} = require("glob"); module.exports = class CongaShooting extends LightProgram { - init() { - this.colors = new Array(this.numberOfLeds); - this.bulletsA = []; - this.bulletsB = []; - this.explosionLevel = 0; - this.time = 0; - this.frame = 0; - this.maxVolume = 0; - GlobalGame.game.restart(); - } - - fire(vol, player) { - const isA = player === 'A'; - let bullets = isA ? this.bulletsA : this.bulletsB; - - if(bullets.length && bullets.slice(-1)[0].frame >= (this.frame - 1)) { - bullets.slice(-1)[0].size += vol; - bullets.slice(-1)[0].frame = this.frame; - } else { - bullets.push({size: vol, pos: isA ? 0 : this.numberOfLeds, speed: isA ? 1 : -1, frame: this.frame}); + init() { + this.colors = new Array(this.numberOfLeds); + this.bulletsA = []; + this.bulletsB = []; + this.explosionLevel = 0; + this.time = 0; + this.frame = 0; + this.maxVolume = 0; + GlobalGame.game.restart(); } - } - renderCollision(){ - this.explosionLevel += 10; - console.log("boom") - } + fire(vol, player) { + const isA = player === 'A'; + let bullets = isA ? this.bulletsA : this.bulletsB; - simulate(){ - for(const b of this.bulletsA){ - b.pos += b.speed * this.config.speed; - - if(b.pos < 0 || b.pos > this.numberOfLeds) { - this.bulletsA = _.without(this.bulletsA, b); - GlobalGame.game.addPoint(0); - - if(GlobalGame.game.score[0] === 10) { - GlobalGame.game.restart() - } - } - } + if(bullets.length && bullets.slice(-1)[0].frame >= (this.frame - 1)) { + bullets.slice(-1)[0].size += vol; + bullets.slice(-1)[0].frame = this.frame; + } else { + bullets.push({size: vol, pos: isA ? 0 : this.numberOfLeds, speed: isA ? 1 : -1, frame: this.frame}); + } + } - for(const b of this.bulletsB){ - b.pos += b.speed * this.config.speed; + renderCollision(){ + this.explosionLevel += 10; + console.log("boom") + } - if(b.pos < 0 || b.pos > this.numberOfLeds) { - this.bulletsB = _.without(this.bulletsB, b); - GlobalGame.game.addPoint(1); + simulate(){ + for(const b of this.bulletsA){ + b.pos += b.speed * this.config.speed; - if(GlobalGame.game.score[1] === 10) { - GlobalGame.game.restart() + if(b.pos < 0 || b.pos > this.numberOfLeds) { + this.bulletsA = _.without(this.bulletsA, b); + GlobalGame.game.addPoint(0); } } - } - for(const a of this.bulletsA){ for(const b of this.bulletsB){ - if (Math.abs(a.pos - b.pos) <= this.config.speed){ - this.renderCollision(); - this.bulletsA = _.without(this.bulletsA, a); + b.pos += b.speed * this.config.speed; + + if(b.pos < 0 || b.pos > this.numberOfLeds) { this.bulletsB = _.without(this.bulletsB, b); + GlobalGame.game.addPoint(1); } } - } - this.explosionLevel = this.explosionLevel * 0.7; - if (this.explosionLevel < 0.5){ - this.explosionLevel = 0; - } - } - - drawFrame(draw, audio) { - audio = audio.currentFrame || {}; - this.frame ++; - this.time += this.config.speed; - - let volP1 = (audio[this.config.soundMetricP1] || 0) * this.config.multiplier; - let volP2 = (audio[this.config.soundMetricP2] || 0) * this.config.multiplier; - - if (volP1 > this.config.fireThreshold) { - this.fire(volP1**2, 'A'); - } - if (volP2 > this.config.fireThreshold) { - this.fire(volP2**2, 'B'); - } - let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20); - this.simulate(); - for (let i = 0; i < this.numberOfLeds; i++) { - this.colors[i] = baseColor; - for(const b of this.bulletsA){ - if (Math.abs(b.pos - i) < this.config.speed){ - this.colors[i] = ColorUtils.HSVtoRGB(b.size/400, 1, 1); + for(const a of this.bulletsA){ + for(const b of this.bulletsB){ + if (Math.abs(a.pos - b.pos) <= this.config.speed){ + this.renderCollision(); + this.bulletsA = _.without(this.bulletsA, a); + this.bulletsB = _.without(this.bulletsB, b); + } } } - for(const b of this.bulletsB){ - if (Math.abs(b.pos - i) < this.config.speed){ - this.colors[i] = ColorUtils.HSVtoRGB(b.size/400+0.33, 1, 1); + this.explosionLevel = this.explosionLevel * 0.7; + if (this.explosionLevel < 0.5){ + this.explosionLevel = 0; + } + } + + drawFrame(draw, audio) { + audio = audio.currentFrame || {}; + this.frame ++; + this.time += this.config.speed; + + let volP1 = (audio[this.config.soundMetricP1] || 0) * this.config.multiplier; + let volP2 = (audio[this.config.soundMetricP2] || 0) * this.config.multiplier; + + if (volP1 > this.config.fireThreshold) { + this.fire(volP1**2, 'A'); + } + if (volP2 > this.config.fireThreshold) { + this.fire(volP2**2, 'B'); + } + + let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20); + this.simulate(); + for (let i = 0; i < this.numberOfLeds; i++) { + this.colors[i] = baseColor; + for(const b of this.bulletsA){ + if (Math.abs(b.pos - i) < this.config.speed){ + this.colors[i] = GlobalGame.game.player1Color; + } + } + for(const b of this.bulletsB){ + if (Math.abs(b.pos - i) < this.config.speed){ + this.colors[i] = GlobalGame.game.player2Color; + } } } + + draw(this.colors); } - draw(this.colors); - } - - static presets() { - return {}; - } - - // Override and extend config Schema - static configSchema() { - let res = super.configSchema(); - res.multiplier = { type: Number, min: 0, max: 2, step: 0.01, default: 1 }; - res.fireThreshold = {type: Number, min: 0, max: 1, step: 0.01, default: 0.45}; - res.soundMetricP1 = {type: 'soundMetric', default: "rms"}; - res.soundMetricP2 = {type: 'soundMetric', default: "mic2_rms"}; - res.speed = { type: Number, min: 1, max: 10, step: 1, default: 3 }; - return res; - } -}; + static presets() { + return {}; + } + + // Override and extend config Schema + static configSchema() { + let res = super.configSchema(); + res.multiplier = { type: Number, min: 0, max: 2, step: 0.01, default: 1 }; + res.fireThreshold = {type: Number, min: 0, max: 1, step: 0.01, default: 0.45}; + res.soundMetricP1 = {type: 'soundMetric', default: "rms"}; + res.soundMetricP2 = {type: 'soundMetric', default: "mic2_rms"}; + res.speed = { type: Number, min: 1, max: 10, step: 1, default: 3 }; + return res; + } +}; \ No newline at end of file