All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

624 lines
22 KiB

1 year ago
  1. import { injectConfig } from "./lib/injectConfig.js";
  2. import { TileHandler } from './handlers/tileHandler.js';
  3. import { RefreshHandler } from './handlers/refreshHandler.js';
  4. import { DrawingHandler } from "./handlers/drawingHandler.js";
  5. import { UIHandler } from "./handlers/uiHandler.js";
  6. import { SightHandler } from "./handlers/sightHandler.js";
  7. import { LightHandler } from "./handlers/lightHandler.js";
  8. import { SoundHandler } from "./handlers/soundHandler.js";
  9. import { NoteHandler } from "./handlers/noteHandler.js";
  10. import { TokenHandler } from "./handlers/tokenHandler.js";
  11. import { TemplateHandler } from "./handlers/templateHandler.js";
  12. import { FoWHandler } from "./handlers/fowHandler.js";
  13. import { BackgroundHandler } from "./handlers/backgroundHandler.js";
  14. import { SettingsHandler } from "./handlers/settingsHandler.js";
  15. import { LevelsAPI } from "./API.js";
  16. import { registerWrappers } from './wrappers.js';
  17. import { inRange,getRangeForDocument, cloneTileMesh, inDistance } from './helpers.js';
  18. //warnings
  19. Hooks.on('ready', () => {
  20. if(!game.user.isGM) return;
  21. const recommendedVersion = '10.291';
  22. if(isNewerVersion(recommendedVersion, game.version)) {
  23. ui.notifications.error(`Levels recommends Foundry VTT version ${recommendedVersion} or newer. Levels might not work as expected in the currently installed version (${game.version}).`, {permanent: true});
  24. return;
  25. }
  26. })
  27. Object.defineProperty(globalThis, "_levels", {
  28. get: () => {
  29. console.warn("Levels: _levels is deprecated. Use CONFIG.Levels.API instead.");
  30. return CONFIG.Levels.API;
  31. }
  32. })
  33. Object.defineProperty(TileDocument.prototype, "elevation", {
  34. get: function () {
  35. if(CONFIG.Levels?.UI?.rangeEnabled && !this.id){
  36. return parseFloat(CONFIG.Levels.UI.range[0] || 0);
  37. }
  38. return this.overhead ? this.flags?.levels?.rangeBottom ?? canvas.scene.foregroundElevation : canvas.primary.background.elevation;
  39. }
  40. });
  41. Object.defineProperty(DrawingDocument.prototype, "elevation", {
  42. get: function () {
  43. if(CONFIG.Levels?.UI?.rangeEnabled && !this.id){
  44. return parseFloat(CONFIG.Levels.UI.range[0] || 0);
  45. }
  46. return this.flags?.levels?.rangeBottom ?? canvas.primary.background.elevation;
  47. }
  48. });
  49. Object.defineProperty(NoteDocument.prototype, "elevation", {
  50. get: function () {
  51. return this.flags?.levels?.rangeBottom ?? canvas.primary.background.elevation;
  52. }
  53. });
  54. Object.defineProperty(AmbientLightDocument.prototype, "elevation", {
  55. get: function () {
  56. if(CONFIG.Levels.UI.rangeEnabled && !this.id){
  57. return parseFloat(CONFIG.Levels.UI.range[0] || 0);
  58. }
  59. return this.flags?.levels?.rangeBottom ?? canvas.primary.background.elevation;
  60. }
  61. });
  62. Object.defineProperty(AmbientSoundDocument.prototype, "elevation", {
  63. get: function () {
  64. if(CONFIG.Levels.UI.rangeEnabled && !this.id){
  65. return parseFloat(CONFIG.Levels.UI.range[0] || 0);
  66. }
  67. if(isNaN(this.flags?.levels?.rangeBottom))return canvas.primary.background.elevation;
  68. return (this.flags?.levels?.rangeBottom + (this.flags?.levels?.rangeTop ?? this.flags?.levels?.rangeBottom)) / 2;
  69. }
  70. });
  71. Object.defineProperty(MeasuredTemplateDocument.prototype, "elevation", {
  72. get: function () {
  73. return this.flags?.levels?.elevation ?? canvas.primary.background.elevation;
  74. }
  75. });
  76. Object.defineProperty(WeatherEffects.prototype, "elevation", {
  77. get: function () {
  78. return canvas?.scene?.flags?.levels?.weatherElevation ?? Infinity;
  79. },
  80. set: function (value) {
  81. console.error("Cannot set elevation on WeatherEffects. Levels overrides WeatherEffects.prototype.elevation core behaviour. If you wish to set the WeatherEffects elevation, use SceneDocument.flags.levels.weatherElevation");
  82. }
  83. });
  84. Hooks.on("init", () => {
  85. const canvas3d = game.modules.get("levels-3d-preview")?.active;
  86. CONFIG.Levels = {
  87. MODULE_ID: "levels"
  88. }
  89. Object.defineProperty(CONFIG.Levels, "useCollision3D", {
  90. get: function () {
  91. return canvas3d && canvas.scene.flags["levels-3d-preview"]?.object3dSight
  92. }
  93. })
  94. Object.defineProperty(CONFIG.Levels, "currentToken", {
  95. get: function () {
  96. return this._currentToken;
  97. },
  98. set: function (value) {
  99. this._currentToken = value;
  100. Hooks.callAll("levelsPerspectiveChanged", this._currentToken);
  101. }
  102. })
  103. CONFIG.Levels.handlers = {
  104. TileHandler,
  105. RefreshHandler,
  106. DrawingHandler,
  107. UIHandler,
  108. SightHandler,
  109. LightHandler,
  110. SoundHandler,
  111. NoteHandler,
  112. TokenHandler,
  113. TemplateHandler,
  114. FoWHandler,
  115. BackgroundHandler,
  116. SettingsHandler,
  117. }
  118. CONFIG.Levels.helpers = {
  119. inRange,
  120. getRangeForDocument,
  121. cloneTileMesh,
  122. inDistance
  123. }
  124. CONFIG.Levels.API = LevelsAPI;
  125. CONFIG.Levels.UI = new LevelsUI();
  126. CONFIG.Levels.settings = new SettingsHandler();
  127. Hooks.callAll("levelsInit", CONFIG.Levels);
  128. registerWrappers();
  129. CONFIG.Levels.FoWHandler = new FoWHandler();
  130. CONFIG.Levels.handlers.BackgroundHandler.setupElevation();
  131. Hooks.callAll("levelsReady", CONFIG.Levels);
  132. })
  133. Hooks.once("ready", () => {
  134. if (game.modules.get("levels-3d-preview")?.active) return;
  135. // Module title
  136. const MODULE_ID = CONFIG.Levels.MODULE_ID;
  137. const MODULE_TITLE = game.modules.get(MODULE_ID).title;
  138. const FALLBACK_MESSAGE_TITLE = MODULE_TITLE;
  139. const FALLBACK_MESSAGE = `<large>
  140. <p><strong>This module may be very complicated for a first timer, be sure to stop by my <a href="https://theripper93.com/">Discord</a> for help and support from the wonderful community as well as many resources</strong></p>
  141. <p>Thanks to all the patreons supporting the development of this module making continued updates possible!</p>
  142. <p>If you want to support the development of the module or get customized support in setting up your maps you can do so here : <a href="https://www.patreon.com/theripper93">Patreon</a> </p></large>
  143. <p><strong>Patreons</strong> get also access to <strong>15+ premium modules</strong></p>
  144. <p>Is Levels not enough? Go Full 3D</p>
  145. <h1>3D Canvas</h1>
  146. <iframe width="385" height="225" src="https://www.youtube.com/embed/hC1QGZFUhcU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  147. <p>Check 3D Canvas and all my other <strong>15+ premium modules <a href="https://theripper93.com/">Here</a></strong></p>
  148. <p>Special thanks to Baileywiki for the support and feedback and Blair for the amazing UI elements</p>`;
  149. // Settings key used for the "Don't remind me again" setting
  150. const DONT_REMIND_AGAIN_KEY = "popup-dont-remind-again-2";
  151. // Dialog code
  152. game.settings.register(MODULE_ID, DONT_REMIND_AGAIN_KEY, {
  153. name: "",
  154. default: false,
  155. type: Boolean,
  156. scope: "world",
  157. config: false,
  158. });
  159. if (game.user.isGM && !game.settings.get(MODULE_ID, DONT_REMIND_AGAIN_KEY)) {
  160. new Dialog({
  161. title: FALLBACK_MESSAGE_TITLE,
  162. content: FALLBACK_MESSAGE,
  163. buttons: {
  164. ok: { icon: '<i class="fas fa-check"></i>', label: "Understood" },
  165. dont_remind: {
  166. icon: '<i class="fas fa-times"></i>',
  167. label: "Don't remind me again",
  168. callback: () =>
  169. game.settings.set(MODULE_ID, DONT_REMIND_AGAIN_KEY, true),
  170. },
  171. },
  172. }).render(true);
  173. }
  174. });
  175. Hooks.on("init", () => {
  176. game.settings.register(CONFIG.Levels.MODULE_ID, "tokenElevScale", {
  177. name: game.i18n.localize("levels.settings.tokenElevScale.name"),
  178. hint: game.i18n.localize("levels.settings.tokenElevScale.hint"),
  179. scope: "world",
  180. config: true,
  181. type: Boolean,
  182. default: false,
  183. onChange: () => {
  184. CONFIG.Levels.settings.cacheSettings()
  185. },
  186. });
  187. game.settings.register(CONFIG.Levels.MODULE_ID, "tokenElevScaleMultiSett", {
  188. name: game.i18n.localize("levels.settings.tokenElevScaleMultiSett.name"),
  189. hint: game.i18n.localize("levels.settings.tokenElevScaleMultiSett.hint"),
  190. scope: "world",
  191. config: true,
  192. type: Number,
  193. range: {
  194. min: 0.1,
  195. max: 2,
  196. step: 0.1,
  197. },
  198. default: 1,
  199. onChange: () => {
  200. CONFIG.Levels.settings.cacheSettings()
  201. },
  202. });
  203. game.settings.register(CONFIG.Levels.MODULE_ID, "fogHiding", {
  204. name: game.i18n.localize("levels.settings.fogHiding.name"),
  205. hint: game.i18n.localize("levels.settings.fogHiding.hint"),
  206. scope: "world",
  207. config: true,
  208. type: Boolean,
  209. default: true,
  210. onChange: () => {
  211. CONFIG.Levels.settings.cacheSettings()
  212. },
  213. });
  214. game.settings.register(CONFIG.Levels.MODULE_ID, "revealTokenInFog", {
  215. name: game.i18n.localize("levels.settings.revealTokenInFog.name"),
  216. hint: game.i18n.localize("levels.settings.revealTokenInFog.hint"),
  217. scope: "world",
  218. config: true,
  219. type: Boolean,
  220. default: false,
  221. onChange: () => {
  222. CONFIG.Levels.settings.cacheSettings()
  223. },
  224. });
  225. game.settings.register(CONFIG.Levels.MODULE_ID, "lockElevation", {
  226. name: game.i18n.localize("levels.settings.lockElevation.name"),
  227. hint: game.i18n.localize("levels.settings.lockElevation.hint"),
  228. scope: "world",
  229. config: true,
  230. type: Boolean,
  231. default: false,
  232. onChange: () => {
  233. CONFIG.Levels.settings.cacheSettings()
  234. },
  235. });
  236. game.settings.register(CONFIG.Levels.MODULE_ID, "hideElevation", {
  237. name: game.i18n.localize("levels.settings.hideElevation.name"),
  238. hint: game.i18n.localize("levels.settings.hideElevation.hint"),
  239. scope: "world",
  240. config: true,
  241. type: Number,
  242. choices: {
  243. 0: game.i18n.localize("levels.settings.hideElevation.opt0"),
  244. 1: game.i18n.localize("levels.settings.hideElevation.opt1"),
  245. 2: game.i18n.localize("levels.settings.hideElevation.opt2"),
  246. },
  247. default: 0,
  248. onChange: () => {
  249. CONFIG.Levels.settings.cacheSettings()
  250. },
  251. });
  252. game.settings.register(CONFIG.Levels.MODULE_ID, "enableTooltips", {
  253. name: game.i18n.localize("levels.settings.enableTooltips.name"),
  254. hint: game.i18n.localize("levels.settings.enableTooltips.hint"),
  255. scope: "world",
  256. config: true,
  257. type: Boolean,
  258. default: true,
  259. onChange: () => {
  260. CONFIG.Levels.settings.cacheSettings()
  261. },
  262. });
  263. game.settings.register(CONFIG.Levels.MODULE_ID, "preciseTokenVisibility", {
  264. name: game.i18n.localize("levels.settings.preciseTokenVisibility.name"),
  265. hint: game.i18n.localize("levels.settings.preciseTokenVisibility.hint"),
  266. scope: "world",
  267. config: true,
  268. type: Boolean,
  269. default: true,
  270. onChange: () => {
  271. CONFIG.Levels.settings.cacheSettings()
  272. },
  273. });
  274. game.settings.register(CONFIG.Levels.MODULE_ID, "exactTokenVisibility", {
  275. name: game.i18n.localize("levels.settings.exactTokenVisibility.name"),
  276. hint: game.i18n.localize("levels.settings.exactTokenVisibility.hint"),
  277. scope: "world",
  278. config: true,
  279. type: Boolean,
  280. default: false,
  281. onChange: () => {
  282. CONFIG.Levels.settings.cacheSettings()
  283. },
  284. });
  285. });
  286. Hooks.on("updateTile", (tile, updates) => {
  287. if(updates?.flags?.levels?.allWallBlockSight !== undefined){
  288. canvas.walls.placeables.forEach(w => w.identifyInteriorState())
  289. WallHeight.schedulePerceptionUpdate()
  290. }
  291. })
  292. Hooks.on("renderTileConfig", (app, html, data) => {
  293. const isInjected = html.find(`input[name="flags.${CONFIG.Levels.MODULE_ID}.rangeTop"]`).length > 0;
  294. if(isInjected) return;
  295. const injHtml = injectConfig.inject(app, html, {
  296. moduleId: "levels",
  297. tab: {
  298. name: "levels",
  299. label: "Levels",
  300. icon: "fas fa-layer-group",
  301. },
  302. rangeTop: {
  303. type: "number",
  304. label: game.i18n.localize("levels.tilecoonfig.rangeTop.name"),
  305. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  306. default: "",
  307. placeholder: "Infinity",
  308. step: "any",
  309. },
  310. rangeBottom: {
  311. type: "number",
  312. label: game.i18n.localize("levels.tilecoonfig.rangeBottom.name"),
  313. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  314. default: "",
  315. placeholder: "-Infinity",
  316. step: "any",
  317. },
  318. showIfAbove: {
  319. type: "checkbox",
  320. label: game.i18n.localize("levels.tilecoonfig.showIfAbove.name"),
  321. notes: game.i18n.localize("levels.tilecoonfig.showIfAbove.hint"),
  322. },
  323. showAboveRange: {
  324. type: "number",
  325. label: game.i18n.localize("levels.tilecoonfig.showAboveRange.name"),
  326. notes: game.i18n.localize("levels.tilecoonfig.showAboveRange.hint"),
  327. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  328. default: "",
  329. placeholder: "Infinity",
  330. },
  331. noCollision: {
  332. type: "checkbox",
  333. label: game.i18n.localize("levels.tilecoonfig.noCollision.name"),
  334. notes: game.i18n.localize("levels.tilecoonfig.noCollision.hint"),
  335. },
  336. noFogHide: {
  337. type: "checkbox",
  338. label: game.i18n.localize("levels.tilecoonfig.noFogHide.name"),
  339. notes: game.i18n.localize("levels.tilecoonfig.noFogHide.hint"),
  340. },
  341. isBasement: {
  342. type: "checkbox",
  343. label: game.i18n.localize("levels.tilecoonfig.isBasement.name"),
  344. notes: game.i18n.localize("levels.tilecoonfig.isBasement.hint"),
  345. },
  346. allWallBlockSight: {
  347. type: "checkbox",
  348. label: game.i18n.localize("levels.tilecoonfig.allWallBlockSight.name"),
  349. notes: game.i18n.localize("levels.tilecoonfig.allWallBlockSight.hint"),
  350. default: true,
  351. },
  352. });
  353. injHtml.find(`input[name="flags.${CONFIG.Levels.MODULE_ID}.rangeTop"]`).closest(".form-group").before(`
  354. <p class="notes" style="color: red" id="no-overhead-warning">${game.i18n.localize("levels.tilecoonfig.noOverhead")}</>
  355. `);
  356. html.on("change", "input", (e) => {
  357. const isOverhead = html.find(`input[name="overhead"]`).is(":checked");
  358. const isShowIfAbove = injHtml.find(`input[name="flags.levels.showIfAbove"]`).is(":checked");
  359. injHtml.find("input").prop("disabled", !isOverhead);
  360. injHtml.find("input[name='flags.levels.showAboveRange']").closest(".form-group").toggle(isShowIfAbove);
  361. html.find("#no-overhead-warning").toggle(!isOverhead);
  362. app.setPosition({ height: "auto" });
  363. })
  364. html.find(`input[name="overhead"]`).trigger("change");
  365. app.setPosition({ height: "auto" });
  366. });
  367. Hooks.on("renderAmbientLightConfig", (app, html, data) => {
  368. const injHtml = injectConfig.inject(app, html, {
  369. moduleId: "levels",
  370. inject: 'input[name="config.dim"]',
  371. rangeTop: {
  372. type: "number",
  373. label: game.i18n.localize("levels.tilecoonfig.rangeTop.name"),
  374. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  375. default: "",
  376. placeholder: "Infinity",
  377. step: "any",
  378. },
  379. rangeBottom: {
  380. type: "number",
  381. label: game.i18n.localize("levels.tilecoonfig.rangeBottom.name"),
  382. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  383. default: "",
  384. placeholder: "-Infinity",
  385. step: "any",
  386. },
  387. });
  388. });
  389. Hooks.on("renderNoteConfig", (app, html, data) => {
  390. const injHtml = injectConfig.inject(app, html, {
  391. moduleId: "levels",
  392. inject: 'select[name="textAnchor"]',
  393. rangeTop: {
  394. type: "number",
  395. label: game.i18n.localize("levels.tilecoonfig.rangeTop.name"),
  396. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  397. default: "",
  398. placeholder: "Infinity",
  399. step: "any",
  400. },
  401. rangeBottom: {
  402. type: "number",
  403. label: game.i18n.localize("levels.tilecoonfig.rangeBottom.name"),
  404. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  405. default: "",
  406. placeholder: "-Infinity",
  407. step: "any",
  408. },
  409. });
  410. });
  411. Hooks.on("renderAmbientSoundConfig", (app, html, data) => {
  412. const injHtml = injectConfig.inject(app, html, {
  413. moduleId: "levels",
  414. inject: 'input[name="radius"]',
  415. rangeTop: {
  416. type: "number",
  417. label: game.i18n.localize("levels.tilecoonfig.rangeTop.name"),
  418. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  419. default: "",
  420. placeholder: "Infinity",
  421. step: "any",
  422. },
  423. rangeBottom: {
  424. type: "number",
  425. label: game.i18n.localize("levels.tilecoonfig.rangeBottom.name"),
  426. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  427. default: "",
  428. placeholder: "-Infinity",
  429. step: "any",
  430. },
  431. });
  432. });
  433. Hooks.on("renderDrawingConfig", (app, html, data) => {
  434. const injHtml = injectConfig.inject(app, html, {
  435. moduleId: "levels",
  436. inject: 'input[name="z"]',
  437. drawingMode: {
  438. type: "select",
  439. label: game.i18n.localize("levels.drawingconfig.isHole.name"),
  440. default: 0,
  441. dType: "Number",
  442. options: {
  443. 0: game.i18n.localize("levels.drawingconfig.isHole.opt0"),
  444. 2: game.i18n.localize("levels.drawingconfig.isHole.opt2"),
  445. 21: game.i18n.localize("levels.drawingconfig.isHole.opt21"),
  446. 22: game.i18n.localize("levels.drawingconfig.isHole.opt22"),
  447. 3: game.i18n.localize("levels.drawingconfig.isHole.opt3"),
  448. },
  449. },
  450. elevatorFloors: {
  451. type: "text",
  452. label: game.i18n.localize("levels.drawingconfig.elevatorFloors.name"),
  453. notes: game.i18n.localize("levels.drawingconfig.elevatorFloors.hint"),
  454. },
  455. rangeTop: {
  456. type: "number",
  457. label: game.i18n.localize("levels.tilecoonfig.rangeTop.name"),
  458. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  459. default: "",
  460. placeholder: "Infinity",
  461. step: "any",
  462. },
  463. rangeBottom: {
  464. type: "number",
  465. label: game.i18n.localize("levels.tilecoonfig.rangeBottom.name"),
  466. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  467. default: "",
  468. placeholder: "-Infinity",
  469. step: "any",
  470. },
  471. });
  472. });
  473. Hooks.on("renderMeasuredTemplateConfig", (app, html, data) => {
  474. const injHtml = injectConfig.inject(app, html, {
  475. moduleId: "levels",
  476. inject: 'input[name="width"]',
  477. elevation: {
  478. type: "text",
  479. dType: "Number",
  480. label: game.i18n.localize("levels.template.elevation.name"),
  481. units: game.i18n.localize("levels.tilecoonfig.range.unit"),
  482. default: Infinity,
  483. step: "any",
  484. },
  485. special: {
  486. type: "number",
  487. label: game.i18n.localize("levels.template.depth.name"),
  488. default: 0,
  489. dType: "Number",
  490. },
  491. });
  492. });
  493. Hooks.on("renderDrawingHUD", (data, hud, drawData) => {
  494. let drawing = data.object.document;
  495. if (drawing.getFlag(CONFIG.Levels.MODULE_ID, "drawingMode")) {
  496. let active = drawing.getFlag(CONFIG.Levels.MODULE_ID, "stairLocked") || false;
  497. let toggleStairbtn = `<div class="control-icon${
  498. active ? " active" : ""
  499. }" id="toggleStair">
  500. <i class="fas fa-lock" width="36" height="36" title='${game.i18n.localize(
  501. "levels.drawingHud.title"
  502. )}'></i>
  503. </div>`;
  504. const controlIcons = hud.find("div.control-icon");
  505. controlIcons.last().after(toggleStairbtn);
  506. $(hud.find(`div[id="toggleStair"]`)).on("click", test);
  507. function test() {
  508. console.log("test");
  509. active = !active;
  510. drawing.setFlag(
  511. CONFIG.Levels.MODULE_ID,
  512. "stairLocked",
  513. !(drawing.getFlag(CONFIG.Levels.MODULE_ID, "stairLocked") || false)
  514. );
  515. let hudbtn = hud.find(`div[id="toggleStair"]`);
  516. if (active) hudbtn.addClass("active");
  517. else hudbtn.removeClass("active");
  518. }
  519. }
  520. });
  521. Hooks.on("renderTokenHUD", (data, hud, drawData) => {
  522. if (
  523. CONFIG.Levels.settings.get("lockElevation") &&
  524. !game.user.isGM
  525. ) {
  526. const controlIcons = hud.find(`div[class="attribute elevation"]`);
  527. $(controlIcons[0]).remove();
  528. }
  529. });
  530. Hooks.on("preCreateMeasuredTemplate", (template) => {
  531. const templateData = CONFIG.Levels.handlers.TemplateHandler.getTemplateData();
  532. if(template.flags?.levels?.elevation) return;
  533. template.updateSource({
  534. flags: { levels: { elevation: templateData.elevation, special: templateData.special } },
  535. });
  536. });
  537. Hooks.on("renderSceneConfig", (app, html, data) => {
  538. injectConfig.inject(app, html, {
  539. "moduleId": "levels",
  540. "inject": 'input[name="foregroundElevation"]',
  541. "backgroundElevation": {
  542. type: "number",
  543. label: game.i18n.localize("levels.sceneconfig.backgroundElevation.name"),
  544. notes: game.i18n.localize("levels.sceneconfig.backgroundElevation.notes"),
  545. default: 0,
  546. },
  547. });
  548. injectConfig.inject(app, html, {
  549. "moduleId": "levels",
  550. "inject": 'select[name="weather"]',
  551. "weatherElevation": {
  552. type: "number",
  553. label: game.i18n.localize("levels.sceneconfig.weatherElevation.name"),
  554. notes: game.i18n.localize("levels.sceneconfig.weatherElevation.notes"),
  555. default: "",
  556. placeholder: "Infinity"
  557. },
  558. });
  559. injectConfig.inject(app, html, {
  560. "moduleId": "levels",
  561. "inject": 'input[name="fogExploration"]',
  562. "lightMasking": {
  563. type: "checkbox",
  564. label: game.i18n.localize("levels.sceneconfig.lightMasking.name"),
  565. notes: game.i18n.localize("levels.sceneconfig.lightMasking.notes"),
  566. default: true,
  567. },
  568. });
  569. })