Skip to content

Commit

Permalink
Russion translation and GI workarounds
Browse files Browse the repository at this point in the history
  • Loading branch information
Eligarf committed Jul 4, 2024
1 parent 812c814 commit 9b702e7
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 9 deletions.
6 changes: 6 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# v4.5.0
* Added Russian translation (thanks VirusNik21)
* Stealthy's light exposure calculations work best in scenes without global illumination enabled, but I've added some support to try and hack a reasonable guess-timate when it is enabled.
* Added a game setting in Stealthy to specify a dim light threshold to use in GI-enabled scenes for exposure purposes.
* dnd5e: When banking perception once combat is active, force a duration on the Spot effect if an already existing effect is found.

# v4.4.1
* Update pt-BR.json (thanks Kharmans)
* Handle darkness sources properly
Expand Down
4 changes: 4 additions & 0 deletions languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
"dim": "in Dim light",
"dark": "in Dark"
},
"gIDimThreshold": {
"name": "Global Illumination dim light threshold",
"hint": "On scenes with GI enabled, bright light exposure is assumed unless the darkness level exceeds the scene's GI threshold multiplied by this factor, yielding dim light. Use 1.0 to transition directly from bright to dark."
},
"stealthToActor": {
"name": "Bank stealth rolls to actor",
"hint": "Bank stealth rolls in an actor effect/item rather than the token."
Expand Down
6 changes: 5 additions & 1 deletion languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
"dim": "in Dim light",
"dark": "in Dark"
},
"gIDimThreshold": {
"name": "Global Illumination dim light threshold",
"hint": "On scenes with GI enabled, bright light exposure is assumed unless the darkness level exceeds the scene's GI threshold multiplied by this factor, yielding dim light. Use 1.0 to transition directly from bright to dark."
},
"stealthToActor": {
"name": "Bank stealth rolls to actor",
"hint": "Bank stealth rolls in an actor effect/item rather than the token."
Expand Down Expand Up @@ -138,4 +142,4 @@
"dependency": "Stealthy nécessite les actions de cache-cache des macros d'action de base dans PF2e Workbench"
}
}
}
}
6 changes: 5 additions & 1 deletion languages/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
"dim": "Em Penumbra",
"dark": "Em Escuridão"
},
"gIDimThreshold": {
"name": "Global Illumination dim light threshold",
"hint": "On scenes with GI enabled, bright light exposure is assumed unless the darkness level exceeds the scene's GI threshold multiplied by this factor, yielding dim light. Use 1.0 to transition directly from bright to dark."
},
"stealthToActor": {
"name": "Acumular Furtividade no Ator",
"hint": "Acumula as rolagens de Furtividade no efeito/item de um ator, ao invés do próprio ator."
Expand Down Expand Up @@ -138,4 +142,4 @@
"dependency": "Stealthy requer as ações Hide and Seek das macros de ação básicas no PF2e Workbench"
}
}
}
}
144 changes: 144 additions & 0 deletions languages/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{
"stealthy": {
"hidden": {
"name": "Стелс",
"description": "Сохраненный Стелс бросок",
"source": "Источник эффекта Стелс",
"preloc": {
"key": "Локализованное значение для статуса Hidden",
"hint": "Эта строка будет обработана через i18n.localize. Изменение может повлиять на общую автоматизацию эффектов модуля."
},
"icon": "Иконка Статуса Стелс",
"iconhint": "Значек по умолчанию для статуса Стелс"
},
"spot": {
"name": "Поиск",
"description": "Сохраненный бросок поиска",
"source": "Источник эффекта Поиск",
"key": "Локализованное значение для статуса Spot",
"preloc": {},
"icon": "Иконка Статуса Поиск",
"iconhint": "Значек по умолчанию для статуса Поиск"
},
"bankPerception": "Ролики восприятия банка",
"title": "Стелс",
"spotHiddenDoors": {
"name": "Автоматическое обнаружение скрытых дверей",
"hint": "Токены могут обнаружить скрытые двери, если их проверка восприятия превышает значение скрытности двери."
},
"friendlyStealth": {
"name": "Как реагировать на дружественные жетоны в стелсе?",
"allow": "Всегда проверять Восприятие",
"inCombat": "Проверять Восприятие только в бою",
"ignore": "Игнорировать проверки"
},
"playerHud": {
"name": "Игрок может видеть сохраненные броски",
"hint": "Значение последнего броска будет отображаться в HUD токена, игрок может только просматривать."
},
"exposure": {
"name": "Отображать освещенность",
"hint": "Иконка в HUD отображает текущий уровень освещенности токена",
"bright": "Яркий свет",
"dim": "Тусклый свет",
"dark": "Темнота"
},
"gIDimThreshold": {
"name": "Порог яркости глобального освещения (GI)",
"hint": "В сценах с включенным GI предполагается яркое освещение, пока уровень темноты не превысит порог GI сцены, умноженный на этот коэффициент, что приведет к тусклому освещению. Используйте 1,0 для прямого перехода от яркого света к темноте."
},
"stealthToActor": {
"name": "Сохранять Стелс бросок в актере",
"hint": "Сохранять бросок в актере вместо токена."
},
"perceptionToActor": {
"name": "Сохранять Поиск бросок в актере",
"hint": "Сохранять бросок в актере вместо токена."
},
"detectionModesMenu": {
"name": "Режимы обнаружения",
"label": "Настройка Режимов Обнаружения",
"hint": "Определяет, для каких Режимов Обнаружения эффект стелс будет скрывать/раскрывать токены",
"title": "Настройка Режимов Обнаружения применяющих Стелс",
"warning": "Для применения изменений к выбранным Режимам Обнаружения требуется перезагрузка."
},
"source": {
"hint": "Какой источник использовать для активного эффекта, хранящегося в актере",
"ce": {
"name": "Convenient Effects",
"beforeLabel": "Эффект не найдет",
"afterLabel": "Использовать Невидимость по умолчанию для СЕ"
},
"clt": {
"name": "Condition Lab & Triggler",
"beforeLabel": "Эффект не найдет",
"afterLabel": "Использовать Невидимость по умолчанию для СLT"
},
"ae": "Невидимость",
"min": "Невидимость (без эффекта на токене)"
},
"schema": {
"name": "Версия схемы",
"hint": "Установите это значение на предыдущую версию, чтобы принудительно перенести данные с указанной версии на текущую."
},
"logLevel": {
"name": "Журналирование",
"none": "не записывать",
"debug": "Отладка",
"log": "Журнал"
},
"config": {
"general": "Основные Настройки",
"effects": "Настройка Active Effect",
"debug": "Настройка Отладки",
"advanced": "Расширенные Настройки"
},
"door": {
"stealth": "Значение Скрытности",
"maxRange": "Расстояние для обнаружения"
},
"dnd5e": {
"name": "Dnd5e",
"endTurn": {
"name": "Конец Хода",
"hint": "Заканчивает ход у выбранного токена, если вы текущий боец."
},
"hiding": {
"choice": "Использовать статус эффект 'Спрятаться'",
"iconhint": "Иконка для эффекта Стелс (не будет применена если использован родной эффект)"
},
"perceptionDisadvantage": {
"name": "Помеха при проверке Восприятия в Тусклом освещении.",
"hint": "Использовать освещение сцены, чтобы определить, применяется ли помеха Восприятия к токенам с невидимостью. Используется центр X,Y токена."
},
"ignorePassiveFloor": {
"name": "Игнорировать минимальное значение пассивного Восприятия при активных проверках",
"hint": "Включает домашние правила, позволяющие игнорировать значение пассивного Восприятия при активной проверке Восприятия"
},
"friendlyUmbralSight": {
"name": "ОПРЕДЕЛЕНО - Как определять дружественные жетоны с помощью Umbral Sight",
"allow": "Umbral Sight скрыт для Ночного зрения",
"inCombat": "Игнорировать Umbral Sight не в бою",
"ignore": "Игнорировать Umbral Sight"
},
"stealthKey": {
"name": "Параметр Скрытность в системе",
"hint": "Должен быть параметр из списка actor.system.skills, по умолчанию - 'ste'. Используется для перехвата бросков из пользовательского навыка в Стелс, все подсказки и т.д. будут продолжать использовать локализацию из 'stealth'"
},
"perceptionKey": {
"name": "Параметр Восприятия в системе",
"hint": "Должен быть параметр из списка actor.system.skills, по умолчанию - 'prc'. См. подсказку к ключу навыка 'Скрытность', но замените на Восприятие"
}
},
"pf1": {
"name": "Pathfinder",
"passiveSpotOffset": {
"name": "Предпологаемый бросок для пассивного поиска",
"hint": "Если бросок восприятия не доступен, возьмите это значение для d20 при проверке видимости. Обычно -99 для автоматического провала или 10 для результата 'дубль-10'.."
}
},
"pf2e": {
"dependency": "Скрытность требует действий Hide и Seek из Basic Action Macros в PF2e Workbench."
}
}
}
61 changes: 54 additions & 7 deletions scripts/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default class Engine {
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);
game.settings.register(Stealthy.MODULE_ID, 'gIDimThreshold', settings.gIDimThreshold);
game.settings.register(Stealthy.MODULE_ID, 'spotSecretDoors', settings.spotSecretDoors);

game.settings.register(Stealthy.MODULE_ID, 'stealthToActor', settings.stealthToActor);
Expand Down Expand Up @@ -160,6 +161,19 @@ export default class Engine {
type: Boolean,
default: false,
},
gIDimThreshold: {
name: "stealthy.gIDimThreshold.name",
hint: "stealthy.gIDimThreshold.hint",
scope: 'world',
config: true,
type: Number,
default: 0.5,
range: {
min: 0,
max: 1,
step: 0.05
}
},
spotSecretDoors: {
name: "stealthy.spotHiddenDoors.name",
hint: "stealthy.spotHiddenDoors.hint",
Expand Down Expand Up @@ -512,7 +526,7 @@ export default class Engine {
};
}

async updateOrCreateEffect({ name, actor, flag, source, makeEffect }) {
async updateOrCreateEffect({ name, actor, flag, source, makeEffect, tweakEffect }) {
const beforeV11 = Math.floor(game.version) < 11;
let effect = actor.effects.find((e) => name === (beforeV11 ? e.label : e.name));

Expand Down Expand Up @@ -558,6 +572,9 @@ export default class Engine {
}

effect = foundry.utils.duplicate(effect);
if (tweakEffect) {
tweakEffect(effect);
}
effect.flags.stealthy = flag;
effect.disabled = false;
await actor.updateEmbeddedDocuments('ActiveEffect', [effect]);
Expand Down Expand Up @@ -592,28 +609,58 @@ export default class Engine {
if (scene !== canvas.scene || !scene.tokenVision) return undefined;

const beforeV12 = Math.floor(game.version) < 12;
const hasGlobal = (beforeV12) ? scene.globalLight : scene.environment.globalLight.enabled;
if (hasGlobal) return 'bright';

// If GI is on, check to see if we think it is dim or bright.
let exposure = 'dark';
const center = token.center;
if (beforeV12) {
const darkness = scene.darkness;
if (scene.globalLight && darkness <= scene.globalLightThreshold) {
const factor = game.settings.get(Stealthy.MODULE_ID, 'gIDimThreshold');
exposure = (darkness <= factor * scene.globalLightThreshold) ? 'bright' : 'dim';
}
}
else {
const gl = scene.environment.globalLight;
if (gl.enabled) {
const darkness = canvas.effects.getDarknessLevel(center, token.document.elevation);
if (darkness <= gl.darkness.max) {
const factor = game.settings.get(Stealthy.MODULE_ID, 'gIDimThreshold');
exposure = (darkness <= factor * gl.darkness.max) ? 'bright' : 'dim';
}
}
}

// If GI is on, need to explicitly check to see if token is in darkness source, after which we
// can short-circuit other checks if we are in bright GI
if (exposure !== 'dark') {
const lights = scene.lights
.map(light => beforeV12 ? light._object?.source : light._object?.lightSource)
.concat(scene.tokens.filter(t => t.object?.light?.active).map(t => t.object.light))
.filter(light => (beforeV12) ? light.isDarkness : light instanceof foundry.canvas.sources.PointDarknessSource)
.filter(light => light?.shape?.contains(center.x, center.y));
if (lights.length) return 'dark';
if (exposure === 'bright') return exposure;
}

const scale = scene.dimensions.size / scene.dimensions.distance;

// function distSquared(a, b, az, bz) {
// const xDiff = a.x - b.x;
// const yDiff = a.y - b.y;
// const zDiff = scale * (az - bz);
// return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
// }

let lights = scene.lights
// Return the GI exposure if we aren't in any lights
const lights = scene.lights
.map(light => beforeV12 ? light._object?.source : light._object?.lightSource)
.concat(scene.tokens.filter(t => t.object?.light?.active).map(t => t.object.light))
.filter(light => !((beforeV12) ? light.isDarkness : light instanceof foundry.canvas.sources.PointDarknessSource))
.filter(light => light?.shape?.contains(center.x, center.y));
// .filter(light => distSquared(center, light, token.document.elevation, light.elevation) < light.data.dim * light.data.dim);
if (!lights.length) return exposure;

if (!lights.length) return 'dark';

// Look for a light that shines brightly enough, otherwise we are dimly lit
const bright = lights.find(light =>
scale * ((beforeV12)
? canvas.grid.measureDistance(center, light)
Expand Down
14 changes: 14 additions & 0 deletions scripts/systems/dnd5e.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@ class Engine5e extends Engine {
};
}

async updateOrCreatePerceptionEffect(actor, flag) {
await this.updateOrCreateEffect({
name: this.spotName,
actor,
flag,
source: game.settings.get(Stealthy.MODULE_ID, 'spotSource'),
makeEffect: this.makePerceptionEffectMaker(this.spotName),
tweakEffect: (effect) => {
if (game.combat) effect.duration = { turns: 1, seconds: 6 };
}
});
stealthy.refreshPerception();
}

}

Hooks.once('init', () => {
Expand Down

0 comments on commit 9b702e7

Please sign in to comment.