Skip to content

Commit

Permalink
Light-based check box
Browse files Browse the repository at this point in the history
  • Loading branch information
Eligarf committed Jul 19, 2024
1 parent c4a062e commit 5d82624
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 101 deletions.
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v4.6.0
* dnd5e: Added a check box to the detection modes configuration dialog, selecting which detection modes can be affected by dim light.
* dnd5e: 3.3 compatible

# v4.5.2
* use 'img' instead of 'icon' for V12 active effects

Expand Down
5 changes: 3 additions & 2 deletions languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@
"name": "Detection Modes",
"label": "Configure Detection Modes",
"hint": "Enable which detection modes stealth will apply to",
"title": "Setting Stealthable Detection Modes",
"warning": "Reload is required to apply changes to selected detection modes"
"warning": "Reload is required to apply changes to selected detection modes",
"enable": "Is enabled",
"light": "Is affected by dim vs bright lighting"
},
"source": {
"hint": "Which source to use for the Active Effect stored on the actor",
Expand Down
5 changes: 3 additions & 2 deletions languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@
"name": "Detection Modes",
"label": "Configure Detection Modes",
"hint": "Enable which detection modes stealth will apply to",
"title": "Setting Stealthable Detection Modes",
"warning": "Reload is required to apply changes to selected detection modes"
"warning": "Reload is required to apply changes to selected detection modes",
"enable": "Is enabled",
"light": "Is affected by dim vs bright lighting"
},
"source": {
"hint": "Which source to use for the Active Effect stored on the actor",
Expand Down
5 changes: 3 additions & 2 deletions languages/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@
"name": "Modos de Detectção",
"label": "Configurar Modos de Detecção",
"hint": "Habilite quais modos de detecção furtivos serão aplicados.",
"title": "Configurando Modos de Detecção Furtivos",
"warning": "É necessário atualizar a página para aplicar alterações aos modos de detecção selecionados"
"warning": "É necessário atualizar a página para aplicar alterações aos modos de detecção selecionados",
"enable": "Is enabled",
"light": "Is affected by dim vs bright lighting"
},
"source": {
"hint": "Qual fonte usar para o Efeito Ativo armazenado no ator",
Expand Down
5 changes: 3 additions & 2 deletions languages/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@
"name": "Режимы обнаружения",
"label": "Настройка Режимов Обнаружения",
"hint": "Определяет, для каких Режимов Обнаружения эффект стелс будет скрывать/раскрывать токены",
"title": "Настройка Режимов Обнаружения применяющих Стелс",
"warning": "Для применения изменений к выбранным Режимам Обнаружения требуется перезагрузка."
"warning": "Для применения изменений к выбранным Режимам Обнаружения требуется перезагрузка.",
"enable": "Is enabled",
"light": "Is affected by dim vs bright lighting"
},
"source": {
"hint": "Какой источник использовать для активного эффекта, хранящегося в актере",
Expand Down
4 changes: 2 additions & 2 deletions module.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"ko-fi": "rule671908"
}
],
"version": "4.1",
"version": "4.6",
"compatibility": {
"minimum": "10.291",
"verified": "12"
Expand All @@ -28,7 +28,7 @@
"id": "dnd5e",
"type": "system",
"compatibility": {
"verified": "3.2"
"verified": "3.3"
}
},
{
Expand Down
29 changes: 20 additions & 9 deletions scripts/detectionModesMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ export class DetectionModesApplicationClass extends FormApplication {
}
get #detectionModes() {
return foundry.utils
.deepClone(game.settings.get(Stealthy.MODULE_ID, "allowedDetectionModes"));
.deepClone(game.settings.get(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES));
}

static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
title: "stealthy.detectionModesMenu.title",
title: "stealthy.detectionModesMenu.label",
id: "stealthy-allowed-detection-modes",
template: "modules/stealthy/templates/detectionModes.hbs",
popOut: true,
Expand All @@ -26,22 +26,33 @@ export class DetectionModesApplicationClass extends FormApplication {
getData() {
const context = super.getData();
const entries = Object.entries(this.#detectionModes)
.filter(([k,v]) => k in CONFIG.Canvas.detectionModes && k !== 'undefined')
.filter(([k, v]) => k in CONFIG.Canvas.detectionModes && k !== 'undefined')
.map(([k, v]) => [k, {
label: CONFIG.Canvas.detectionModes[k].label,
enabled: v
}]);
...v,
}])
.sort();
context.detectionModes = Object.fromEntries(entries);
if ('lightBased' in entries[0][1])
context.lightBased = true;
return context;
}

_updateObject(event, formData) {
Stealthy.log('_updateObject', { event, formData });
const original = game.settings.get(Stealthy.MODULE_ID, "allowedDetectionModes");
const different = (JSON.stringify(formData) !== JSON.stringify(original));
if (different) {
const original = game.settings.get(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES);
let modes = {};
for (let [key, value] of Object.entries(formData)) {
const bits = key.split('-');
key = bits[1];
if (!(key in modes)) modes[key] = {};
modes[key][bits[2]] = value;
}
Stealthy.log('new modes', modes);

if (false && JSON.stringify(formData) !== JSON.stringify(original)) {
ui.notifications.warn(game.i18n.localize("stealthy.detectionModesMenu.warning"));
game.settings.set(Stealthy.MODULE_ID, 'allowedDetectionModes', formData);
game.settings.set(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES, modes);
}
}
}
Expand Down
135 changes: 82 additions & 53 deletions scripts/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ function versionAtLeast(version, target) {
return true;
}

function migrate(moduleVersion, oldVersion) {
// ui.notifications.warn(`Updated Stealthy from ${oldVersion} to ${moduleVersion}`);
return moduleVersion;
}

export default class Engine {

constructor() {
Expand Down Expand Up @@ -71,8 +66,8 @@ export default class Engine {
this.setup();
});

Hooks.once('ready', () => {
this.ready();
Hooks.once('ready', async () => {
await this.ready();
});
}

Expand All @@ -90,7 +85,7 @@ export default class Engine {
restricted: true,
});

game.settings.register(Stealthy.MODULE_ID, 'allowedDetectionModes', settings.allowedDetectionModes);
game.settings.register(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES, settings.allowedDetectionModes);
game.settings.register(Stealthy.MODULE_ID, 'friendlyStealth', settings.friendlyStealth);
game.settings.register(Stealthy.MODULE_ID, 'playerHud', settings.playerHud);
game.settings.register(Stealthy.MODULE_ID, 'exposure', settings.exposure);
Expand All @@ -111,6 +106,14 @@ export default class Engine {
game.settings.register(Stealthy.MODULE_ID, 'schema', settings.schema);
game.settings.register(Stealthy.MODULE_ID, 'activeSpot', settings.activeSpot);

// Back-compatibility settings, config must be false
game.settings.register(Stealthy.MODULE_ID, 'allowedDetectionModes', {
scope: 'world',
config: false,
type: Object,
default: {},
});

Stealthy.log(`${moduleVersion}: init`);
}

Expand All @@ -123,7 +126,73 @@ export default class Engine {
}
}

ready() {
buildDetectModePermission(mode, enabled) {
return { enabled };
}

async migrate_to_4_6() {
let modes = foundry.utils.deepClone(game.settings.get(Stealthy.MODULE_ID, "allowedDetectionModes"));
let changed = false;
for (let [k, v] of Object.entries(modes)) {
if (typeof v == "boolean") {
modes[k] = this.buildDetectModePermission(k, v);
changed = true;
}
}
if (changed) {
Stealthy.log('migrated detection mode settings for 4.6', modes);
await game.settings.set(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES, modes);
}
}

async migrate(moduleVersion, oldVersion) {
if (!versionAtLeast(oldVersion, '4.6.0')) {
await this.migrate_to_4_6();
}
return moduleVersion;
}

async ready() {
const module = game.modules.get(Stealthy.MODULE_ID);
const moduleVersion = module.version;
const schemaVersion = game.settings.get(Stealthy.MODULE_ID, 'schema');

if (schemaVersion !== moduleVersion) {
await this.migrate(moduleVersion, schemaVersion);
ui.notifications.info(`Updated Stealthy settings from ${moduleVersion} to ${moduleVersion}`);
await game.settings.set(Stealthy.MODULE_ID, 'schema', moduleVersion);
}

let allowedModes = game.settings.get(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES);
const changed = this.mixInDefaults(allowedModes);
if (changed) {
Stealthy.log('Allowed modes settings update', allowedModes);
await game.settings.set(Stealthy.MODULE_ID, Stealthy.ALLOWED_DETECTION_MODES, allowedModes);
}

for (const [mode, setting] of Object.entries(allowedModes)) {
if (!setting.enabled) continue;
if (mode === 'undefined' || !(mode in CONFIG.Canvas.detectionModes)) continue;

Stealthy.log(`patching ${mode}`);
libWrapper.register(
Stealthy.MODULE_ID,
`CONFIG.Canvas.detectionModes.${mode}._canDetect`,
function (wrapped, visionSource, target) {
if (!(wrapped(visionSource, target))) return false;
const engine = stealthy.engine;
if (target instanceof DoorControl)
return engine.canSpotDoor(target, visionSource);
const tgtToken = target?.document;
if (tgtToken instanceof TokenDocument)
return engine.checkDispositionAndCanDetect(visionSource, tgtToken, mode, setting.lightBased);
return true;
},
libWrapper.MIXED,
{ perf_mode: libWrapper.PERF_FAST }
);
}
stealthy.refreshPerception();
}

getSettingsParameters(version) {
Expand Down Expand Up @@ -298,13 +367,6 @@ export default class Engine {
config: true,
type: String,
default: '',
onChange: value => {
Stealthy.log(`Changing schema value to ${value}`);
const newValue = migrate(moduleVersion, value);
if (value != newValue) {
game.settings.set(Stealthy.MODULE_ID, 'schema', newValue);
}
}
},
activeSpot: {
scope: 'world',
Expand All @@ -319,54 +381,20 @@ export default class Engine {
let changed = false;
for (const mode in CONFIG.Canvas.detectionModes) {
if (!(mode in settings)) {
settings[mode] = this.defaultDetectionModes.includes(mode);
const enabled = this.defaultDetectionModes.includes(mode);
settings[mode] = this.buildDetectModePermission(mode, enabled);
changed = true;
}
}
return changed;
}

patchFoundry() {
let allowedModes = game.settings.get(Stealthy.MODULE_ID, 'allowedDetectionModes');
const changed = this.mixInDefaults(allowedModes);
if (changed) {
Hooks.once('ready', () => {
Stealthy.log('Allowed modes settings update', allowedModes);
game.settings.set(Stealthy.MODULE_ID, 'allowedDetectionModes', allowedModes);
});
}

for (const mode in allowedModes) {
if (!allowedModes[mode]) continue;
if (mode === 'undefined') continue;
if (!(mode in CONFIG.Canvas.detectionModes)) continue;

Stealthy.log(`patching ${mode}`);
libWrapper.register(
Stealthy.MODULE_ID,
`CONFIG.Canvas.detectionModes.${mode}._canDetect`,
function (wrapped, visionSource, target) {
if (!(wrapped(visionSource, target))) return false;
const engine = stealthy.engine;
if (target instanceof DoorControl)
return engine.canSpotDoor(target, visionSource);
const tgtToken = target?.document;
if (tgtToken instanceof TokenDocument)
return engine.checkDispositionAndCanDetect(visionSource, tgtToken, mode);
return true;
},
libWrapper.MIXED,
{ perf_mode: libWrapper.PERF_FAST }
);
}
}

// deprecated
isHidden(visionSource, tgtToken, detectionMode = undefined) {
return false;
}

checkDispositionAndCanDetect(visionSource, tgtToken, detectionMode) {
checkDispositionAndCanDetect(visionSource, tgtToken, detectionMode, lightBased) {
// Early out for buddies
if (tgtToken?.disposition === visionSource.object.document?.disposition) {
const friendlyStealth = game.settings.get(Stealthy.MODULE_ID, 'friendlyStealth');
Expand All @@ -383,6 +411,7 @@ export default class Engine {
visionSource,
tgtToken,
detectionMode,
lightBased,
stealthFlag,
stealthValue: this.getStealthValue(stealthFlag),
perceptionFlag,
Expand Down
13 changes: 2 additions & 11 deletions scripts/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ Hooks.once('setup', () => {

stealthy.stealthToActor = game.settings.get(Stealthy.MODULE_ID, 'stealthToActor');
stealthy.perceptionToActor = game.settings.get(Stealthy.MODULE_ID, 'perceptionToActor');

const schemaVersion = game.settings.get(Stealthy.MODULE_ID, 'schema');
if (schemaVersion !== moduleVersion) {
Stealthy.log(`Found schema version ${schemaVersion}`);
Hooks.once('ready', () => {
game.settings.set(Stealthy.MODULE_ID, 'schema', moduleVersion);
});
}

stealthy.bankingPerception = game.settings.get(Stealthy.MODULE_ID, 'activeSpot');

Stealthy.log(`${moduleVersion}: setup`);
Expand Down Expand Up @@ -95,8 +86,8 @@ Hooks.on('getSceneControlButtons', (controls) => {
title: game.i18n.localize("stealthy.bankPerception"),
toggle: true,
active: stealthy.bankingPerception,
onClick: (toggled) => {
game.settings.set(Stealthy.MODULE_ID, 'activeSpot', toggled);
onClick: async (toggled) => {
await game.settings.set(Stealthy.MODULE_ID, 'activeSpot', toggled);
stealthy.socket.executeForEveryone('TogglePerceptionBanking', toggled);
}
});
Expand Down
2 changes: 1 addition & 1 deletion scripts/stealthy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export class Stealthy {

static MODULE_ID = 'stealthy';
static ALLOWED_DETECTION_MODES = 'allowedDetectionModesV2';

constructor(engine) {
this.engine = engine;
Expand Down Expand Up @@ -28,7 +29,6 @@ export class Stealthy {
}

setup() {
this.engine.patchFoundry();
this.socket = socketlib.registerModule(Stealthy.MODULE_ID);
this.socket.register('TogglePerceptionBanking', this.togglePerceptionBanking);
this.socket.register('GetPerceptionBanking', this.getPerceptionBanking);
Expand Down
Loading

0 comments on commit 5d82624

Please sign in to comment.