Skip to content

Commit 4c54b82

Browse files
author
Giesenberg, Jan
committed
feat(#20): can now start multiple timers [WIP]
1 parent fb606a1 commit 4c54b82

23 files changed

+992
-113
lines changed

src/js/MindFlayer.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ export default class MindFlayer {
4141
}
4242

4343
init() {
44-
if (this.#settings.enabled && dependencies.warnIfAnyMissing()) {
44+
if (dependencies.warnIfAnyMissing()) {
4545
console.debug(LOG_PREFIX + "Initializing modules");
4646
this._vttBugFixes();
4747
loader.init(this);
4848
}
4949
}
5050

5151
ready() {
52-
if (this.#settings.enabled && dependencies.warnIfAnyMissing(false)) {
52+
if (dependencies.warnIfAnyMissing(false)) {
5353
console.debug(LOG_PREFIX + "Calling ready methods on modules");
5454
loader.ready(this);
5555
}

src/js/modules/AbstractSubModule.js

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ export default class AbstractSubModule {
2929
return [];
3030
}
3131

32+
/**
33+
* Ask to start the module when the module Setting is enabled
34+
*
35+
* @param {Mindflayer} instance the instance to start
36+
* @returns {boolean} if true the module should be loaded
37+
*/
38+
static shouldStart(instance) {
39+
return instance.settings.enabled;
40+
}
41+
3242
constructor(instance) {
3343
this.#instance = instance;
3444
this.#loaded = true;

src/js/modules/ControllerManager/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ export default class ControllerManager extends AbstractSubModule {
143143
const keypad = this.#keypads[name];
144144
const leds = keypad.getLEDsIfChanged();
145145
if (leds) {
146+
console.debug(
147+
`${SUB_LOG_PREFIX}Sending updated LEDs to keypad '${keypad.controllerId}'`
148+
);
146149
const data = JSON.stringify({
147150
type: "configuration",
148151
"controller-id": keypad.controllerId,

src/js/modules/ambilight/index.js

+24-38
Original file line numberDiff line numberDiff line change
@@ -13,57 +13,60 @@
1313
* see <https://www.gnu.org/licenses/>.
1414
*/
1515
"use strict";
16+
import { TABLE_LED_PRIORITY } from "../../settings/constants";
1617
import { Rectangle, Vector } from "../../utils/2d-geometry";
1718
import { hexToRgb } from "../../utils/color";
1819
import AbstractSubModule from "../AbstractSubModule";
19-
import { default as Socket } from "../socket";
20+
import TableLEDRing from "../tableLedRing";
21+
import { TableLEDRingHandlerMixin } from "../tableLedRing/TableLEDRingHandlerMixin";
2022

21-
export default class Ambilight extends AbstractSubModule {
23+
export default class Ambilight extends TableLEDRingHandlerMixin(
24+
AbstractSubModule
25+
) {
2226
#enabled = true;
2327
#updateLEDsTimer = null;
24-
#ambilightLastSent = "null";
2528

2629
ready() {
2730
this.#enabled = game.canvas.initialized;
28-
// setup the animation loop for reading the canvas for ambilight leds
29-
this.#updateLEDsTimer = window.setInterval(
30-
this._ambilightLoop.bind(this),
31-
Math.round(1000 / this.instance.settings.ambilight.fps)
32-
);
31+
this.tableLEDRing.registerHandler(this);
3332
}
3433

3534
unhook() {
3635
this.#enabled = false;
36+
this.tableLEDRing.unregisterHandler(this);
3737
window.clearInterval(this.#updateLEDsTimer);
3838
super.unhook();
3939
}
4040

4141
static get moduleDependencies() {
42-
return [...super.moduleDependencies, Socket.name];
42+
return [...super.moduleDependencies, TableLEDRing.name];
4343
}
4444

45-
/**
46-
* @returns {Socket}
47-
*/
48-
get socket() {
49-
return this.instance.modules[Socket.name];
45+
/** @returns {TableLEDRing} */
46+
get tableLEDRing() {
47+
return this.instance.modules[TableLEDRing.name];
5048
}
5149

5250
set enabled(value) {
5351
this.#enabled = game.canvas.initialized && value;
5452
}
5553

56-
/**
57-
* @protected
58-
*/
59-
async _ambilightLoop() {
54+
get priority() {
55+
if (this.#enabled) {
56+
return TABLE_LED_PRIORITY.AMBILIGHT;
57+
} else {
58+
return TABLE_LED_PRIORITY.OFF;
59+
}
60+
}
61+
62+
async updateLEDs(count) {
6063
this.ensureLoaded();
6164
if (!this.#enabled || !this.instance.settings.ambilight.enabled) {
6265
return;
6366
}
6467
const pixelsRaw = await this.loadPixels();
6568
if (pixelsRaw !== null) {
66-
this.sendAmbilightData(this._compileLEDData(pixelsRaw));
69+
return this._compileLEDData(pixelsRaw, count);
6770
}
6871
}
6972

@@ -74,7 +77,7 @@ export default class Ambilight extends AbstractSubModule {
7477
}
7578

7679
/**
77-
* Will only function correctly if call from within requestAnimationFrame()
80+
* Will only function correctly if called from within requestAnimationFrame()
7881
* @protected
7982
*/
8083
_loadPixels(resolve, reject) {
@@ -107,8 +110,7 @@ export default class Ambilight extends AbstractSubModule {
107110
/**
108111
* @protected
109112
*/
110-
_compileLEDData(pixelsRaw) {
111-
const ledCount = this.instance.settings.ambilight.led.count;
113+
_compileLEDData(pixelsRaw, ledCount) {
112114
const brightMin = this.instance.settings.ambilight.brightness.min;
113115
const brightRange =
114116
(this.instance.settings.ambilight.brightness.max - brightMin) / 255;
@@ -178,20 +180,4 @@ export default class Ambilight extends AbstractSubModule {
178180
}
179181
return Math.floor(bounds.center.x + bounds.center.y * bounds.p1.x) * 4;
180182
}
181-
182-
sendAmbilightData(ledState) {
183-
if (ledState === null) {
184-
return;
185-
}
186-
const data = JSON.stringify({
187-
type: "ambilight",
188-
target: this.instance.settings.ambilight.target,
189-
universe: this.instance.settings.ambilight.universe,
190-
colors: Array.from(ledState),
191-
});
192-
if (this.#ambilightLastSent !== data) {
193-
this.socket.send(data);
194-
this.#ambilightLastSent = data;
195-
}
196-
}
197183
}

src/js/modules/combatIndicator/index.js

+15-63
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import AbstractSubModule from "../AbstractSubModule";
33
import Ambilight from "../ambilight";
44
import ControllerManager from "../ControllerManager";
55
import Keypad from "../ControllerManager/Keypad";
6+
import Timer from "../timer";
7+
import TimerRunner from "../timer/TimerRunner";
68

79
const COMBAT_DURATION_TACTICAL_DISCUSSION = 60000; //ms
810
const COMBAT_DURATION_FORCED_DODGE = 7000; //ms
@@ -28,11 +30,7 @@ export default class CombatIndicator extends AbstractSubModule {
2830
}
2931

3032
static get moduleDependencies() {
31-
return [
32-
...super.moduleDependencies,
33-
ControllerManager.name,
34-
AmbientLight.name,
35-
];
33+
return [...super.moduleDependencies, ControllerManager.name, Timer.name];
3634
}
3735

3836
/**
@@ -43,10 +41,10 @@ export default class CombatIndicator extends AbstractSubModule {
4341
}
4442

4543
/**
46-
* @returns {Ambilight}
44+
* @returns {Timer}
4745
*/
48-
get ambilight() {
49-
return this.instance.modules[Ambilight.name];
46+
get timer() {
47+
return this.instance.modules[Timer.name];
5048
}
5149

5250
/**
@@ -119,61 +117,15 @@ export default class CombatIndicator extends AbstractSubModule {
119117
}
120118
}
121119
}
122-
async #startTacticalTimer(updateCombatCallback, timeInMS) {
123-
const totalLEDs = this.instance.settings.ambilight.led.count;
124-
if (this.#updateLEDsTimer !== null) {
125-
window.clearInterval(this.#updateLEDsTimer);
126-
}
127-
if (totalLEDs <= 0) {
128-
window.setTimeout(updateCombatCallback, timeInMS);
129-
} else {
130-
this.ambilight.enabled = false;
131-
this.#updateLEDsTimer = window.setInterval(
132-
this.#updateLEDs.bind(this, updateCombatCallback),
133-
Math.floor(timeInMS / totalLEDs)
134-
);
135-
this.#currentLED = 0;
136-
this.#updateLEDs(updateCombatCallback);
137-
}
138-
}
139120

140-
async #finishTacticalTimer(callback) {
141-
this.ambilight.enabled = true;
142-
window.clearInterval(this.#updateLEDsTimer);
143-
this.#updateLEDsTimer = null;
144-
callback();
145-
}
146-
147-
#updateLEDs(callback) {
148-
const total = this.instance.settings.ambilight.led.count;
149-
const totalValues = total * 3;
150-
const offset = this.instance.settings.ambilight.led.offset;
151-
const minBright = this.instance.settings.ambilight.brightness.min;
152-
const remaining = total - this.#currentLED;
153-
const completion = remaining / total;
154-
const leds = [];
155-
let color;
156-
this.#currentLED++;
157-
if (completion > 2 / 3) {
158-
color = hexToRgb("#00FF00");
159-
} else if (completion > 1 / 3) {
160-
color = hexToRgb("#FFFF00");
161-
} else if (remaining <= 0) {
162-
return this.#finishTacticalTimer(callback);
163-
} else {
164-
color = hexToRgb("#FF0000");
165-
}
166-
for (let i = 0; i < total; i++) {
167-
if (i < remaining) {
168-
leds[(offset + i * 3 + 0) % totalValues] = color.r;
169-
leds[(offset + i * 3 + 1) % totalValues] = color.g;
170-
leds[(offset + i * 3 + 2) % totalValues] = color.b;
171-
} else {
172-
leds[(offset + i * 3 + 0) % totalValues] = minBright;
173-
leds[(offset + i * 3 + 1) % totalValues] = minBright;
174-
leds[(offset + i * 3 + 2) % totalValues] = minBright;
175-
}
176-
}
177-
this.ambilight.sendAmbilightData(leds);
121+
async #startTacticalTimer(updateCombatCallback, timeInMS) {
122+
const start = new Date().valueOf();
123+
this.timer.addTimer({
124+
start: start,
125+
end: start + timeInMS,
126+
options: {
127+
onDone: updateCombatCallback,
128+
},
129+
});
178130
}
179131
}

src/js/modules/doorHandler/index.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default class DoorHandler extends AbstractSubModule {
3333
}
3434

3535
ready() {
36-
if (!game.canvas.initialized) {
36+
if (this.instance.settings.core.noCanvas) {
3737
console.info(SUB_LOG_PREFIX + "canvas is disabled, cannot control doors");
3838
return;
3939
}
@@ -42,7 +42,9 @@ export default class DoorHandler extends AbstractSubModule {
4242
}
4343

4444
unhook() {
45-
this.controllerManager.unregisterTickListener(this.#tickHandlerFun);
45+
if (!this.instance.settings.core.noCanvas) {
46+
this.controllerManager.unregisterTickListener(this.#tickHandlerFun);
47+
}
4648
super.unhook();
4749
}
4850

src/js/modules/loader.js

+37-6
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,42 @@ let dependencyGraph = null;
3636
*/
3737
export function init(instance) {
3838
console.debug(LOG_PREFIX + "Sorting submodules");
39+
3940
subModules.sort((a, b) => {
41+
/*
42+
* This functions sorts modules by their dependencies.
43+
* starting with the modules with the most dependencies,
44+
* ending with the ones who don't have any
45+
*/
4046
const bDeps = b.default.moduleDependencies;
4147
if (bDeps.includes(a.default.name)) {
42-
return -1;
48+
return 1;
4349
}
4450
const aDeps = a.default.moduleDependencies;
4551
if (aDeps.includes(b.default.name)) {
46-
return 1;
52+
return -1;
4753
}
4854
return aDeps.length - bDeps.length;
4955
});
56+
57+
console.debug(LOG_PREFIX + "Filtering unnecessary modules")
58+
59+
let filteredModules = [];
60+
let requiredModuleNames = new Set();
61+
for(var key in subModules) {
62+
const module = subModules[key];
63+
const moduleName = module.default.name;
64+
if(module.default.shouldStart(instance) || requiredModuleNames.has(moduleName)) {
65+
filteredModules.push(module);
66+
requiredModuleNames.delete(moduleName);
67+
module.default.moduleDependencies.forEach(name => requiredModuleNames.add(name));
68+
}
69+
}
70+
subModules = filteredModules.reverse();
71+
5072
console.info(LOG_PREFIX + "Starting submodules");
5173

52-
__loadModules(instance, subModules);
74+
__loadModules(instance, filteredModules);
5375

5476
console.info(LOG_PREFIX + "Submodules initialized");
5577
}
@@ -74,11 +96,19 @@ export function ready(instance, modules = null) {
7496
}
7597
return aDeps.length - bDeps.length;
7698
})
77-
.map((mod) => instance.modules[mod.default.name]);
99+
.map((mod) => instance.modules[mod.default.name])
100+
.filter((mod) => mod !== undefined && mod !== null);
78101
}
79102
modules.forEach((mod) => {
80-
console.debug(`${LOG_PREFIX}Readying Module: ${mod.constructor.name}`);
81-
mod.ready();
103+
try {
104+
console.debug(`${LOG_PREFIX}Readying Module: ${mod.constructor.name}`);
105+
mod.ready();
106+
} catch (e) {
107+
console.warn(
108+
`${LOG_PREFIX}Failed to ready module '${mod.constructor.name}', continuing...`,
109+
e
110+
);
111+
}
82112
});
83113
}
84114

@@ -134,6 +164,7 @@ function _reload(instance, module) {
134164
subModules.find((mod) => mod.default.name === name)
135165
);
136166
__loadModules(instance, modules);
167+
ready(instance, modules);
137168
}
138169

139170
export const reload = foundry.utils.debounce(_reload, 500);

src/js/modules/socket/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export default class Socket extends AbstractSubModule {
3939
}
4040

4141
ready() {
42+
// The module may be asked to load as dependency
43+
// WebSocket to connect to elder brain will only start if enabled
44+
if(!Socket.shouldStart(this.instance)) return;
4245
this._initializeWebsocket();
4346
}
4447

0 commit comments

Comments
 (0)