/** * Runtime configuration settings for Foundry VTT which exposes a large number of variables which determine how * aspects of the software behaves. * * Unlike the CONST analog which is frozen and immutable, the CONFIG object may be updated during the course of a * session or modified by system and module developers to adjust how the application behaves. * * @type {object} */ const CONFIG = globalThis.CONFIG = { /** * Configure debugging flags to display additional information */ debug: { applications: false, audio: false, combat: false, dice: false, documents: false, fog: { extractor: false, manager: false }, hooks: false, av: false, avclient: false, mouseInteraction: false, time: false, keybindings: false, polygons: false, gamepad: false, canvas: { primary: { bounds: false } }, rollParsing: false }, /** * Configure the verbosity of compatibility warnings generated throughout the software. * The compatibility mode defines the logging level of any displayed warnings. * The includePatterns and excludePatterns arrays provide a set of regular expressions which can either only * include or specifically exclude certain file paths or warning messages. * Exclusion rules take precedence over inclusion rules. * * @see {@link CONST.COMPATIBILITY_MODES} * @type {{mode: number, includePatterns: RegExp[], excludePatterns: RegExp[]}} * * @example Include Specific Errors * ```js * const includeRgx = new RegExp("/systems/dnd5e/module/documents/active-effect.mjs"); * CONFIG.compatibility.includePatterns.push(includeRgx); * ``` * * @example Exclude Specific Errors * ```js * const excludeRgx = new RegExp("/systems/dnd5e/"); * CONFIG.compatibility.excludePatterns.push(excludeRgx); * ``` * * @example Both Include and Exclude * ```js * const includeRgx = new RegExp("/systems/dnd5e/module/actor/"); * const excludeRgx = new RegExp("/systems/dnd5e/module/actor/sheets/base.js"); * CONFIG.compatibility.includePatterns.push(includeRgx); * CONFIG.compatibility.excludePatterns.push(excludeRgx); * ``` * * @example Targeting more than filenames * ```js * const includeRgx = new RegExp("applyActiveEffects"); * CONFIG.compatibility.includePatterns.push(includeRgx); * ``` */ compatibility: { mode: CONST.COMPATIBILITY_MODES.WARNING, includePatterns: [], excludePatterns: [] }, compendium: { /** * Configure a table of compendium UUID redirects. Must be configured before the game *ready* hook is fired. * @type {Record} * * @example Re-map individual UUIDs * ```js * CONFIG.compendium.uuidRedirects["Compendium.system.heroes.Actor.Tf0JDPzHOrIxz6BH"] = "Compendium.system.villains.Actor.DKYLeIliXXzlAZ2G"; * ``` * * @example Redirect UUIDs from one compendium to another. * ```js * CONFIG.compendium.uuidRedirects["Compendium.system.heroes"] = "Compendium.system.villains"; * ``` */ uuidRedirects: {}, }, /** * Configure the DatabaseBackend used to perform Document operations * @type {foundry.data.ClientDatabaseBackend} */ DatabaseBackend: new foundry.data.ClientDatabaseBackend(), /** * Configuration for the Actor document */ Actor: { documentClass: Actor, collection: Actors, compendiumIndexFields: [], compendiumBanner: "ui/banners/actor-banner.webp", sidebarIcon: "fas fa-user", dataModels: {}, typeLabels: {}, typeIcons: {}, trackableAttributes: {} }, /** * Configuration for the Adventure document. * Currently for internal use only. * @private */ Adventure: { documentClass: Adventure, exporterClass: AdventureExporter, compendiumIndexFields: [], compendiumBanner: "ui/banners/adventure-banner.webp", sidebarIcon: "fa-solid fa-treasure-chest" }, /** * Configuration for the Cards primary Document type */ Cards: { collection: CardStacks, compendiumIndexFields: [], compendiumBanner: "ui/banners/cards-banner.webp", documentClass: Cards, sidebarIcon: "fa-solid fa-cards", dataModels: {}, presets: { pokerDark: { type: "deck", label: "CARDS.DeckPresetPokerDark", src: "cards/poker-deck-dark.json" }, pokerLight: { type: "deck", label: "CARDS.DeckPresetPokerLight", src: "cards/poker-deck-light.json" } }, typeLabels: {}, typeIcons: { deck: "fas fa-cards", hand: "fa-duotone fa-cards", pile: "fa-duotone fa-layer-group" } }, /** * Configuration for the ChatMessage document */ ChatMessage: { documentClass: ChatMessage, collection: Messages, template: "templates/sidebar/chat-message.html", sidebarIcon: "fas fa-comments", dataModels: {}, typeLabels: {}, typeIcons: {}, batchSize: 100 }, /** * Configuration for the Combat document */ Combat: { documentClass: Combat, collection: CombatEncounters, sidebarIcon: "fas fa-swords", dataModels: {}, typeLabels: {}, typeIcons: {}, initiative: { formula: null, decimals: 2 }, sounds: { epic: { label: "COMBAT.Sounds.Epic", startEncounter: ["sounds/combat/epic-start-3hit.ogg", "sounds/combat/epic-start-horn.ogg"], nextUp: ["sounds/combat/epic-next-horn.ogg"], yourTurn: ["sounds/combat/epic-turn-1hit.ogg", "sounds/combat/epic-turn-2hit.ogg"] }, mc: { label: "COMBAT.Sounds.MC", startEncounter: ["sounds/combat/mc-start-battle.ogg", "sounds/combat/mc-start-begin.ogg", "sounds/combat/mc-start-fight.ogg", "sounds/combat/mc-start-fight2.ogg"], nextUp: ["sounds/combat/mc-next-itwillbe.ogg", "sounds/combat/mc-next-makeready.ogg", "sounds/combat/mc-next-youare.ogg"], yourTurn: ["sounds/combat/mc-turn-itisyour.ogg", "sounds/combat/mc-turn-itsyour.ogg"] } } }, /** * @typedef {object} DiceFulfillmentConfiguration * @property {Record} dice The die denominations available for configuration. * @property {Record} methods The methods available for fulfillment. * @property {string} defaultMethod Designate one of the methods to be used by default * for dice fulfillment, if the user hasn't specified * otherwise. Leave this blank to use the configured * randomUniform to generate die rolls. */ /** * @typedef {object} DiceFulfillmentDenomination * @property {string} label The human-readable label for the die. * @property {string} icon An icon to display on the configuration sheet. */ /** * @typedef {object} DiceFulfillmentMethod * @property {string} label The human-readable label for the fulfillment method. * @property {string} [icon] An icon to represent the fulfillment method. * @property {boolean} [interactive=false] Whether this method requires input from the user or if it is * fulfilled entirely programmatically. * @property {DiceFulfillmentHandler} [handler] A function to invoke to programmatically fulfil a given term for non- * interactive fulfillment methods. * @property {typeof RollResolver} [resolver] A custom RollResolver implementation. If the only interactive methods * the user has configured are this method and manual, this resolver * will be used to resolve interactive rolls, instead of the default * resolver. This resolver must therefore be capable of handling manual * rolls. */ /** * Only used for non-interactive fulfillment methods. If a die configured to use this fulfillment method is rolled, * this handler is called and awaited in order to produce the die roll result. * @callback DiceFulfillmentHandler * @param {DiceTerm} term The term being fulfilled. * @param {object} [options] Additional options to configure fulfillment. * @returns {Promise} The fulfilled value, or undefined if it could not be fulfilled. */ /** * @callback RollFunction * @param {...any} args * @returns {Promise|number} */ /** * Configuration for dice rolling behaviors in the Foundry Virtual Tabletop client. * @type {object} */ Dice: { /** * The Dice types which are supported. * @type {Array} */ types: [foundry.dice.terms.Die, foundry.dice.terms.FateDie], rollModes: Object.entries(CONST.DICE_ROLL_MODES).reduce((obj, e) => { let [k, v] = e; obj[v] = `CHAT.Roll${k.titleCase()}`; return obj; }, {}), /** * Configured Roll class definitions * @type {Array} */ rolls: [Roll], /** * Configured DiceTerm class definitions * @type {Record} */ termTypes: { DiceTerm: foundry.dice.terms.DiceTerm, FunctionTerm: foundry.dice.terms.FunctionTerm, NumericTerm: foundry.dice.terms.NumericTerm, OperatorTerm: foundry.dice.terms.OperatorTerm, ParentheticalTerm: foundry.dice.terms.ParentheticalTerm, PoolTerm: foundry.dice.terms.PoolTerm, StringTerm: foundry.dice.terms.StringTerm }, /** * Configured roll terms and the classes they map to. * @enum {typeof DiceTerm} */ terms: { c: foundry.dice.terms.Coin, d: foundry.dice.terms.Die, f: foundry.dice.terms.FateDie }, /** * A function used to provide random uniform values. * @type {function():number} */ randomUniform: foundry.dice.MersenneTwister.random, /** * A parser implementation for parsing Roll expressions. * @type {typeof RollParser} */ parser: foundry.dice.RollParser, /** * A collection of custom functions that can be included in roll expressions. * @type {Record} */ functions: {}, /** * Dice roll fulfillment configuration. * @type {DiceFulfillmentConfiguration} * @type {{dice: Record, methods: Record}} */ fulfillment: { dice: { d4: { label: "d4", icon: '' }, d6: { label: "d6", icon: '' }, d8: { label: "d8", icon: '' }, d10: { label: "d10", icon: '' }, d12: { label: "d12", icon: '' }, d20: { label: "d20", icon: '' }, d100: { label: "d100", icon: '' } }, methods: { mersenne: { label: "DICE.FULFILLMENT.Mersenne", interactive: false, handler: term => term.mapRandomFace(foundry.dice.MersenneTwister.random()) }, manual: { label: "DICE.FULFILLMENT.Manual", icon: '', interactive: true } }, defaultMethod: "" } }, /** * Configuration for the FogExploration document */ FogExploration: { documentClass: FogExploration, collection: FogExplorations }, /** * Configuration for the Folder document */ Folder: { documentClass: Folder, collection: Folders, sidebarIcon: "fas fa-folder" }, /** * Configuration for Item document */ Item: { documentClass: Item, collection: Items, compendiumIndexFields: [], compendiumBanner: "ui/banners/item-banner.webp", sidebarIcon: "fas fa-suitcase", dataModels: {}, typeLabels: {}, typeIcons: {} }, /** * Configuration for the JournalEntry document */ JournalEntry: { documentClass: JournalEntry, collection: Journal, compendiumIndexFields: [], compendiumBanner: "ui/banners/journalentry-banner.webp", noteIcons: { Anchor: "icons/svg/anchor.svg", Barrel: "icons/svg/barrel.svg", Book: "icons/svg/book.svg", Bridge: "icons/svg/bridge.svg", Cave: "icons/svg/cave.svg", Castle: "icons/svg/castle.svg", Chest: "icons/svg/chest.svg", City: "icons/svg/city.svg", Coins: "icons/svg/coins.svg", Fire: "icons/svg/fire.svg", "Hanging Sign": "icons/svg/hanging-sign.svg", House: "icons/svg/house.svg", Mountain: "icons/svg/mountain.svg", "Oak Tree": "icons/svg/oak.svg", Obelisk: "icons/svg/obelisk.svg", Pawprint: "icons/svg/pawprint.svg", Ruins: "icons/svg/ruins.svg", Skull: "icons/svg/skull.svg", Statue: "icons/svg/statue.svg", Sword: "icons/svg/sword.svg", Tankard: "icons/svg/tankard.svg", Temple: "icons/svg/temple.svg", Tower: "icons/svg/tower.svg", Trap: "icons/svg/trap.svg", Village: "icons/svg/village.svg", Waterfall: "icons/svg/waterfall.svg", Windmill: "icons/svg/windmill.svg" }, sidebarIcon: "fas fa-book-open" }, /** * Configuration for the Macro document */ Macro: { documentClass: Macro, collection: Macros, compendiumIndexFields: [], compendiumBanner: "ui/banners/macro-banner.webp", sidebarIcon: "fas fa-code" }, /** * Configuration for the Playlist document */ Playlist: { documentClass: Playlist, collection: Playlists, compendiumIndexFields: [], compendiumBanner: "ui/banners/playlist-banner.webp", sidebarIcon: "fas fa-music", autoPreloadSeconds: 20 }, /** * Configuration for RollTable random draws */ RollTable: { documentClass: RollTable, collection: RollTables, compendiumIndexFields: ["formula"], compendiumBanner: "ui/banners/rolltable-banner.webp", sidebarIcon: "fas fa-th-list", resultIcon: "icons/svg/d20-black.svg", resultTemplate: "templates/dice/table-result.html" }, /** * Configuration for the Scene document */ Scene: { documentClass: Scene, collection: Scenes, compendiumIndexFields: [], compendiumBanner: "ui/banners/scene-banner.webp", sidebarIcon: "fas fa-map" }, Setting: { documentClass: Setting, collection: WorldSettings }, /** * Configuration for the User document */ User: { documentClass: User, collection: Users }, /* -------------------------------------------- */ /* Canvas */ /* -------------------------------------------- */ /** * Configuration settings for the Canvas and its contained layers and objects * @type {object} */ Canvas: { blurStrength: 8, blurQuality: 4, darknessColor: 0x303030, daylightColor: 0xEEEEEE, brightestColor: 0xFFFFFF, chatBubblesClass: ChatBubbles, darknessLightPenalty: 0.25, dispositionColors: { HOSTILE: 0xE72124, NEUTRAL: 0xF1D836, FRIENDLY: 0x43DFDF, INACTIVE: 0x555555, PARTY: 0x33BC4E, CONTROLLED: 0xFF9829, SECRET: 0xA612D4 }, /** * The class used to render door control icons. * @type {typeof DoorControl} */ doorControlClass: DoorControl, exploredColor: 0x000000, unexploredColor: 0x000000, darknessToDaylightAnimationMS: 10000, daylightToDarknessAnimationMS: 10000, darknessSourceClass: foundry.canvas.sources.PointDarknessSource, lightSourceClass: foundry.canvas.sources.PointLightSource, globalLightSourceClass: foundry.canvas.sources.GlobalLightSource, visionSourceClass: foundry.canvas.sources.PointVisionSource, soundSourceClass: foundry.canvas.sources.PointSoundSource, groups: { hidden: { groupClass: HiddenCanvasGroup, parent: "stage" }, rendered: { groupClass: RenderedCanvasGroup, parent: "stage" }, environment: { groupClass: EnvironmentCanvasGroup, parent: "rendered" }, primary: { groupClass: PrimaryCanvasGroup, parent: "environment" }, effects: { groupClass: EffectsCanvasGroup, parent: "environment" }, visibility: { groupClass: CanvasVisibility, parent: "rendered" }, interface: { groupClass: InterfaceCanvasGroup, parent: "rendered", zIndexDrawings: 500, zIndexScrollingText: 1100 }, overlay: { groupClass: OverlayCanvasGroup, parent: "stage" } }, layers: { weather: { layerClass: WeatherEffects, group: "primary" }, grid: { layerClass: GridLayer, group: "interface" }, regions: { layerClass: RegionLayer, group: "interface" }, drawings: { layerClass: DrawingsLayer, group: "interface" }, templates: { layerClass: TemplateLayer, group: "interface" }, tiles: { layerClass: TilesLayer, group: "interface" }, walls: { layerClass: WallsLayer, group: "interface" }, tokens: { layerClass: TokenLayer, group: "interface" }, sounds: { layerClass: SoundsLayer, group: "interface" }, lighting: { layerClass: LightingLayer, group: "interface" }, notes: { layerClass: NotesLayer, group: "interface" }, controls: { layerClass: ControlsLayer, group: "interface" } }, lightLevels: { dark: 0, halfdark: 0.5, dim: 0.25, bright: 1.0 }, fogManager: FogManager, /** * @enum {typeof PointSourcePolygon} */ polygonBackends: { sight: ClockwiseSweepPolygon, light: ClockwiseSweepPolygon, sound: ClockwiseSweepPolygon, move: ClockwiseSweepPolygon }, darknessSourcePaddingMultiplier: 0.5, visibilityFilter: VisibilityFilter, visualEffectsMaskingFilter: VisualEffectsMaskingFilter, rulerClass: Ruler, dragSpeedModifier: 0.8, maxZoom: 3.0, objectBorderThickness: 4, gridStyles: { solidLines: { label: "GRID.STYLES.SolidLines", shaderClass: GridShader, shaderOptions: { style: 0 } }, dashedLines: { label: "GRID.STYLES.DashedLines", shaderClass: GridShader, shaderOptions: { style: 1 } }, dottedLines: { label: "GRID.STYLES.DottedLines", shaderClass: GridShader, shaderOptions: { style: 2 } }, squarePoints: { label: "GRID.STYLES.SquarePoints", shaderClass: GridShader, shaderOptions: { style: 3 } }, diamondPoints: { label: "GRID.STYLES.DiamondPoints", shaderClass: GridShader, shaderOptions: { style: 4 } }, roundPoints: { label: "GRID.STYLES.RoundPoints", shaderClass: GridShader, shaderOptions: { style: 5 } } }, /** * A light source animation configuration object. * @typedef {Record} LightSourceAnimationConfig */ /** @type {LightSourceAnimationConfig} */ lightAnimations: { flame: { label: "LIGHT.AnimationFlame", animation: foundry.canvas.sources.PointLightSource.prototype.animateFlickering, illuminationShader: FlameIlluminationShader, colorationShader: FlameColorationShader }, torch: { label: "LIGHT.AnimationTorch", animation: foundry.canvas.sources.PointLightSource.prototype.animateTorch, illuminationShader: TorchIlluminationShader, colorationShader: TorchColorationShader }, revolving: { label: "LIGHT.AnimationRevolving", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: RevolvingColorationShader }, siren: { label: "LIGHT.AnimationSiren", animation: foundry.canvas.sources.PointLightSource.prototype.animateTorch, illuminationShader: SirenIlluminationShader, colorationShader: SirenColorationShader }, pulse: { label: "LIGHT.AnimationPulse", animation: foundry.canvas.sources.PointLightSource.prototype.animatePulse, illuminationShader: PulseIlluminationShader, colorationShader: PulseColorationShader }, chroma: { label: "LIGHT.AnimationChroma", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: ChromaColorationShader }, wave: { label: "LIGHT.AnimationWave", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: WaveIlluminationShader, colorationShader: WaveColorationShader }, fog: { label: "LIGHT.AnimationFog", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: FogColorationShader }, sunburst: { label: "LIGHT.AnimationSunburst", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: SunburstIlluminationShader, colorationShader: SunburstColorationShader }, dome: { label: "LIGHT.AnimationLightDome", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: LightDomeColorationShader }, emanation: { label: "LIGHT.AnimationEmanation", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: EmanationColorationShader }, hexa: { label: "LIGHT.AnimationHexaDome", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: HexaDomeColorationShader }, ghost: { label: "LIGHT.AnimationGhostLight", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: GhostLightIlluminationShader, colorationShader: GhostLightColorationShader }, energy: { label: "LIGHT.AnimationEnergyField", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: EnergyFieldColorationShader }, vortex: { label: "LIGHT.AnimationVortex", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: VortexIlluminationShader, colorationShader: VortexColorationShader }, witchwave: { label: "LIGHT.AnimationBewitchingWave", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: BewitchingWaveIlluminationShader, colorationShader: BewitchingWaveColorationShader }, rainbowswirl: { label: "LIGHT.AnimationSwirlingRainbow", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: SwirlingRainbowColorationShader }, radialrainbow: { label: "LIGHT.AnimationRadialRainbow", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: RadialRainbowColorationShader }, fairy: { label: "LIGHT.AnimationFairyLight", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: FairyLightIlluminationShader, colorationShader: FairyLightColorationShader }, grid: { label: "LIGHT.AnimationForceGrid", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: ForceGridColorationShader }, starlight: { label: "LIGHT.AnimationStarLight", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, colorationShader: StarLightColorationShader }, smokepatch: { label: "LIGHT.AnimationSmokePatch", animation: foundry.canvas.sources.PointLightSource.prototype.animateTime, illuminationShader: SmokePatchIlluminationShader, colorationShader: SmokePatchColorationShader } }, /** * A darkness source animation configuration object. * @typedef {Record} DarknessSourceAnimationConfig */ /** @type {DarknessSourceAnimationConfig} */ darknessAnimations: { magicalGloom: { label: "LIGHT.AnimationMagicalGloom", animation: foundry.canvas.sources.PointDarknessSource.prototype.animateTime, darknessShader: MagicalGloomDarknessShader }, roiling: { label: "LIGHT.AnimationRoilingMass", animation: foundry.canvas.sources.PointDarknessSource.prototype.animateTime, darknessShader: RoilingDarknessShader }, hole: { label: "LIGHT.AnimationBlackHole", animation: foundry.canvas.sources.PointDarknessSource.prototype.animateTime, darknessShader: BlackHoleDarknessShader } }, /** * A registry of Scenes which are managed by a specific SceneManager class. * @type {Record} */ managedScenes: {}, pings: { types: { PULSE: "pulse", ALERT: "alert", PULL: "chevron", ARROW: "arrow" }, styles: { alert: { class: AlertPing, color: "#ff0000", size: 1.5, duration: 900 }, arrow: { class: ArrowPing, size: 1, duration: 900 }, chevron: { class: ChevronPing, size: 1, duration: 2000 }, pulse: { class: PulsePing, size: 1.5, duration: 900 } }, pullSpeed: 700 }, targeting: { size: .15 }, /** * The hover-fading configuration. * @type {object} */ hoverFade: { /** * The delay in milliseconds before the (un)faded animation starts on (un)hover. * @type {number} */ delay: 250, /** * The duration in milliseconds of the (un)fade animation on (un)hover. * @type {number} */ duration: 750 }, /* -------------------------------------------- */ /* Transcoders */ /* -------------------------------------------- */ /** * Allow specific transcoders for assets * @type {Record} */ transcoders: { basis: false // set to true to activate basis support }, /* -------------------------------------------- */ /** * The set of VisionMode definitions which are available to be used for Token vision. * @type {Record} */ visionModes: { // Default (Basic) Vision basic: new VisionMode({ id: "basic", label: "VISION.ModeBasicVision", vision: { defaults: { attenuation: 0, contrast: 0, saturation: 0, brightness: 0 }, preferred: true // Takes priority over other vision modes } }), // Darkvision darkvision: new VisionMode({ id: "darkvision", label: "VISION.ModeDarkvision", canvas: { shader: ColorAdjustmentsSamplerShader, uniforms: { contrast: 0, saturation: -1.0, brightness: 0 } }, lighting: { levels: { [VisionMode.LIGHTING_LEVELS.DIM]: VisionMode.LIGHTING_LEVELS.BRIGHT }, background: { visibility: VisionMode.LIGHTING_VISIBILITY.REQUIRED } }, vision: { darkness: { adaptive: false }, defaults: { attenuation: 0, contrast: 0, saturation: -1.0, brightness: 0 } } }), // Darkvision monochromatic: new VisionMode({ id: "monochromatic", label: "VISION.ModeMonochromatic", canvas: { shader: ColorAdjustmentsSamplerShader, uniforms: { contrast: 0, saturation: -1.0, brightness: 0 } }, lighting: { background: { postProcessingModes: ["SATURATION"], uniforms: { saturation: -1.0, tint: [1, 1, 1] } }, illumination: { postProcessingModes: ["SATURATION"], uniforms: { saturation: -1.0, tint: [1, 1, 1] } }, coloration: { postProcessingModes: ["SATURATION"], uniforms: { saturation: -1.0, tint: [1, 1, 1] } } }, vision: { darkness: { adaptive: false }, defaults: { attenuation: 0, contrast: 0, saturation: -1, brightness: 0 } } }), // Blindness blindness: new VisionMode({ id: "blindness", label: "VISION.ModeBlindness", tokenConfig: false, canvas: { shader: ColorAdjustmentsSamplerShader, uniforms: { contrast: -0.75, saturation: -1, exposure: -0.3 } }, lighting: { background: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, illumination: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, coloration: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED } }, vision: { darkness: { adaptive: false }, defaults: { color: null, attenuation: 0, contrast: -0.5, saturation: -1, brightness: -1 } } }), // Tremorsense tremorsense: new VisionMode({ id: "tremorsense", label: "VISION.ModeTremorsense", canvas: { shader: ColorAdjustmentsSamplerShader, uniforms: { contrast: 0, saturation: -0.8, exposure: -0.65 } }, lighting: { background: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, illumination: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, coloration: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, darkness: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED } }, vision: { darkness: { adaptive: false }, defaults: { attenuation: 0, contrast: 0.2, saturation: -0.3, brightness: 1 }, background: { shader: WaveBackgroundVisionShader }, coloration: { shader: WaveColorationVisionShader } } }, {animated: true}), // Light Amplification lightAmplification: new VisionMode({ id: "lightAmplification", label: "VISION.ModeLightAmplification", canvas: { shader: AmplificationSamplerShader, uniforms: { saturation: -0.5, tint: [0.38, 0.8, 0.38] } }, lighting: { background: { visibility: VisionMode.LIGHTING_VISIBILITY.REQUIRED, postProcessingModes: ["SATURATION", "EXPOSURE"], uniforms: { saturation: -0.5, exposure: 1.5, tint: [0.38, 0.8, 0.38] } }, illumination: { postProcessingModes: ["SATURATION"], uniforms: { saturation: -0.5 } }, coloration: { postProcessingModes: ["SATURATION", "EXPOSURE"], uniforms: { saturation: -0.5, exposure: 1.5, tint: [0.38, 0.8, 0.38] } }, levels: { [VisionMode.LIGHTING_LEVELS.DIM]: VisionMode.LIGHTING_LEVELS.BRIGHT, [VisionMode.LIGHTING_LEVELS.BRIGHT]: VisionMode.LIGHTING_LEVELS.BRIGHTEST } }, vision: { darkness: { adaptive: false }, defaults: { attenuation: 0, contrast: 0, saturation: -0.5, brightness: 1 }, background: { shader: AmplificationBackgroundVisionShader } } }) }, /* -------------------------------------------- */ /** * The set of DetectionMode definitions which are available to be used for visibility detection. * @type {Record} */ detectionModes: { lightPerception: new DetectionModeLightPerception({ id: "lightPerception", label: "DETECTION.LightPerception", type: DetectionMode.DETECTION_TYPES.SIGHT }), basicSight: new DetectionModeBasicSight({ id: "basicSight", label: "DETECTION.BasicSight", type: DetectionMode.DETECTION_TYPES.SIGHT }), seeInvisibility: new DetectionModeInvisibility({ id: "seeInvisibility", label: "DETECTION.SeeInvisibility", type: DetectionMode.DETECTION_TYPES.SIGHT }), senseInvisibility: new DetectionModeInvisibility({ id: "senseInvisibility", label: "DETECTION.SenseInvisibility", walls: false, angle: false, type: DetectionMode.DETECTION_TYPES.OTHER }), feelTremor: new DetectionModeTremor({ id: "feelTremor", label: "DETECTION.FeelTremor", walls: false, angle: false, type: DetectionMode.DETECTION_TYPES.MOVE }), seeAll: new DetectionModeAll({ id: "seeAll", label: "DETECTION.SeeAll", type: DetectionMode.DETECTION_TYPES.SIGHT }), senseAll: new DetectionModeAll({ id: "senseAll", label: "DETECTION.SenseAll", walls: false, angle: false, type: DetectionMode.DETECTION_TYPES.OTHER }) } }, /* -------------------------------------------- */ /** * Configure the default Token text style so that it may be reused and overridden by modules * @type {PIXI.TextStyle} */ canvasTextStyle: new PIXI.TextStyle({ fontFamily: "Signika", fontSize: 36, fill: "#FFFFFF", stroke: "#111111", strokeThickness: 1, dropShadow: true, dropShadowColor: "#000000", dropShadowBlur: 2, dropShadowAngle: 0, dropShadowDistance: 0, align: "center", wordWrap: false, padding: 1 }), /** * Available Weather Effects implementations * @typedef {Object} WeatherAmbienceConfiguration * @param {string} id * @param {string} label * @param {{enabled: boolean, blendMode: PIXI.BLEND_MODES}} filter * @param {WeatherEffectConfiguration[]} effects * * @typedef {Object} WeatherEffectConfiguration * @param {string} id * @param {typeof ParticleEffect|WeatherShaderEffect} effectClass * @param {PIXI.BLEND_MODES} blendMode * @param {object} config */ weatherEffects: { leaves: { id: "leaves", label: "WEATHER.AutumnLeaves", effects: [{ id: "leavesParticles", effectClass: AutumnLeavesWeatherEffect }] }, rain: { id: "rain", label: "WEATHER.Rain", filter: { enabled: false }, effects: [{ id: "rainShader", effectClass: WeatherShaderEffect, shaderClass: RainShader, blendMode: PIXI.BLEND_MODES.SCREEN, config: { opacity: 0.25, tint: [0.7, 0.9, 1.0], intensity: 1, strength: 1, rotation: 0.2618, speed: 0.2, } }] }, rainStorm: { id: "rainStorm", label: "WEATHER.RainStorm", filter: { enabled: false }, effects: [{ id: "fogShader", effectClass: WeatherShaderEffect, shaderClass: FogShader, blendMode: PIXI.BLEND_MODES.SCREEN, performanceLevel: 2, config: { slope: 1.5, intensity: 0.050, speed: -55.0, scale: 25, } }, { id: "rainShader", effectClass: WeatherShaderEffect, shaderClass: RainShader, blendMode: PIXI.BLEND_MODES.SCREEN, config: { opacity: 0.45, tint: [0.7, 0.9, 1.0], intensity: 1.5, strength: 1.5, rotation: 0.5236, speed: 0.30, } }] }, fog: { id: "fog", label: "WEATHER.Fog", filter: { enabled: false }, effects: [{ id: "fogShader", effectClass: WeatherShaderEffect, shaderClass: FogShader, blendMode: PIXI.BLEND_MODES.SCREEN, config: { slope: 0.45, intensity: 0.4, speed: 0.4, } }] }, snow: { id: "snow", label: "WEATHER.Snow", filter: { enabled: false }, effects: [{ id: "snowShader", effectClass: WeatherShaderEffect, shaderClass: SnowShader, blendMode: PIXI.BLEND_MODES.SCREEN, config: { tint: [0.85, 0.95, 1], direction: 0.5, speed: 2, scale: 2.5, } }] }, blizzard: { id: "blizzard", label: "WEATHER.Blizzard", filter: { enabled: false }, effects: [{ id: "snowShader", effectClass: WeatherShaderEffect, shaderClass: SnowShader, blendMode: PIXI.BLEND_MODES.SCREEN, config: { tint: [0.95, 1, 1], direction: 0.80, speed: 8, scale: 2.5, } }, { id: "fogShader", effectClass: WeatherShaderEffect, shaderClass: FogShader, blendMode: PIXI.BLEND_MODES.SCREEN, performanceLevel: 2, config: { slope: 1.0, intensity: 0.15, speed: -4.0, } }] } }, /** * The control icons used for rendering common HUD operations * @type {object} */ controlIcons: { combat: "icons/svg/combat.svg", visibility: "icons/svg/cowled.svg", effects: "icons/svg/aura.svg", lock: "icons/svg/padlock.svg", up: "icons/svg/up.svg", down: "icons/svg/down.svg", defeated: "icons/svg/skull.svg", light: "icons/svg/light.svg", lightOff: "icons/svg/light-off.svg", template: "icons/svg/explosion.svg", sound: "icons/svg/sound.svg", soundOff: "icons/svg/sound-off.svg", doorClosed: "icons/svg/door-closed-outline.svg", doorOpen: "icons/svg/door-open-outline.svg", doorSecret: "icons/svg/door-secret-outline.svg", doorLocked: "icons/svg/door-locked-outline.svg", wallDirection: "icons/svg/wall-direction.svg" }, /** * @typedef {FontFaceDescriptors} FontDefinition * @property {string} urls An array of remote URLs the font files exist at. */ /** * @typedef {object} FontFamilyDefinition * @property {boolean} editor Whether the font is available in the rich text editor. This will also enable it * for notes and drawings. * @property {FontDefinition[]} fonts Individual font face definitions for this font family. If this is empty, the * font family may only be loaded from the client's OS-installed fonts. */ /** * A collection of fonts to load either from the user's local system, or remotely. * @type {Record} */ fontDefinitions: { Arial: {editor: true, fonts: []}, Amiri: { editor: true, fonts: [ {urls: ["fonts/amiri/amiri-regular.woff2"]}, {urls: ["fonts/amiri/amiri-bold.woff2"], weight: 700} ] }, "Bruno Ace": {editor: true, fonts: [ {urls: ["fonts/bruno-ace/bruno-ace.woff2"]} ]}, Courier: {editor: true, fonts: []}, "Courier New": {editor: true, fonts: []}, "Modesto Condensed": { editor: true, fonts: [ {urls: ["fonts/modesto-condensed/modesto-condensed.woff2"]}, {urls: ["fonts/modesto-condensed/modesto-condensed-bold.woff2"], weight: 700} ] }, Signika: { editor: true, fonts: [ {urls: ["fonts/signika/signika-regular.woff2"]}, {urls: ["fonts/signika/signika-bold.woff2"], weight: 700} ] }, Times: {editor: true, fonts: []}, "Times New Roman": {editor: true, fonts: []} }, /** * The default font family used for text labels on the PIXI Canvas * @type {string} */ defaultFontFamily: "Signika", /** * @typedef {object} _StatusEffectConfig Configured status effects which are recognized by the game system * @property {string} id A string identifier for the effect * @property {string} label Alias for ActiveEffectData#name (deprecated) * @property {string} icon Alias for ActiveEffectData#img (deprecated) * @property {boolean|{actorTypes?: string[]}} [hud=true] Should this effect be selectable in the Token HUD? * This effect is only selectable in the Token HUD if the Token's * Actor sub-type is one of the configured ones. */ /** * Configured status effects which are recognized by the game system. * @typedef {_StatusEffectConfig & Partial} StatusEffectConfig */ /** * The array of status effects which can be applied to an Actor. * @type {Array} */ statusEffects: [ { id: "dead", name: "EFFECT.StatusDead", img: "icons/svg/skull.svg" }, { id: "unconscious", name: "EFFECT.StatusUnconscious", img: "icons/svg/unconscious.svg" }, { id: "sleep", name: "EFFECT.StatusAsleep", img: "icons/svg/sleep.svg" }, { id: "stun", name: "EFFECT.StatusStunned", img: "icons/svg/daze.svg" }, { id: "prone", name: "EFFECT.StatusProne", img: "icons/svg/falling.svg" }, { id: "restrain", name: "EFFECT.StatusRestrained", img: "icons/svg/net.svg" }, { id: "paralysis", name: "EFFECT.StatusParalysis", img: "icons/svg/paralysis.svg" }, { id: "fly", name: "EFFECT.StatusFlying", img: "icons/svg/wing.svg" }, { id: "blind", name: "EFFECT.StatusBlind", img: "icons/svg/blind.svg" }, { id: "deaf", name: "EFFECT.StatusDeaf", img: "icons/svg/deaf.svg" }, { id: "silence", name: "EFFECT.StatusSilenced", img: "icons/svg/silenced.svg" }, { id: "fear", name: "EFFECT.StatusFear", img: "icons/svg/terror.svg" }, { id: "burning", name: "EFFECT.StatusBurning", img: "icons/svg/fire.svg" }, { id: "frozen", name: "EFFECT.StatusFrozen", img: "icons/svg/frozen.svg" }, { id: "shock", name: "EFFECT.StatusShocked", img: "icons/svg/lightning.svg" }, { id: "corrode", name: "EFFECT.StatusCorrode", img: "icons/svg/acid.svg" }, { id: "bleeding", name: "EFFECT.StatusBleeding", img: "icons/svg/blood.svg" }, { id: "disease", name: "EFFECT.StatusDisease", img: "icons/svg/biohazard.svg" }, { id: "poison", name: "EFFECT.StatusPoison", img: "icons/svg/poison.svg" }, { id: "curse", name: "EFFECT.StatusCursed", img: "icons/svg/sun.svg" }, { id: "regen", name: "EFFECT.StatusRegen", img: "icons/svg/regen.svg" }, { id: "degen", name: "EFFECT.StatusDegen", img: "icons/svg/degen.svg" }, { id: "hover", name: "EFFECT.StatusHover", img: "icons/svg/wingfoot.svg" }, { id: "burrow", name: "EFFECT.StatusBurrow", img: "icons/svg/mole.svg" }, { id: "upgrade", name: "EFFECT.StatusUpgrade", img: "icons/svg/upgrade.svg" }, { id: "downgrade", name: "EFFECT.StatusDowngrade", img: "icons/svg/downgrade.svg" }, { id: "invisible", name: "EFFECT.StatusInvisible", img: "icons/svg/invisible.svg" }, { id: "target", name: "EFFECT.StatusTarget", img: "icons/svg/target.svg" }, { id: "eye", name: "EFFECT.StatusMarked", img: "icons/svg/eye.svg" }, { id: "bless", name: "EFFECT.StatusBlessed", img: "icons/svg/angel.svg" }, { id: "fireShield", name: "EFFECT.StatusFireShield", img: "icons/svg/fire-shield.svg" }, { id: "coldShield", name: "EFFECT.StatusIceShield", img: "icons/svg/ice-shield.svg" }, { id: "magicShield", name: "EFFECT.StatusMagicShield", img: "icons/svg/mage-shield.svg" }, { id: "holyShield", name: "EFFECT.StatusHolyShield", img: "icons/svg/holy-shield.svg" } ].map(status => { /** @deprecated since v12 */ for ( const [oldKey, newKey] of Object.entries({label: "name", icon: "img"}) ) { const msg = `StatusEffectConfig#${oldKey} has been deprecated in favor of StatusEffectConfig#${newKey}`; Object.defineProperty(status, oldKey, { get() { foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true}); return this[newKey]; }, set(value) { foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true}); this[newKey] = value; }, enumerable: false, configurable: true }); } return status; }), /** * A mapping of status effect IDs which provide some additional mechanical integration. * @enum {string} */ specialStatusEffects: { DEFEATED: "dead", INVISIBLE: "invisible", BLIND: "blind", BURROW: "burrow", HOVER: "hover", FLY: "fly" }, /** * A mapping of core audio effects used which can be replaced by systems or mods * @type {object} */ sounds: { dice: "sounds/dice.wav", lock: "sounds/lock.wav", notification: "sounds/notify.wav", combat: "sounds/drums.wav" }, /** * Define the set of supported languages for localization * @type {{string, string}} */ supportedLanguages: { en: "English" }, /** * Localization constants. * @type {object} */ i18n: { /** * In operations involving the document index, search prefixes must have at least this many characters to avoid too * large a search space. Languages that have hundreds or thousands of characters will typically have very shallow * search trees, so it should be safe to lower this number in those cases. */ searchMinimumCharacterLength: 4 }, /** * Configuration for time tracking * @type {{turnTime: number}} */ time: { turnTime: 0, roundTime: 0 }, /* -------------------------------------------- */ /* Embedded Documents */ /* -------------------------------------------- */ /** * Configuration for the ActiveEffect embedded document type */ ActiveEffect: { documentClass: ActiveEffect, dataModels: {}, typeLabels: {}, typeIcons: {}, /** * If true, Active Effects on Items will be copied to the Actor when the Item is created on the Actor if the * Active Effect's transfer property is true, and will be deleted when that Item is deleted from the Actor. * If false, Active Effects are never copied to the Actor, but will still apply to the Actor from within the Item * if the transfer property on the Active Effect is true. * @deprecated since v11 */ legacyTransferral: true }, /** * Configuration for the ActorDelta embedded document type. */ ActorDelta: { documentClass: ActorDelta }, /** * Configuration for the Card embedded Document type */ Card: { documentClass: Card, dataModels: {}, typeLabels: {}, typeIcons: {} }, /** * Configuration for the TableResult embedded document type */ TableResult: { documentClass: TableResult }, /** * Configuration for the JournalEntryPage embedded document type. */ JournalEntryPage: { documentClass: JournalEntryPage, dataModels: {}, typeLabels: {}, typeIcons: { image: "fas fa-file-image", pdf: "fas fa-file-pdf", text: "fas fa-file-lines", video: "fas fa-file-video" }, defaultType: "text", sidebarIcon: "fas fa-book-open" }, /** * Configuration for the PlaylistSound embedded document type */ PlaylistSound: { documentClass: PlaylistSound, sidebarIcon: "fas fa-music" }, /** * Configuration for the AmbientLight embedded document type and its representation on the game Canvas * @enum {Function} */ AmbientLight: { documentClass: AmbientLightDocument, objectClass: AmbientLight, layerClass: LightingLayer }, /** * Configuration for the AmbientSound embedded document type and its representation on the game Canvas * @enum {Function} */ AmbientSound: { documentClass: AmbientSoundDocument, objectClass: AmbientSound, layerClass: SoundsLayer }, /** * Configuration for the Combatant embedded document type within a Combat document * @enum {Function} */ Combatant: { documentClass: Combatant, dataModels: {}, typeLabels: {}, typeIcons: {} }, /** * Configuration for the Drawing embedded document type and its representation on the game Canvas * @type {{ * documentClass: typeof DrawingDocument, * objectClass: typeof Drawing, * layerClass: typeof DrawingsLayer, * hudClass: typeof DrawingHUD * }} */ Drawing: { documentClass: DrawingDocument, objectClass: Drawing, layerClass: DrawingsLayer, hudClass: DrawingHUD }, /** * Configuration for the MeasuredTemplate embedded document type and its representation on the game Canvas * @enum {Function} */ MeasuredTemplate: { defaults: { angle: 53.13, width: 1 }, types: { circle: "Circle", cone: "Cone", rect: "Rectangle", ray: "Ray" }, documentClass: MeasuredTemplateDocument, objectClass: MeasuredTemplate, layerClass: TemplateLayer }, /** * Configuration for the Note embedded document type and its representation on the game Canvas * @enum {Function} */ Note: { documentClass: NoteDocument, objectClass: Note, layerClass: NotesLayer }, /** * Configuration for the Region embedded document type and its representation on the game Canvas */ Region: { documentClass: RegionDocument, objectClass: Region, layerClass: RegionLayer }, /** * Configuration for the RegionBehavior embedded document type */ RegionBehavior: { documentClass: RegionBehavior, dataModels: { adjustDarknessLevel: foundry.data.regionBehaviors.AdjustDarknessLevelRegionBehaviorType, displayScrollingText: foundry.data.regionBehaviors.DisplayScrollingTextRegionBehaviorType, executeMacro: foundry.data.regionBehaviors.ExecuteMacroRegionBehaviorType, executeScript: foundry.data.regionBehaviors.ExecuteScriptRegionBehaviorType, pauseGame: foundry.data.regionBehaviors.PauseGameRegionBehaviorType, suppressWeather: foundry.data.regionBehaviors.SuppressWeatherRegionBehaviorType, teleportToken: foundry.data.regionBehaviors.TeleportTokenRegionBehaviorType, toggleBehavior: foundry.data.regionBehaviors.ToggleBehaviorRegionBehaviorType }, typeLabels: {}, typeIcons: { adjustDarknessLevel: "fa-solid fa-circle-half-stroke", displayScrollingText: "fa-solid fa-message-arrow-up", executeMacro: "fa-solid fa-code", executeScript: "fa-brands fa-js", pauseGame: "fa-solid fa-pause", suppressWeather: "fa-solid fa-cloud-slash", teleportToken: "fa-solid fa-transporter-1", toggleBehavior: "fa-solid fa-sliders" } }, /** * Configuration for the Tile embedded document type and its representation on the game Canvas * @type {{ * documentClass: typeof TileDocument, * objectClass: typeof Tile, * layerClass: typeof TilesLayer, * hudClass: typeof TileHUD * }} */ Tile: { documentClass: TileDocument, objectClass: Tile, layerClass: TilesLayer, hudClass: TileHUD }, /** * Configuration for the Token embedded document type and its representation on the game Canvas * @type {{ * documentClass: typeof TokenDocument, * objectClass: typeof Token, * layerClass: typeof TokenLayer, * prototypeSheetClass: typeof TokenConfig, * hudClass: typeof TokenHUD, * adjectivesPrefix: string, * ring: foundry.canvas.tokens.TokenRingConfig * }} */ Token: { documentClass: TokenDocument, objectClass: Token, layerClass: TokenLayer, prototypeSheetClass: TokenConfig, hudClass: TokenHUD, adjectivesPrefix: "TOKEN.Adjectives" // ring property is initialized in foundry.canvas.tokens.TokenRingConfig.initialize }, /** * @typedef {Object} WallDoorSound * @property {string} label A localization string label * @property {string} close A sound path when the door is closed * @property {string} lock A sound path when the door becomes locked * @property {string} open A sound path when opening the door * @property {string} test A sound path when attempting to open a locked door * @property {string} unlock A sound path when the door becomes unlocked */ /** * Configuration for the Wall embedded document type and its representation on the game Canvas * @property {typeof ClientDocument} documentClass * @property {typeof PlaceableObject} objectClass * @property {typeof CanvasLayer} layerClass * @property {number} thresholdAttenuationMultiplier * @property {WallDoorSound[]} doorSounds */ Wall: { documentClass: WallDocument, objectClass: Wall, layerClass: WallsLayer, thresholdAttenuationMultiplier: 1, doorSounds: { futuristicFast: { label: "WALLS.DoorSound.FuturisticFast", close: "sounds/doors/futuristic/close-fast.ogg", lock: "sounds/doors/futuristic/lock.ogg", open: "sounds/doors/futuristic/open-fast.ogg", test: "sounds/doors/futuristic/test.ogg", unlock: "sounds/doors/futuristic/unlock.ogg" }, futuristicHydraulic: { label: "WALLS.DoorSound.FuturisticHydraulic", close: "sounds/doors/futuristic/close-hydraulic.ogg", lock: "sounds/doors/futuristic/lock.ogg", open: "sounds/doors/futuristic/open-hydraulic.ogg", test: "sounds/doors/futuristic/test.ogg", unlock: "sounds/doors/futuristic/unlock.ogg" }, futuristicForcefield: { label: "WALLS.DoorSound.FuturisticForcefield", close: "sounds/doors/futuristic/close-forcefield.ogg", lock: "sounds/doors/futuristic/lock.ogg", open: "sounds/doors/futuristic/open-forcefield.ogg", test: "sounds/doors/futuristic/test-forcefield.ogg", unlock: "sounds/doors/futuristic/unlock.ogg" }, industrial: { label: "WALLS.DoorSound.Industrial", close: "sounds/doors/industrial/close.ogg", lock: "sounds/doors/industrial/lock.ogg", open: "sounds/doors/industrial/open.ogg", test: "sounds/doors/industrial/test.ogg", unlock: "sounds/doors/industrial/unlock.ogg" }, industrialCreaky: { label: "WALLS.DoorSound.IndustrialCreaky", close: "sounds/doors/industrial/close-creaky.ogg", lock: "sounds/doors/industrial/lock.ogg", open: "sounds/doors/industrial/open-creaky.ogg", test: "sounds/doors/industrial/test.ogg", unlock: "sounds/doors/industrial/unlock.ogg" }, jail: { label: "WALLS.DoorSound.Jail", close: "sounds/doors/jail/close.ogg", lock: "sounds/doors/jail/lock.ogg", open: "sounds/doors/jail/open.ogg", test: "sounds/doors/jail/test.ogg", unlock: "sounds/doors/jail/unlock.ogg" }, magicDoor: { label: "WALLS.DoorSound.MagicDoor", close: "sounds/doors/magic/door-close.ogg", lock: "sounds/doors/magic/lock.ogg", open: "sounds/doors/magic/door-open.ogg", test: "sounds/doors/magic/test.ogg", unlock: "sounds/doors/magic/unlock.ogg" }, magicWall: { label: "WALLS.DoorSound.MagicWall", close: "sounds/doors/magic/wall-close.ogg", lock: "sounds/doors/magic/lock.ogg", open: "sounds/doors/magic/wall-open.ogg", test: "sounds/doors/magic/test.ogg", unlock: "sounds/doors/magic/unlock.ogg" }, metal: { label: "WALLS.DoorSound.Metal", close: "sounds/doors/metal/close.ogg", lock: "sounds/doors/metal/lock.ogg", open: "sounds/doors/metal/open.ogg", test: "sounds/doors/metal/test.ogg", unlock: "sounds/doors/metal/unlock.ogg" }, slidingMetal: { label: "WALLS.DoorSound.SlidingMetal", close: "sounds/doors/shutter/close.ogg", lock: "sounds/doors/shutter/lock.ogg", open: "sounds/doors/shutter/open.ogg", test: "sounds/doors/shutter/test.ogg", unlock: "sounds/doors/shutter/unlock.ogg" }, slidingModern: { label: "WALLS.DoorSound.SlidingModern", close: "sounds/doors/sliding/close.ogg", lock: "sounds/doors/sliding/lock.ogg", open: "sounds/doors/sliding/open.ogg", test: "sounds/doors/sliding/test.ogg", unlock: "sounds/doors/sliding/unlock.ogg" }, slidingWood: { label: "WALLS.DoorSound.SlidingWood", close: "sounds/doors/sliding/close-wood.ogg", lock: "sounds/doors/sliding/lock.ogg", open: "sounds/doors/sliding/open-wood.ogg", test: "sounds/doors/sliding/test.ogg", unlock: "sounds/doors/sliding/unlock.ogg" }, stoneBasic: { label: "WALLS.DoorSound.StoneBasic", close: "sounds/doors/stone/close.ogg", lock: "sounds/doors/stone/lock.ogg", open: "sounds/doors/stone/open.ogg", test: "sounds/doors/stone/test.ogg", unlock: "sounds/doors/stone/unlock.ogg" }, stoneRocky: { label: "WALLS.DoorSound.StoneRocky", close: "sounds/doors/stone/close-rocky.ogg", lock: "sounds/doors/stone/lock.ogg", open: "sounds/doors/stone/open-rocky.ogg", test: "sounds/doors/stone/test.ogg", unlock: "sounds/doors/stone/unlock.ogg" }, stoneSandy: { label: "WALLS.DoorSound.StoneSandy", close: "sounds/doors/stone/close-sandy.ogg", lock: "sounds/doors/stone/lock.ogg", open: "sounds/doors/stone/open-sandy.ogg", test: "sounds/doors/stone/test.ogg", unlock: "sounds/doors/stone/unlock.ogg" }, woodBasic: { label: "WALLS.DoorSound.WoodBasic", close: "sounds/doors/wood/close.ogg", lock: "sounds/doors/wood/lock.ogg", open: "sounds/doors/wood/open.ogg", test: "sounds/doors/wood/test.ogg", unlock: "sounds/doors/wood/unlock.ogg" }, woodCreaky: { label: "WALLS.DoorSound.WoodCreaky", close: "sounds/doors/wood/close-creaky.ogg", lock: "sounds/doors/wood/lock.ogg", open: "sounds/doors/wood/open-creaky.ogg", test: "sounds/doors/wood/test.ogg", unlock: "sounds/doors/wood/unlock.ogg" }, woodHeavy: { label: "WALLS.DoorSound.WoodHeavy", close: "sounds/doors/wood/close-heavy.ogg", lock: "sounds/doors/wood/lock.ogg", open: "sounds/doors/wood/open-heavy.ogg", test: "sounds/doors/wood/test.ogg", unlock: "sounds/doors/wood/unlock.ogg" } } }, /** * An enumeration of sound effects which can be applied to Sound instances. * @enum {{label: string, effectClass: AudioNode}} */ soundEffects: { lowpass: { label: "SOUND.EFFECTS.LOWPASS", effectClass: foundry.audio.BiquadFilterEffect }, highpass: { label: "SOUND.EFFECTS.HIGHPASS", effectClass: foundry.audio.BiquadFilterEffect }, reverb: { label: "SOUND.EFFECTS.REVERB", effectClass: foundry.audio.ConvolverEffect } }, /* -------------------------------------------- */ /* Integrations */ /* -------------------------------------------- */ /** * Default configuration options for TinyMCE editors * @type {object} */ TinyMCE: { branding: false, menubar: false, statusbar: false, content_css: ["/css/mce.css"], plugins: "lists image table code save link", toolbar: "styles bullist numlist image table hr link removeformat code save", save_enablewhendirty: true, table_default_styles: {}, style_formats: [ { title: "Custom", items: [ { title: "Secret", block: "section", classes: "secret", wrapper: true } ] } ], style_formats_merge: true }, /** * @callback TextEditorEnricher * @param {RegExpMatchArray} match The regular expression match result * @param {EnrichmentOptions} [options] Options provided to customize text enrichment * @returns {Promise} An HTML element to insert in place of the matched text or null to * indicate that no replacement should be made. */ /** * @typedef {object} TextEditorEnricherConfig * @property {RegExp} pattern The string pattern to match. Must be flagged as global. * @property {boolean} [replaceParent] Hoist the replacement element out of its containing element if it replaces * the entire contents of the element. * @property {TextEditorEnricher} enricher The function that will be called on each match. It is expected that this * returns an HTML element to be inserted into the final enriched content. */ /** * Rich text editing configuration. * @type {object} */ TextEditor: { /** * A collection of custom enrichers that can be applied to text content, allowing for the matching and handling of * custom patterns. * @type {TextEditorEnricherConfig[]} */ enrichers: [] }, /** * Configuration for the WebRTC implementation class * @type {object} */ WebRTC: { clientClass: SimplePeerAVClient, detectPeerVolumeInterval: 50, detectSelfVolumeInterval: 20, emitVolumeInterval: 25, speakingThresholdEvents: 2, speakingHistoryLength: 10, connectedUserPollIntervalS: 8 }, /* -------------------------------------------- */ /* Interface */ /* -------------------------------------------- */ /** * Configure the Application classes used to render various core UI elements in the application. * The order of this object is relevant, as certain classes need to be constructed and referenced before others. * @type {Record} */ ui: { menu: MainMenu, sidebar: Sidebar, pause: Pause, nav: SceneNavigation, notifications: Notifications, actors: ActorDirectory, cards: CardsDirectory, chat: ChatLog, combat: CombatTracker, compendium: CompendiumDirectory, controls: SceneControls, hotbar: Hotbar, items: ItemDirectory, journal: JournalDirectory, macros: MacroDirectory, players: PlayerList, playlists: PlaylistDirectory, scenes: SceneDirectory, settings: Settings, tables: RollTableDirectory, webrtc: CameraViews } }; // Define the ring property with a TokenRingConfig instance Object.defineProperty(CONFIG.Token, 'ring', { value: new foundry.canvas.tokens.TokenRingConfig(), enumerable: true }); /** * @deprecated since v11 */ ["Actor", "Item", "JournalEntryPage", "Cards", "Card"].forEach(doc => { const warning = `You are accessing CONFIG.${doc}.systemDataModels which is deprecated. ` + `Please use CONFIG.${doc}.dataModels instead.`; Object.defineProperty(CONFIG[doc], "systemDataModels", { enumerable: false, get() { foundry.utils.logCompatibilityWarning(warning, {since: 11, until: 13}); return CONFIG[doc].dataModels; }, set(models) { foundry.utils.logCompatibilityWarning(warning, {since: 11, until: 13}); CONFIG[doc].dataModels = models; } }); }); /** * @deprecated since v11 */ Object.defineProperty(CONFIG.Canvas, "losBackend", { get() { const warning = "You are accessing CONFIG.Canvas.losbackend, which is deprecated." + " Use CONFIG.Canvas.polygonBackends.sight instead."; foundry.utils.logCompatibilityWarning(warning, {since: 11, until: 13}); return CONFIG.Canvas.polygonBackends.sight; }, set(cls) { const warning = "You are setting CONFIG.Canvas.losbackend, which is deprecated." + " Use CONFIG.Canvas.polygonBackends[type] instead."; foundry.utils.logCompatibilityWarning(warning, {since: 11, until: 13}); for ( const k of Object.keys(CONFIG.Canvas.polygonBackends) ) CONFIG.Canvas.polygonBackends[k] = cls; } });