├── .vscode ├── settings.json ├── launch.json └── i18n-ally-custom-framework.yml ├── img ├── readme │ ├── carousel.png │ ├── combat-controls.png │ ├── combatant-card.png │ ├── encounter-info.png │ ├── carousel-no-markup.png │ ├── multi-state-icon.png │ ├── combatant-card-hovered.png │ ├── property-overlay-config.png │ └── collapsed-empty-carousel.png ├── deathsave-newlogo.png ├── Discord-Logo-Color.png ├── combat-carousel-fulllogo.png ├── combat-carousel-textlogo.png ├── combat-carousel-textlogo-white.png └── Digital-Patreon-Logo_FieryCoral.png ├── icons ├── combat-carousel.gif ├── combat-carousel.webm ├── combat-carousel-solid2.png ├── combat-carousel-solid2-2.png ├── combat-carousel-solid2-3.png ├── cowled.svg ├── bookmark.svg ├── spiked-dragon-head.svg ├── wyvern.svg ├── goblin-head.svg ├── orc-head.svg ├── empty-carousel-solid2.svg ├── empty-carousel-solid.svg ├── empty-carousel.svg ├── carousel.svg ├── combat-carousel-solid2-2.svg ├── combat-carousel-solid2-3.svg ├── combat-carousel-solid2.svg └── combat-carousel-solid.svg ├── jsconfig.json ├── init.js ├── jsdoc └── conf.json ├── .github ├── zip-exclude.lst ├── auto-assign-issues.yml ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── enhancement-request.md │ └── bug_report.md └── workflows │ └── main.yml ├── templates ├── combat-carousel-button.hbs ├── about.hbs ├── config-form.hbs ├── combat-carousel.hbs └── combatant-card.hbs ├── modules ├── templates.mjs ├── about.mjs ├── overrides.mjs ├── util.mjs ├── config-form.mjs ├── fixed-draggable.mjs ├── config.mjs └── settings.mjs ├── package.json ├── README.md ├── lang ├── pt-br.json ├── fr.json ├── ja.json ├── en.json └── es.json ├── .gitignore ├── patrons.json ├── module.json ├── gulpfile.js ├── libs └── splide │ ├── css │ └── splide.min.css │ └── js │ └── splide-render.min.js └── CHANGELOG.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "i18n-ally.localesPaths": [ 3 | "lang" 4 | ] 5 | } -------------------------------------------------------------------------------- /img/readme/carousel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/carousel.png -------------------------------------------------------------------------------- /icons/combat-carousel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/icons/combat-carousel.gif -------------------------------------------------------------------------------- /img/deathsave-newlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/deathsave-newlogo.png -------------------------------------------------------------------------------- /icons/combat-carousel.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/icons/combat-carousel.webm -------------------------------------------------------------------------------- /img/Discord-Logo-Color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/Discord-Logo-Color.png -------------------------------------------------------------------------------- /img/readme/combat-controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/combat-controls.png -------------------------------------------------------------------------------- /img/readme/combatant-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/combatant-card.png -------------------------------------------------------------------------------- /img/readme/encounter-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/encounter-info.png -------------------------------------------------------------------------------- /icons/combat-carousel-solid2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/icons/combat-carousel-solid2.png -------------------------------------------------------------------------------- /img/combat-carousel-fulllogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/combat-carousel-fulllogo.png -------------------------------------------------------------------------------- /img/combat-carousel-textlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/combat-carousel-textlogo.png -------------------------------------------------------------------------------- /img/readme/carousel-no-markup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/carousel-no-markup.png -------------------------------------------------------------------------------- /img/readme/multi-state-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/multi-state-icon.png -------------------------------------------------------------------------------- /icons/combat-carousel-solid2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/icons/combat-carousel-solid2-2.png -------------------------------------------------------------------------------- /icons/combat-carousel-solid2-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/icons/combat-carousel-solid2-3.png -------------------------------------------------------------------------------- /img/combat-carousel-textlogo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/combat-carousel-textlogo-white.png -------------------------------------------------------------------------------- /img/readme/combatant-card-hovered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/combatant-card-hovered.png -------------------------------------------------------------------------------- /img/readme/property-overlay-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/property-overlay-config.png -------------------------------------------------------------------------------- /img/Digital-Patreon-Logo_FieryCoral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/Digital-Patreon-Logo_FieryCoral.png -------------------------------------------------------------------------------- /img/readme/collapsed-empty-carousel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/death-save/combat-carousel/HEAD/img/readme/collapsed-empty-carousel.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["@league-of-foundry-developers/foundry-vtt-types/index-lenient"] 4 | } 5 | } -------------------------------------------------------------------------------- /init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module init 3 | */ 4 | import registerHooks from "./modules/hooks.mjs"; 5 | 6 | /** 7 | * Register all hooks from the hooks module 8 | */ 9 | registerHooks(); -------------------------------------------------------------------------------- /jsdoc/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "includePattern": ".+\\.(m|c)?js(doc|x)?$" 4 | }, 5 | "plugins": [ 6 | "@pixi/jsdoc-template/plugins/es6-fix" 7 | ] 8 | } -------------------------------------------------------------------------------- /.github/zip-exclude.lst: -------------------------------------------------------------------------------- 1 | ./dist/* 2 | ./out/* 3 | ./jsdoc/* 4 | ./node_modules/* 5 | *.git* 6 | *.vscode* 7 | gulpfile.js 8 | package.json 9 | package-lock.json 10 | tsconfig.json 11 | foundry.js -------------------------------------------------------------------------------- /templates/combat-carousel-button.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 |
    4 | 5 |
    6 |
  • 7 | -------------------------------------------------------------------------------- /.github/auto-assign-issues.yml: -------------------------------------------------------------------------------- 1 | # If enabled, auto-assigns users when a new issue is created 2 | # Defaults to true, allows you to install the app globally, and disable on a per-repo basis 3 | addAssignees: true 4 | 5 | # The list of users to assign to new issues. 6 | # If empty or not provided, the repository owner is assigned 7 | assignees: 8 | - eclarke12 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: #eclarke12 4 | patreon: #deathsave 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: errational 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://paypal.me/evanc/'] 13 | -------------------------------------------------------------------------------- /modules/templates.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Templates module 3 | * @module templates 4 | */ 5 | 6 | /** 7 | * Define a set of template paths to pre-load 8 | * Pre-loaded templates are compiled and cached for fast access when rendering 9 | * Taken from dnd5e system (see link) 10 | * @link https://gitlab.com/foundrynet/dnd5e/-/blob/master/module/templates.js 11 | * @return {Promise} 12 | */ 13 | export const preloadHandlebarsTemplates = async function() { 14 | 15 | // Define template paths to load 16 | const templatePaths = [ 17 | "modules/combat-carousel/templates/combatant-card.hbs" 18 | ]; 19 | 20 | // Load the template parts 21 | return loadTemplates(templatePaths); 22 | }; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: eclarke12 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement request 3 | about: Suggest an idea/enhancement for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:49999", 12 | "webRoot": "http://localhost:49999", 13 | "pathMapping": { 14 | "/modules/combat-carousel": "${workspaceFolder}" 15 | }, 16 | "skipFiles": [ 17 | "${workspaceFolder}/libs/**", 18 | "/**" 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug, unverified 6 | assignees: eclarke12 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment Info (please complete the following information):** 27 | - Foundry Version: [e.g. 0.8.5] 28 | - Game System / Version: [e.g. dnd5e 1.0.0] 29 | - CC Version [e.g. 0.3.0] 30 | - Browser [e.g. chrome, safari] 31 | - OS [e.g. Windows 10] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /icons/cowled.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/bookmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "combat-carousel", 3 | "version": "0.3.3", 4 | "description": "A Foundry VTT module providing CRPG-style combat tracker", 5 | "main": "init.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/death-save/combat-carousel.git" 12 | }, 13 | "keywords": [ 14 | "fvtt", 15 | "foundryvtt", 16 | "death-save", 17 | "combat-carousel" 18 | ], 19 | "author": "Evan Clarke (errational)", 20 | "license": "SEE LICENSE IN LICENSE", 21 | "bugs": { 22 | "url": "https://github.com/death-save/combat-carousel-public/issues" 23 | }, 24 | "homepage": "https://github.com/death-save/combat-carousel-public#readme", 25 | "devDependencies": { 26 | "@league-of-foundry-developers/foundry-vtt-types": "^9.280.0", 27 | "@pixi/jsdoc-template": "^2.6.0", 28 | "gulp": "^4.0.2", 29 | "gulp-zip": "^5.0.2", 30 | "jquery": "^3.6.0", 31 | "jsdoc-to-markdown": "^6.0.1", 32 | "node-fetch": "^2.6.1", 33 | "patreon": "^0.4.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/i18n-ally-custom-framework.yml: -------------------------------------------------------------------------------- 1 | # .vscode/i18n-ally-custom-framework.yml 2 | 3 | # An array of strings which contain Language Ids defined by VS Code 4 | # You can check avaliable language ids here: https://code.visualstudio.com/docs/languages/overview#_language-id 5 | languageIds: 6 | - javascript 7 | - typescript 8 | - javascriptreact 9 | - typescriptreact 10 | - handlebars 11 | - html 12 | 13 | # An array of RegExes to find the key usage. **The key should be captured in the first match group**. 14 | # You should unescape RegEx strings in order to fit in the YAML file 15 | # To help with this, you can use https://www.freeformatter.com/json-escape.html 16 | usageMatchRegex: 17 | - "[^\\w\\d]game\\.i18n\\.localize\\(['\"`]({key})['\"`]\\)" 18 | - "[^\\w\\d]game\\.i18n\\.format\\(['\"`]({key})['\"`]" 19 | - "\\{\\{~*\\s*localize\\s+[\"']({key})['\"]\\s*~*\\}\\}" 20 | - "\\{\\{~*\\s*localize\\s+[\"']{key}['\"]\\s*~*\\}\\}" 21 | - "\\{\\{[\\w\\.\\s\\=]*\\(localize\\s+[\"']({key})['\"]\\)[\\w\\.\\s\\=]*\\}\\}" 22 | 23 | # If set to true, only enables this custom framework (will disable all built-in frameworks) 24 | monopoly: true -------------------------------------------------------------------------------- /modules/about.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * AboutApp module 3 | * @module about 4 | */ 5 | 6 | /** 7 | * About this module FormApp 8 | * @extends FormApplication 9 | */ 10 | export default class AboutApp extends FormApplication { 11 | constructor(options={}) { 12 | super(options); 13 | } 14 | 15 | /** 16 | * Call app default options 17 | * @override 18 | */ 19 | static get defaultOptions() { 20 | return mergeObject(super.defaultOptions, { 21 | id: "combat-carousel-about", 22 | title: "About Combat Carousel", 23 | template: "modules/combat-carousel/templates/about.hbs", 24 | popOut: true, 25 | width: 500, 26 | height: 605 27 | }); 28 | } 29 | 30 | /** 31 | * Supplies data to the template 32 | */ 33 | async getData() { 34 | return { 35 | version: game.modules.get("combat-carousel").version, 36 | patrons: await this.fetchPatrons() 37 | } 38 | } 39 | 40 | /** 41 | * Fetches a list of Patrons to display on the About page 42 | */ 43 | async fetchPatrons() { 44 | const jsonPath = "modules/combat-carousel/patrons.json"; 45 | const response = await fetch(jsonPath); 46 | if (!response.ok) return null; 47 | 48 | const json = await response.json(); 49 | return json; 50 | } 51 | } -------------------------------------------------------------------------------- /modules/overrides.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Overrides module 3 | * @module overrides 4 | */ 5 | 6 | /** 7 | * Wrapper to call overrides 8 | */ 9 | export default function overrideMethods() { 10 | SceneNavigation.prototype.expand = sceneNavExpandOverride; 11 | SceneNavigation.prototype.collapse = sceneNavCollapseOverride; 12 | } 13 | 14 | /** 15 | * Override sceneNavExpand to move Hook call into promise 16 | */ 17 | function sceneNavExpandOverride() { 18 | if ( !this._collapsed ) return true; 19 | const nav = this.element; 20 | const icon = nav.find("#nav-toggle i.fas"); 21 | const ul = nav.children("#scene-list"); 22 | return new Promise(resolve => { 23 | ul.slideDown(200, () => { 24 | nav.removeClass("collapsed"); 25 | icon.removeClass("fa-caret-down").addClass("fa-caret-up"); 26 | this._collapsed = false; 27 | Hooks.callAll("collapseSceneNavigation", this, this._collapsed); 28 | resolve(true); 29 | }); 30 | 31 | }); 32 | } 33 | 34 | /** 35 | * Override sceneNavCollapse to move Hook call into promise 36 | */ 37 | function sceneNavCollapseOverride() { 38 | if ( this._collapsed ) return true; 39 | const nav = this.element; 40 | const icon = nav.find("#nav-toggle i.fas"); 41 | const ul = nav.children("#scene-list"); 42 | return new Promise(resolve => { 43 | ul.slideUp(200, () => { 44 | nav.addClass("collapsed"); 45 | icon.removeClass("fa-caret-up").addClass("fa-caret-down"); 46 | this._collapsed = true; 47 | Hooks.callAll("collapseSceneNavigation", this, this._collapsed); 48 | resolve(true); 49 | }); 50 | 51 | }); 52 | } -------------------------------------------------------------------------------- /modules/util.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Util module 3 | * @module util 4 | */ 5 | 6 | /** 7 | * Retrieves a key using the given value 8 | * @param {Object} object -- the object that contains the key/value 9 | * @param {*} value 10 | */ 11 | export function getKeyByValue(object, value) { 12 | return Object.keys(object).find(key => object[key] === value); 13 | } 14 | 15 | /** 16 | * Helper to get Token instance based on Combatant Id 17 | * @param {*} combatantId 18 | */ 19 | export function getTokenFromCombatantId(combatantId) { 20 | const combatant = game?.combat?.combatants.find(c => c.id === combatantId); 21 | if (!combatant) return; 22 | 23 | const token = combatant.token ?? null; 24 | 25 | return token; 26 | } 27 | 28 | /** 29 | * Helper to get Actor instance based on Combatant Id 30 | * @param {*} combatantId 31 | */ 32 | export function getActorFromCombatantId(combatantId) { 33 | const combatant = game?.combat?.combatants.find(c => c.id === combatantId); 34 | if (!combatant) return; 35 | 36 | const actor = combatant.actor ?? null; 37 | 38 | return actor; 39 | } 40 | 41 | /** 42 | * Gets all the siblings of a given element 43 | * Adapted from: https://stackoverflow.com/a/51670871/7351584 44 | * @param {Element} element 45 | * @param {Element} [parent] 46 | * @returns {Array} siblings 47 | */ 48 | export function getAllElementSiblings(element, parent) { 49 | if (!parent) parent = element.parentElement; 50 | 51 | const children = [...parent.children]; 52 | 53 | return children.filter(child => child !== element); 54 | } 55 | 56 | /** 57 | * Sets a string to Title Case 58 | * @param {*} string 59 | */ 60 | export function toTitleCase(string) { 61 | return string.charAt(0).toUpperCase() + string.slice(1); 62 | }; 63 | -------------------------------------------------------------------------------- /icons/spiked-dragon-head.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Combat Carousel Logo](img/combat-carousel-fulllogo.png) 2 | *It's free to ride, but it might cost your life...* 3 | # Combat Carousel 4 | ![https://img.shields.io/endpoint?url=https%3A%2F%2Ffoundryshields.com%2Fversion%3Fstyle%3Dflat%26url%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fdeath-save%2Fcombat-carousel%2Fmain%2Fmodule.json](https://img.shields.io/endpoint?url=https%3A%2F%2Ffoundryshields.com%2Fversion%3Fstyle%3Dflat%26url%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fdeath-save%2Fcombat-carousel%2Fmain%2Fmodule.json) 5 | ![GitHub downloads (latest)](https://img.shields.io/github/downloads-pre/death-save/combat-carousel/latest/module.zip) 6 | [![Forge Installs](https://img.shields.io/badge/dynamic/json?label=Forge%20Install%20Base&query=package.installs&suffix=%&url=https://forge-vtt.com/api/bazaar/package/combat-carousel&colorB=brightgreen)](https://forge-vtt.com/) 7 | 8 | A Foundry VTT module to display CRPG-style Combatant cards in carousel-like format. 9 | 10 | # Using the Module 11 | [Wiki](https://github.com/death-save/combat-carousel/wiki) 12 | 13 | [Release Video - YouTube](https://www.youtube.com/watch?v=tG1rKcG1Oa4) 14 | [Release Stream - Twitch](https://www.twitch.tv/videos/754023462?t=0h0m49s) 15 | 16 | # Attribution 17 | - This module uses the SplideJS library to provide carousel functionality: https://splidejs.com/ 18 | - Carousel icon based on `Horse Carousel` icon by Freepik from https://www.flaticon.com/free-icon/horse-carousel_82324 19 | - Bookmark Icon made by DinosoftLabs from www.flaticon.com 20 | - `goblin-face`, `spiked-dragon`, `cowled` icons from https://game-icons.net 21 | - "Combat Carousel" textmark uses the following fonts: 22 | - - `Capture It` by Magique Fonts https://www.dafont.com/capture-it.font 23 | - - `Budmo Jiggler` by Typodermic Fonts https://www.dafont.com/budmo.font?text=budmo 24 | -------------------------------------------------------------------------------- /templates/about.hbs: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | You can ride for free, but it might cost your life... 5 | v{{version}} 6 |
    7 | 10 |
    11 | 12 | Made with by DEATH SAVE DEVELOPMENT 13 | 14 |
    15 |
    16 |

    Thanks to the support of these awesome people:

    17 |
      18 | {{#each patrons}} 19 |
    1. {{this.name}}
    2. 20 | {{/each}} 21 |
    22 |
    23 | 44 |
    45 | -------------------------------------------------------------------------------- /lang/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "COMBAT_CAROUSEL.SETTINGS.AboutN": "Sobre", 3 | "COMBAT_CAROUSEL.SETTINGS.AboutH": "Sobre o Combat Carousel", 4 | "COMBAT_CAROUSEL.SETTINGS.CollapseNavN": "Esconder Barra de Navegação", 5 | "COMBAT_CAROUSEL.SETTINGS.CollapseNavH": "Esconde a Barra de Navegação quando o Combat Carousel aparecer.", 6 | "COMBAT_CAROUSEL.SETTINGS.ShowHealthN": "Mostrar Barra de Vida", 7 | "COMBAT_CAROUSEL.SETTINGS.ShowHealthH": "Mostra a Barra de Vida embaixo do retrato.", 8 | "COMBAT_CAROUSEL.SETTINGS.HealthBarPermissionN": "Permissões da Barra de Vida para os jogadores", 9 | "COMBAT_CAROUSEL.SETTINGS.HealthBarPermissionH": "Define quando as Barras de Vida devem ser mostradas para os jogadores. All Owned: mostra as Barras de Vida de todos os tokens que o jogador for dono. Use Token Setting: usa a configuração de Barra de Vida de cada token. None: não mostra nenhuma Barra de Vida aos jogadores.", 10 | 11 | "COMBAT_CAROUSEL.CAROUSEL.NextTurn": "Próximo Turno", 12 | "COMBAT_CAROUSEL.CAROUSEL.PreviousTurn": "Turno Anterior", 13 | "COMBAT_CAROUSEL.CAROUSEL.NextRound": "Próxima Rodada", 14 | "COMBAT_CAROUSEL.CAROUSEL.PreviousRound": "Rodada Anterior", 15 | "COMBAT_CAROUSEL.CAROUSEL.NextRoundTooltip": "Ir para a Próxima Rodada", 16 | "COMBAT_CAROUSEL.CAROUSEL.PreviousRoundTooltip": "Ir para a Rodada Anterior", 17 | "COMBAT_CAROUSEL.CAROUSEL.ToggleHint": "Botão esquerdo para esconder/revelar, botão direito para configurar", 18 | 19 | "COMBAT_CAROUSEL.COMBATANT_CARD.Controls.Hide": "Mudar Visibilidade", 20 | "COMBAT_CAROUSEL.COMBATANT_CARD.Controls.Defeated": "Marcar como Derrotado", 21 | "COMBAT_CAROUSEL.COMBATANT_CARD.Controls.Remove": "Remover do Encontro", 22 | 23 | "COMBAT_CAROUSEL.WORDS.Save": "Salvar", 24 | "COMBAT_CAROUSEL.WORDS.Cancel": "Calcelar", 25 | "COMBAT_CAROUSEL.WORDS.Hidden": "Escondido", 26 | "COMBAT_CAROUSEL.WORDS.Defeated": "Derrotado", 27 | "COMBAT_CAROUSEL.WORDS.Encounter": "Encontro", 28 | "COMBAT_CAROUSEL.WORDS.Round": "Rodada", 29 | "COMBAT_CAROUSEL.WORDS.Turn": "Turno" 30 | } 31 | -------------------------------------------------------------------------------- /lang/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "COMBAT_CAROUSEL.SETTINGS.AboutN": "A propos", 3 | "COMBAT_CAROUSEL.SETTINGS.AboutH": "Infos à propos de Combat Carousel", 4 | "COMBAT_CAROUSEL.SETTINGS.CollapseNavN": "Réduire la Barre de Navigation", 5 | "COMBAT_CAROUSEL.SETTINGS.CollapseNavH": "Réduire la Barre de Navigation lorsque Combat Carousel est affiché", 6 | "COMBAT_CAROUSEL.SETTINGS.ShowHealthN": "Afficher la Jauge de Vie", 7 | "COMBAT_CAROUSEL.SETTINGS.ShowHealthH": "Affiche la jauge de vie sous chaque portrait", 8 | "COMBAT_CAROUSEL.SETTINGS.HealthBarPermissionN": "Permissions sur les Jauges de Vie pour les Joueurs", 9 | "COMBAT_CAROUSEL.SETTINGS.HealthBarPermissionH": "Définit dans quelles circonstances montrer les jauges de vies aux joueurs. All Owned: Les jauges de vie de tout jeton que le joueur possède seront affichées. Use Token Setting: Utiliser le paramètre de jauge du jeton (comme Owner ou Always). None: Ne pas montrer aux joueurs les jauges de vie.", 10 | 11 | "COMBAT_CAROUSEL.CAROUSEL.NextTurn": "Prochain Tour", 12 | "COMBAT_CAROUSEL.CAROUSEL.PreviousTurn": "Tour Précédent", 13 | "COMBAT_CAROUSEL.CAROUSEL.NextRound": "Prochain Round", 14 | "COMBAT_CAROUSEL.CAROUSEL.PreviousRound": "Round Précédent", 15 | "COMBAT_CAROUSEL.CAROUSEL.NextRoundTooltip": "Cliquer pour accéder au Prochain Round", 16 | "COMBAT_CAROUSEL.CAROUSEL.PreviousRoundTooltip": "Cliquer pour retourner au Round Précédent", 17 | "COMBAT_CAROUSEL.CAROUSEL.ToggleHint": "Cliquer pour afficher/cacher, clic droit pour accéder à la configuration", 18 | 19 | "COMBAT_CAROUSEL.COMBATANT_CARD.Controls.Hide": "Changer la Visibilité", 20 | "COMBAT_CAROUSEL.COMBATANT_CARD.Controls.Defeated": "Noter comme Vaincu", 21 | "COMBAT_CAROUSEL.COMBATANT_CARD.Controls.Remove": "Retirer de la Rencontre", 22 | 23 | "COMBAT_CAROUSEL.WORDS.Save": "Sauver", 24 | "COMBAT_CAROUSEL.WORDS.Cancel": "Annuler", 25 | "COMBAT_CAROUSEL.WORDS.Hidden": "Caché", 26 | "COMBAT_CAROUSEL.WORDS.Defeated": "Vaincu", 27 | "COMBAT_CAROUSEL.WORDS.Encounter": "Rencontre", 28 | "COMBAT_CAROUSEL.WORDS.Round": "Round", 29 | "COMBAT_CAROUSEL.WORDS.Turn": "Tour" 30 | } -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # From https://github.com/League-of-Foundry-Developers/FoundryVTT-Module-Template/blob/master/.github/workflows/main.yml 2 | name: Release Creation 3 | 4 | on: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | #Substitute the Manifest and Download URLs in the module.json 15 | - name: Substitute Manifest and Download Links For Versioned Ones 16 | id: sub_manifest_link_version 17 | uses: microsoft/variable-substitution@v1 18 | with: 19 | files: 'module.json' 20 | env: 21 | version: ${{github.event.release.tag_name}} 22 | manifest: https://github.com/${{github.repository}}/releases/latest/download/module.json 23 | download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/module.zip 24 | 25 | # create a zip file with all files required by the module to add to the release 26 | - run: zip -r ./module.zip . -x@.github/zip-exclude.lst 27 | 28 | # Create a release for this specific version 29 | - name: Update Release with Files 30 | id: create_version_release 31 | uses: ncipollo/release-action@v1 32 | with: 33 | allowUpdates: true # set this to false if you want to prevent updating existing releases 34 | name: ${{ github.event.release.name }} 35 | draft: false 36 | prerelease: false 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | artifacts: './module.json, ./module.zip' 39 | tag: ${{ github.event.release.tag_name }} 40 | body: ${{ github.event.release.body }} 41 | 42 | # Update the 'latest' release 43 | - name: Create Release 44 | id: create_latest_release 45 | uses: ncipollo/release-action@v1 46 | if: endsWith(github.ref, 'master') 47 | with: 48 | allowUpdates: true 49 | name: Latest 50 | draft: false 51 | prerelease: false 52 | token: ${{ secrets.GITHUB_TOKEN }} 53 | artifacts: './module.json,./module.zip' 54 | tag: latest 55 | body: ${{ github.event.release.body }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | patreon_key.txt 107 | foundry.js -------------------------------------------------------------------------------- /patrons.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "John O" 4 | }, 5 | { 6 | "name": "Greg L" 7 | }, 8 | { 9 | "name": "Cody R" 10 | }, 11 | { 12 | "name": "4535992" 13 | }, 14 | { 15 | "name": "Lazytron" 16 | }, 17 | { 18 | "name": "Alessandro V" 19 | }, 20 | { 21 | "name": "Little A" 22 | }, 23 | { 24 | "name": "wmiller1000 ." 25 | }, 26 | { 27 | "name": "Bernie F" 28 | }, 29 | { 30 | "name": "Norc" 31 | }, 32 | { 33 | "name": "Yicheng M" 34 | }, 35 | { 36 | "name": "Tsulex X" 37 | }, 38 | { 39 | "name": "Martin K" 40 | }, 41 | { 42 | "name": "Duncan" 43 | }, 44 | { 45 | "name": "Zondark" 46 | }, 47 | { 48 | "name": "cesarvillavi" 49 | }, 50 | { 51 | "name": "Sammy P" 52 | }, 53 | { 54 | "name": "Kelevra" 55 | }, 56 | { 57 | "name": "Gerry S" 58 | }, 59 | { 60 | "name": "Mr E" 61 | }, 62 | { 63 | "name": "Shawn M" 64 | }, 65 | { 66 | "name": "TotalFusionOne" 67 | }, 68 | { 69 | "name": "Leon" 70 | }, 71 | { 72 | "name": "Andrés A" 73 | }, 74 | { 75 | "name": "Alex B" 76 | }, 77 | { 78 | "name": "Cody S" 79 | }, 80 | { 81 | "name": "Apostol" 82 | }, 83 | { 84 | "name": "Ms E" 85 | }, 86 | { 87 | "name": "David B" 88 | }, 89 | { 90 | "name": "Trevor s" 91 | }, 92 | { 93 | "name": "David C" 94 | }, 95 | { 96 | "name": "Youness A" 97 | }, 98 | { 99 | "name": "Kyle R" 100 | }, 101 | { 102 | "name": "jlew" 103 | }, 104 | { 105 | "name": "Darshyne I" 106 | }, 107 | { 108 | "name": "Kevin J" 109 | }, 110 | { 111 | "name": "Saevar L" 112 | }, 113 | { 114 | "name": "Thomas Z" 115 | }, 116 | { 117 | "name": "Gene R" 118 | }, 119 | { 120 | "name": "Jose R" 121 | }, 122 | { 123 | "name": "Istvan S" 124 | }, 125 | { 126 | "name": "Mark W" 127 | }, 128 | { 129 | "name": "Shawn" 130 | }, 131 | { 132 | "name": "tim p" 133 | } 134 | ] -------------------------------------------------------------------------------- /module.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "combat-carousel", 3 | "title": "Combat Carousel", 4 | "description": "Adds a CRPG-style combat tracker to the top of the screen", 5 | "version": "0.3.3", 6 | "authors": [ 7 | { 8 | "name": "Evan Clarke", 9 | "url": "https://deathsave.dev", 10 | "discord": "errational#2007", 11 | "reddit": "u/etherboy", 12 | "twitter": "@death_save_dev", 13 | "patreon": "deathsave" 14 | } 15 | ], 16 | "url": "https://github.com/death-save/combat-carousel", 17 | "manifest": "https://github.com/death-save/combat-carousel/releases/latest/download/module.json", 18 | "download": "https://github.com/death-save/combat-carousel/releases/latest/download/module.zip", 19 | "bugs": "https://github.com/death-save/combat-carousel/issues", 20 | "changelog": "https://github.com/death-save/combat-carousel/CHANGELOG.md", 21 | "readme": "https://github.com/death-save/combat-carousel/wiki/", 22 | "scripts": [ 23 | "libs/splide/js/splide.min.js", 24 | "libs/splide/js/splide-render.min.js" 25 | ], 26 | "esmodules": [ 27 | "init.js" 28 | ], 29 | "languages": [ 30 | { 31 | "lang": "en", 32 | "name": "English", 33 | "path": "lang/en.json" 34 | }, 35 | { 36 | "lang": "es", 37 | "name": "Español", 38 | "path": "lang/es.json" 39 | }, 40 | { 41 | "lang": "fr", 42 | "name": "Français", 43 | "path": "lang/fr.json" 44 | }, 45 | { 46 | "lang": "ja", 47 | "name": "日本語", 48 | "path": "lang/ja.json" 49 | }, 50 | { 51 | "lang": "pt-br", 52 | "name": "português do Brasil", 53 | "path": "lang/pt-br.json" 54 | } 55 | ], 56 | "styles" : [ 57 | "./styles/combat-carousel.css", 58 | "./libs/splide/css/splide.min.css" 59 | ], 60 | "media": [ 61 | { 62 | "type": "cover", 63 | "url": "https://i.imgur.com/JSYoVvb.png", 64 | "caption": "Combat Carousel in action" 65 | }, 66 | { 67 | "type": "video", 68 | "url": "https://i.imgur.com/yZK53mA.mp4", 69 | "caption":"Combat Carousel v0.2.0 moving" 70 | }, 71 | { 72 | "type": "video", 73 | "url": "https://www.youtube.com/watch?v=tG1rKcG1Oa4", 74 | "caption":"Combat Carousel launch video" 75 | } 76 | ], 77 | "flags": { 78 | "manifestPlusVersion": "1.2.0", 79 | "allowBugReporter": true 80 | }, 81 | "compatibility": { 82 | "minimum": "10.275", 83 | "verified": "10.291" 84 | }, 85 | "name": "combat-carousel", 86 | "minimumCoreVersion": "10.275" 87 | } -------------------------------------------------------------------------------- /icons/wyvern.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/config-form.hbs: -------------------------------------------------------------------------------- 1 |
    2 | 22 | 23 |

    {{localize "COMBAT_CAROUSEL.OVERLAY_CONFIG.Name"}}

    24 | {{#each overlaySettings}} 25 |
    26 |
    27 | 28 | 29 |
    30 |
    31 | 32 | 33 |
    34 | 35 |
    36 | 37 | 40 |
    41 |
    42 |
    43 | {{/each}} 44 |
    45 | 46 | 47 |
    48 |
    -------------------------------------------------------------------------------- /icons/goblin-head.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const jsdoc2md = require('jsdoc-to-markdown'); 2 | const { parallel, series } = require('gulp'); 3 | const fs = require('fs'); 4 | const zip = require('gulp-zip'); 5 | const gulp = require('gulp'); 6 | const version = require('./package.json').version; 7 | const fetch = require('node-fetch'); 8 | 9 | function docs(done) { 10 | jsdoc2md.render({ files: ['modules/*.mjs', '*.js'], configure: 'jsdoc/conf.json' }) 11 | .then(output => fs.writeFileSync('api.md', output)); 12 | return done(); 13 | } 14 | 15 | async function patrons(done) { 16 | 17 | const patrons = await fetchPatrons(); 18 | const uniquePatrons = patrons.filter((v, i, a) => { 19 | return a.findIndex(t => (t.attributes.full_name === v.attributes.full_name)) === i; 20 | }); 21 | 22 | const activePatrons = uniquePatrons.filter(m => m.attributes.patron_status === "active_patron"); 23 | const patronList = []; 24 | for (let p of activePatrons) { 25 | const nameParts = p.attributes.full_name.split(" "); 26 | const firstName = nameParts[0]; 27 | const lastName = nameParts.length > 1 && nameParts[1].length ? nameParts[1] : ""; 28 | const lastInitial = lastName ? lastName.substr(0,1) : ""; 29 | const name = lastInitial ? `${firstName} ${lastInitial}` : `${firstName}`; 30 | 31 | const patron = { 32 | name 33 | } 34 | 35 | patronList.push(patron); 36 | } 37 | 38 | fs.writeFileSync('patrons.json', JSON.stringify(patronList, null, 4)) 39 | console.log(activePatrons.length); 40 | 41 | return done(); 42 | } 43 | 44 | function build(done) { 45 | gulp.src('module.json') 46 | .pipe(gulp.dest('dist')); 47 | 48 | gulp.src([ 49 | '**/*', 50 | '!dist/**', 51 | '!out/**', 52 | '!jsdoc/**', 53 | '!node_modules/**', 54 | '!.gitignore', 55 | '!gulpfile.js', 56 | '!package.json', 57 | '!package-lock.json' 58 | ]) 59 | .pipe(zip(`combat-carousel.zip`)) 60 | .pipe(gulp.dest('dist')); 61 | return done(); 62 | } 63 | 64 | async function fetchPatrons(patrons=[], nextPage=null) { 65 | const accessToken = fs.readFileSync('patreon_key.txt', 'utf-8'); 66 | const campaignId = '5254689'; 67 | const url = `https://www.patreon.com/api/oauth2/v2/campaigns/${campaignId}/members`; 68 | const query = '?fields%5Bmember%5D=full_name,patron_status'; 69 | const pagination = nextPage ? `&page%5Bcursor%5D=${nextPage}` : ''; 70 | 71 | let myHeaders = new fetch.Headers(); 72 | myHeaders.append("Authorization", `Bearer ${accessToken}`); 73 | myHeaders.append("Cookie", "__cfduid=d0ff53e0a0f52232bc3d071e4e41b36f11601349207; patreon_device_id=aa72995a-d296-4e22-8336-fba6580fa49b; __cf_bm=6d8302a7c059da3dc166c9718f7372a386a1911c-1601350627-1800-AXgBmtTqEn83/w2Ka4Mq+ewtUVrvq5+bZppjqM6WDA2ofb1XwF22RNcARQzzHOF2S9K/A2UUeVUnfInGOwqJ6qk="); 74 | 75 | const requestOptions = { 76 | method: 'GET', 77 | headers: myHeaders, 78 | redirect: 'follow' 79 | }; 80 | 81 | const response = await fetch(`${url}${query}${pagination}`, requestOptions); 82 | //console.log(response); 83 | const json = await response.json(); 84 | //console.log(json); 85 | const data = json.data; 86 | //console.log(data); 87 | const responseNextPage = json.meta && json.meta.pagination && json.meta.pagination.cursors ? json.meta.pagination.cursors.next : null; 88 | //console.log(responseNextPage); 89 | //console.log(json.meta); 90 | patrons = patrons.concat(data); 91 | //console.log(patrons); 92 | 93 | if (responseNextPage) { 94 | const nextPatrons = await fetchPatrons(patrons, responseNextPage); 95 | //console.log(nextPatrons); 96 | patrons = patrons.concat(nextPatrons) 97 | } 98 | 99 | console.log(patrons.length); 100 | return patrons; 101 | } 102 | 103 | const chores = parallel(/*patrons,*/ docs); 104 | 105 | exports.build = build; 106 | exports.docs = docs; 107 | //exports.patrons = patrons; 108 | exports.chores = chores; 109 | exports.default = series(chores, build); -------------------------------------------------------------------------------- /icons/orc-head.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/config-form.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * CombatCarouselConfig module 3 | * @module config-form 4 | */ 5 | 6 | import { NAME, SETTING_KEYS, TEMPLATE_PATH } from "./config.mjs"; 7 | 8 | /** 9 | * A form-app for setting the icons and properties to use in the Combat Carousel Overlay 10 | * @extends FormApplication 11 | */ 12 | export default class CombatCarouselConfig extends FormApplication { 13 | /** 14 | * Class instance constructor 15 | * @param args 16 | */ 17 | constructor(...args) { 18 | super(...args); 19 | } 20 | 21 | /** 22 | * Get the default options for the class 23 | * @override 24 | */ 25 | static get defaultOptions() { 26 | return mergeObject(super.defaultOptions, { 27 | id: "combat-carousel-config", 28 | template: `${TEMPLATE_PATH}/config-form.hbs`, 29 | title: "COMBAT_CAROUSEL.OVERLAY_CONFIG.Title", 30 | width: 400, 31 | height: "auto", 32 | resizable: true, 33 | classes: ["sheet"] 34 | }); 35 | } 36 | 37 | /** 38 | * Gets data for the template 39 | */ 40 | getData() { 41 | const overlaySettings = game.settings.get(NAME, SETTING_KEYS.overlaySettings); 42 | return { 43 | overlaySettings, 44 | barAttributes: this.getAttributeChoices() 45 | } 46 | } 47 | 48 | /** 49 | * Update handler following form submission 50 | * @param {Event} event form submit event 51 | * @param {Object} formData 52 | */ 53 | async _updateObject(event, formData) { 54 | const oldValues = game.settings.get(NAME, SETTING_KEYS.overlaySettings); 55 | const newValues = oldValues ? duplicate(oldValues) : []; 56 | const names = []; 57 | const icons = []; 58 | const actorProperties = []; 59 | 60 | const nameRegex = new RegExp("name", "i"); 61 | const iconRegex = new RegExp("icon", "i"); 62 | const actorPropertyRegex = new RegExp("actor-property", "i"); 63 | const indexRegex = /\d+/; 64 | 65 | for (const key in formData) { 66 | const index = key.match(indexRegex)[0]; 67 | 68 | if (key.match(nameRegex)) { 69 | //names.splice(index, 0, value); 70 | newValues[index].name = formData[key]; 71 | } else if (key.match(iconRegex)) { 72 | //icons.splice(index, 0, value); 73 | newValues[index].img = formData[key]; 74 | } else if (key.match(actorPropertyRegex)) { 75 | //actorProperties.splice(index, 0, value); 76 | newValues[index].value = formData[key]; 77 | } 78 | } 79 | 80 | await game.settings.set(NAME, SETTING_KEYS.overlaySettings, newValues); 81 | ui?.combatCarousel?.render(); 82 | } 83 | 84 | /** 85 | * Attach listeners 86 | * @param html 87 | */ 88 | activateListeners(html) { 89 | const imgPathInput = html.find("input.icon-path"); 90 | const cancelButton = html.find("button[name='cancel']"); 91 | 92 | imgPathInput.on("change", (event, html) => this._onImgPathChange(event, html)); 93 | cancelButton.on("click", (event) => this.close()); 94 | 95 | super.activateListeners(html); 96 | } 97 | 98 | /** 99 | * Img Path Change handler 100 | * @param event 101 | * @param html 102 | */ 103 | _onImgPathChange(event, html) { 104 | const row = event.currentTarget.closest("div.overlay-row"); 105 | const index = row ? row.dataset.row : null; 106 | const icon = row.querySelector("img"); 107 | icon.src = event.currentTarget.value; 108 | } 109 | 110 | /** 111 | * Get an Array of attribute choices which could be tracked for Actors in the Combat Tracker 112 | * @return {Promise} 113 | */ 114 | getAttributeChoices() { 115 | const actorData = {}; 116 | for ( let model of Object.values(game.system.model.Actor) ) { 117 | mergeObject(actorData, model); 118 | } 119 | const attributes = TokenDocument.getTrackedAttributes(actorData, []); 120 | //attributes.bar.forEach(a => a.push("value")); 121 | return TokenDocument.getTrackedAttributeChoices(attributes); 122 | } 123 | } -------------------------------------------------------------------------------- /templates/combat-carousel.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/combatant-card.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 |
    3 | {{#if combatant.carousel.isGM}} 4 |
    5 | 6 | 7 | 8 |
    9 | {{/if}} 10 |

    {{combatant.name}}

    11 |
    12 |
    13 |
    14 | 15 | {{#if combatant.defeated}} 16 | 17 | {{/if}} 18 | {{#if combatant.carousel.owner}} 19 | {{#if combatant.hidden}} 20 | 21 | {{/if}} 22 | {{/if}} 23 | 24 | 25 |
    26 | {{#each combatant.carousel.overlayProperties}} 27 |
    28 | 29 | {{this.value}} 30 |
    31 | {{/each}} 32 |
    33 |
    34 |
    35 | 36 | 37 | 38 | 39 | 40 | 41 |
    42 |
    43 | {{#if combatant.carousel.overlayEffect}} 44 | 45 | {{/if}} 46 | 47 |
    48 |
    49 | {{#each combatant.carousel.effects}} 50 |
    51 | 52 |
    53 | {{/each}} 54 |
    55 |
    56 | 57 |
    58 | 66 |
    67 |
    68 |
    69 |
    70 | {{!--
    71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
    --}} 81 |
  • -------------------------------------------------------------------------------- /libs/splide/css/splide.min.css: -------------------------------------------------------------------------------- 1 | .splide__container{box-sizing:border-box;position:relative}.splide__list{backface-visibility:hidden;display:-ms-flexbox;display:flex;height:100%;margin:0!important;padding:0!important}.splide.is-initialized:not(.is-active) .splide__list{display:block}.splide__pagination{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:center;justify-content:center;margin:0;pointer-events:none}.splide__pagination li{display:inline-block;line-height:1;list-style-type:none;margin:0;pointer-events:auto}.splide:not(.is-overflow) .splide__pagination{display:none}.splide__progress__bar{width:0}.splide{position:relative;visibility:hidden}.splide.is-initialized,.splide.is-rendered{visibility:visible}.splide__slide{backface-visibility:hidden;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0;list-style-type:none!important;margin:0;position:relative}.splide__slide img{vertical-align:bottom}.splide__spinner{animation:splide-loading 1s linear infinite;border:2px solid #999;border-left-color:transparent;border-radius:50%;bottom:0;contain:strict;display:inline-block;height:20px;left:0;margin:auto;position:absolute;right:0;top:0;width:20px}.splide__sr{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.splide__toggle.is-active .splide__toggle__play,.splide__toggle__pause{display:none}.splide__toggle.is-active .splide__toggle__pause{display:inline}.splide__track{overflow:hidden;position:relative;z-index:0}@keyframes splide-loading{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.splide__track--draggable{-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.splide__track--fade>.splide__list>.splide__slide{margin:0!important;opacity:0;z-index:0}.splide__track--fade>.splide__list>.splide__slide.is-active{opacity:1;z-index:1}.splide--rtl{direction:rtl}.splide__track--ttb>.splide__list{display:block}.splide__arrow{-ms-flex-align:center;align-items:center;background:#ccc;border:0;border-radius:50%;cursor:pointer;display:-ms-flexbox;display:flex;height:2em;-ms-flex-pack:center;justify-content:center;opacity:.7;padding:0;position:absolute;top:50%;transform:translateY(-50%);width:2em;z-index:1}.splide__arrow svg{fill:#000;height:1.2em;width:1.2em}.splide__arrow:hover:not(:disabled){opacity:.9}.splide__arrow:disabled{opacity:.3}.splide__arrow:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide__arrow--prev{left:1em}.splide__arrow--prev svg{transform:scaleX(-1)}.splide__arrow--next{right:1em}.splide.is-focus-in .splide__arrow:focus{outline:3px solid #0bf;outline-offset:3px}.splide__pagination{bottom:.5em;left:0;padding:0 1em;position:absolute;right:0;z-index:1}.splide__pagination__page{background:#ccc;border:0;border-radius:50%;display:inline-block;height:8px;margin:3px;opacity:.7;padding:0;position:relative;transition:transform .2s linear;width:8px}.splide__pagination__page.is-active{background:#fff;transform:scale(1.4);z-index:1}.splide__pagination__page:hover{cursor:pointer;opacity:.9}.splide__pagination__page:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide.is-focus-in .splide__pagination__page:focus{outline:3px solid #0bf;outline-offset:3px}.splide__progress__bar{background:#ccc;height:3px}.splide__slide{-webkit-tap-highlight-color:rgba(0,0,0,0)}.splide__slide:focus{outline:0}@supports(outline-offset:-3px){.splide__slide:focus-visible{outline:3px solid #0bf;outline-offset:-3px}}@media screen and (-ms-high-contrast:none){.splide__slide:focus-visible{border:3px solid #0bf}}@supports(outline-offset:-3px){.splide.is-focus-in .splide__slide:focus{outline:3px solid #0bf;outline-offset:-3px}}@media screen and (-ms-high-contrast:none){.splide.is-focus-in .splide__slide:focus{border:3px solid #0bf}.splide.is-focus-in .splide__track>.splide__list>.splide__slide:focus{border-color:#0bf}}.splide__toggle{cursor:pointer}.splide__toggle:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide.is-focus-in .splide__toggle:focus{outline:3px solid #0bf;outline-offset:3px}.splide__track--nav>.splide__list>.splide__slide{border:3px solid transparent;cursor:pointer}.splide__track--nav>.splide__list>.splide__slide.is-active{border:3px solid #000}.splide__arrows--rtl .splide__arrow--prev{left:auto;right:1em}.splide__arrows--rtl .splide__arrow--prev svg{transform:scaleX(1)}.splide__arrows--rtl .splide__arrow--next{left:1em;right:auto}.splide__arrows--rtl .splide__arrow--next svg{transform:scaleX(-1)}.splide__arrows--ttb .splide__arrow{left:50%;transform:translate(-50%)}.splide__arrows--ttb .splide__arrow--prev{top:1em}.splide__arrows--ttb .splide__arrow--prev svg{transform:rotate(-90deg)}.splide__arrows--ttb .splide__arrow--next{bottom:1em;top:auto}.splide__arrows--ttb .splide__arrow--next svg{transform:rotate(90deg)}.splide__pagination--ttb{bottom:0;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;left:auto;padding:1em 0;right:.5em;top:0} -------------------------------------------------------------------------------- /modules/fixed-draggable.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @module FixedDraggable 3 | */ 4 | 5 | /** 6 | * An implementation of the Draggable class that allows fixed elements to be dragged 7 | */ 8 | export default class FixedDraggable extends Draggable { 9 | constructor(app, element, handle, resizable) { 10 | super(app, element, handle, resizable); 11 | } 12 | 13 | /* ----------------------------------------- */ 14 | 15 | /** 16 | * Handle the initial mouse click which activates dragging behavior for the application 17 | * @private 18 | * @override 19 | */ 20 | _onDragMouseDown(event) { 21 | event.preventDefault(); 22 | // Record initial position 23 | const appPosition = { 24 | width: this.app.position.width ?? this.app.element.width(), 25 | height: this.app.position.height ?? this.app.element.height(), 26 | left: this.app.position.left ?? this.app.element.position().left, 27 | top: this.app.position.top ?? this.app.element.position().top, 28 | scale: this.app.position.scale ?? 1.0 29 | } 30 | 31 | this.initialPosition = appPosition; 32 | this.position = duplicate(appPosition); 33 | this._initial = {x: event.clientX, y: event.clientY}; 34 | // Add temporary handlers 35 | window.addEventListener(...this.handlers.dragMove); 36 | window.addEventListener(...this.handlers.dragUp); 37 | } 38 | 39 | /* ----------------------------------------- */ 40 | 41 | /** 42 | * Move the window with the mouse, bounding the movement to ensure the window stays within bounds of the viewport 43 | * @private 44 | */ 45 | _onDragMouseMove(event) { 46 | event.preventDefault(); 47 | // Limit dragging to 60 updates per second 48 | const now = Date.now(); 49 | if ( (now - this._moveTime) < (1000/60) ) return; 50 | this._moveTime = now; 51 | 52 | // Update application position 53 | this.position.left = this.initialPosition.left + (event.clientX - this._initial.x); 54 | this.position.top = this.initialPosition.top + (event.clientY - this._initial.y); 55 | 56 | this.app.setPosition({ 57 | left: this.position.left, 58 | top: this.position.top 59 | }); 60 | } 61 | 62 | /* ----------------------------------------- */ 63 | 64 | /** 65 | * Conclude the dragging behavior when the mouse is release, setting the final position and removing listeners 66 | * @private 67 | */ 68 | _onDragMouseUp(event) { 69 | event.preventDefault(); 70 | if (this.app._onDragMouseUp instanceof Function) this.app._onDragMouseUp(event, this.position, this.initialPosition); 71 | window.removeEventListener(...this.handlers.dragMove); 72 | window.removeEventListener(...this.handlers.dragUp); 73 | } 74 | 75 | /* ----------------------------------------- */ 76 | 77 | /** 78 | * Handle the initial mouse click which activates dragging behavior for the application 79 | * @private 80 | */ 81 | _onResizeMouseDown(event) { 82 | event.preventDefault(); 83 | // Limit dragging to 60 updates per second 84 | const now = Date.now(); 85 | if ( (now - this._moveTime) < (1000/60) ) return; 86 | this._moveTime = now; 87 | // Record initial position 88 | this.position = duplicate(this.app.position); 89 | if ( this.position.height === "auto" ) this.position.height = this.element.clientHeight; 90 | if ( this.position.width === "auto" ) this.position.width = this.element.clientWidth; 91 | this._initial = {x: event.clientX, y: event.clientY}; 92 | // Add temporary handlers 93 | window.addEventListener(...this.handlers.resizeMove); 94 | window.addEventListener(...this.handlers.resizeUp); 95 | } 96 | 97 | /* ----------------------------------------- */ 98 | 99 | /** 100 | * Move the window with the mouse, bounding the movement to ensure the window stays within bounds of the viewport 101 | * @private 102 | */ 103 | _onResizeMouseMove(event) { 104 | event.preventDefault(); 105 | this.app.setPosition({ 106 | width: this.position.width + (event.clientX - this._initial.x), 107 | height: this.position.height + (event.clientY - this._initial.y) 108 | }); 109 | } 110 | 111 | /* ----------------------------------------- */ 112 | 113 | /** 114 | * Conclude the dragging behavior when the mouse is release, setting the final position and removing listeners 115 | * @private 116 | */ 117 | _onResizeMouseUp(event) { 118 | event.preventDefault(); 119 | window.removeEventListener(...this.handlers.resizeMove); 120 | window.removeEventListener(...this.handlers.resizeUp); 121 | this.app._onResize(event); 122 | } 123 | } -------------------------------------------------------------------------------- /modules/config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Config module 3 | * @module config 4 | */ 5 | 6 | /** 7 | * Module Name 8 | */ 9 | export const NAME = "combat-carousel"; 10 | 11 | /** 12 | * Module title 13 | */ 14 | export const TITLE = "Combat Carousel"; 15 | 16 | /** 17 | * Path to module 18 | */ 19 | export const PATH = "modules/combat-carousel"; 20 | 21 | /** 22 | * Path to templates 23 | */ 24 | export const TEMPLATE_PATH = `${PATH}/templates`; 25 | 26 | /** 27 | * Module Icon Paths 28 | */ 29 | export const CAROUSEL_ICONS = { 30 | noCombat: "modules/combat-carousel/icons/empty-carousel-solid.svg", 31 | noTurns: "modules/combat-carousel/icons/empty-carousel-solid2.svg", 32 | hasTurns: "modules/combat-carousel/icons/combat-carousel-solid2.svg" 33 | } 34 | 35 | /** 36 | * Settings Keys 37 | */ 38 | export const SETTING_KEYS = { 39 | about: "about", 40 | appPosition: "appPosition", 41 | enabled: "enabled", 42 | overlayConfigMenu: "overlayConfigMenu", 43 | showOverlay: "showOverlay", 44 | overlayPermission: "overlayPermission", 45 | showEffects: "showEffects", 46 | collapseNav: "collapseNav", 47 | collapsed: "carouselCollapsed", 48 | showBar1: "showBar1", 49 | bar1Permission: "playerBar1Permission", 50 | bar1Attribute: "bar1Attribute", 51 | bar1Title: "bar1Title", 52 | overlaySettings: "overlaySettings", 53 | carouselSize: "carouselSize", 54 | showInitiative: "showInitiative", 55 | showInitiativeIcon: "showInitiativeIcon", 56 | initiativePermission: "initiativePermission", 57 | imageType: "imageType", 58 | controlActiveCombatantToken: "controlActiveCombatantToken", 59 | panOnClick: "panOnClick", 60 | alwaysOnTop: "alwaysOnTop", 61 | openOnCombatCreate: "openOnCombatCreate" 62 | } 63 | 64 | /** 65 | * Default Config 66 | */ 67 | export const DEFAULT_CONFIG = { 68 | // @todo #1 Create this programmatically in the future... 69 | overlaySettings: [ 70 | { 71 | name: "", 72 | img: "/icons/svg/d20-black.svg", 73 | value: "" 74 | }, 75 | { 76 | name: "", 77 | img: "/icons/svg/d20-black.svg", 78 | value: "" 79 | }, 80 | { 81 | name: "", 82 | img: "/icons/svg/d20-black.svg", 83 | value: "" 84 | }, 85 | { 86 | name: "", 87 | img: "/icons/svg/d20-black.svg", 88 | value: "" 89 | } 90 | ], 91 | showOverlay: { 92 | choices: { 93 | never: "Never", 94 | hover: "On Hover", 95 | active: "Active Combatant", 96 | activeHover: "Active Combatant and On Hover", 97 | always: "Always" 98 | } 99 | }, 100 | overlayPermission: { 101 | choices: { 102 | all: "All", 103 | owned: "Owned Actors", 104 | observed: "Observed Actors", 105 | limited: "Limited Actors", 106 | none: "None" 107 | } 108 | }, 109 | showBar: { 110 | choices: { 111 | never: "Never", 112 | hover: "On Hover", 113 | active: "Active Combatant", 114 | activeHover: "Active Combatant and On Hover", 115 | always: "Always" 116 | } 117 | }, 118 | bar1Permission: { 119 | choices: { 120 | all: "All", 121 | owned: "Owned Actors", 122 | observed: "Observed Actors", 123 | limited: "Limited Actors", 124 | none: "None" 125 | } 126 | }, 127 | bar1Attribute: "attributes.hp", 128 | bar1Title: "HP", 129 | appPosition: { 130 | left: 120, 131 | top: 0, 132 | scale: 1.0 133 | }, 134 | carouselSize: { 135 | choices: { 136 | xs: "Extra Small", 137 | sm: "Small", 138 | med: "Medium", 139 | lg: "Large" 140 | }, 141 | sizeScaleMap: { 142 | xs: 0.5, 143 | sm: 0.8, 144 | med: 1, 145 | lg: 1.2 146 | } 147 | }, 148 | showInitiative: { 149 | choices: { 150 | never: "Never", 151 | hover: "On Hover", 152 | active: "Active Combatant", 153 | activeHover: "Active Combatant and On Hover", 154 | always: "Always" 155 | } 156 | }, 157 | showInitiativeIcon: { 158 | choices: { 159 | never: "Never", 160 | withInit: "With Initiative Value", 161 | unrolled: "For Unrolled Combatants", 162 | withInitUnrolled: "With Initiative and Unrolled", 163 | always: "Always" 164 | } 165 | }, 166 | initiativePermission: { 167 | choices: { 168 | all: "All", 169 | owned: "Owned", 170 | observed: "Observed", 171 | limited: "Limited Actors", 172 | none: "None" 173 | } 174 | }, 175 | imageType: { 176 | choices: { 177 | actor: "Actor", 178 | tokenActor: "Token Actor", 179 | token: "Token", 180 | combatant: "Combatant" 181 | } 182 | }, 183 | showEffects: { 184 | choices: { 185 | all: "All Effects", 186 | none: "No Effects", 187 | allActive: "All Active Effects", 188 | activeTemporary: "Active Temporary Effects", 189 | activePassive: "Active Passive Effects" 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /icons/empty-carousel-solid2.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 44 | 47 | 53 | 54 | 60 | 68 | 72 | 73 | 75 | 76 | 78 | 79 | 81 | 82 | 84 | 85 | 87 | 88 | 90 | 91 | 93 | 94 | 96 | 97 | 99 | 100 | 102 | 103 | 105 | 106 | 108 | 109 | 111 | 112 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /lang/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "COMBAT_CAROUSEL": { 3 | "SETTINGS": { 4 | "AboutN": "詳細", 5 | "AboutH": "Combat Carouselについての情報を表示します。", 6 | "EnableModuleN": "Combat Carouselを有効にする", 7 | "EnableModuleH": "モジュールの機能を有効にします。", 8 | "OverlayConfigH": "参戦者のオーバーレイ表示を設定するフォームを開きます。", 9 | "CollapseNavN": "ナビゲーションバーの折りたたみ", 10 | "CollapseNavH": "Combat Carouselが表示されるときは、シーンナビゲーションバーが折りたたまれるようになります。", 11 | "ShowBar1N": "リソースバー1を表示する", 12 | "ShowBar1H": "設定されたリソースバーを表示します。 「Always」:(あれば)常に表示します。「Active Combatant」:アクティブな参戦者のみ表示します。「Hover」:マウスホバー時のみ表示します。「Never」:表示しません。※注:追加のGM設定により、表示が制限されることがあります。", 13 | "Bar1PermissionN": "プレイヤーへのリソースバー1の表示方法", 14 | "Bar1PermissionH": "リソースバー1がプレイヤーにどのように表示されるかを設定します。「All」:すべての参戦者にリソースバーが表示されます。「Owned Actors」:所有しているコマのみリソースバーが表示されます。「Observed Actor」:観察者(Observer)権限を持っているコマのリソースバーが表示されす。「-」:プレイヤーはリソースバーを見ることができません。", 15 | "ShowOverlayN": "オーバーレイ情報を表示する", 16 | "ShowOverlayH": "設定されたオーバーレイ情報を表示します。「Always」:常に表示します。「Active Combatant」:アクティブな参戦者のみ表示します。「On Hover」:マウスホバー時のみ表示します。「Never」:オーバーレイを表示しません。※注:追加のGM設定により、表示が制限されることがあります。", 17 | "ShowEffectsN": "効果を表示", 18 | "ShowEffectsH": "Combat Crouselにキャラクターに設定されている効果のアイコンを表示します。状態異常などを多く多様するときに便利です。「All」はすべての効果を表示します。「None」は効果を表示しないです。「Active」はアクティブ効果のみを表示します。「Active and Passive」はアクティブと受動効果を両方表示します。(未発動効果は表示されません)", 19 | "OverlayPermissionN": "プレイヤーへのオーバーレイ情報の表示方法", 20 | "OverlayPermissionH": "オーバーレイ情報がプレイヤーにどのように表示されるかを設定します。「All」:すべての参戦者に情報が表示されます。「Owned Actor」:所有しているコマのみ情報が表示されます。「Observed Actor」:観察者(Observer)権限を持っているコマの情報が表示されます。「-」:プレイヤーは情報を見ることができません。", 21 | "Bar1AttributeN": "バー1の属性", 22 | "Bar1AttributeH": "バーに用いる属性値へのパスを入力します。属性値が単一の値の場合は,その数値を表示します。現在値と最大値のプロパティを持つ場合は、値が小さくなるにつれ減少するメーターを表示します(例:体力)。", 23 | "Bar1TitleN": "バー1の名前", 24 | "Bar1TitleH": "バーに表示される名前を設定します(例:HP)。", 25 | "ShowInitiativeN": "イニシアチブを表示する", 26 | "ShowInitiativeH": "イニシアチブの値を表示します。「Always」:(あれば)常に値を表示します。「Active Combatant」:アクティブな参戦者の値のみ表示されます。「On Hover」:マウスホバー時のみ値を表示します。「Never」:表示しません。", 27 | "ShowInitiativeIconN": "イニシアチブ・アイコンを表示する", 28 | "ShowInitiativeIconH": "イニシアチブ値の背景にアイコンを表示します。「Always」:(あれば)常にアイコンを表示します。「With Initiative Value」:イニシアチブ値があるときに表示します。「For Unrolled Combatants」:イニシアチブ値がないときに表示する。「Never」:アイコンは表示しない。", 29 | "InitiativePermissionN": "プレイヤーのイニシアチブ権限", 30 | "InitiativePermissionH": "イニシアチブ値がプレイヤーにどのように表示されるかを設定します。「All」:すべての参戦者のイニシアチブ値を表示する。「Owned」:所有しているコマのみイニシアチブ値が表示されます。「Observed」:観察者(Observer)権限を持っているコマにイニシアチブ値が表示されます。「-」:プレイヤーはイニシアチブ値を見ることができません。", 31 | "ImageTypeN": "参戦者の画像", 32 | "ImageTypeH": "Carousel上に表示される参戦者の画像を設定します。「Actor」:アクター画像が使用されます。「Token Actor」:Token Actorの画像(設定がない場合はアクター画像が使用されます)。「トークン」:コマ画像が使用されます。「Combatant」:参戦者に設定された画像を使用します(設定がない場合はコマ画像が使用されます)。", 33 | "CarouselSizeN": "Carouselのサイズ", 34 | "CarouselSizeH": "Carouselの表示サイズを設定します。HUD/コントロール要素は拡大/縮小されません。", 35 | "ControlActiveCombatantTokenN": "ターン開始コマを自動選択", 36 | "ControlActiveCombatantTokenH": "ターンを開始したコマを自動的に選択肢コントロール化に置きます。", 37 | "PanOnClickN": "カードクリックで注目", 38 | "PanOnClickH": "Carouselのイニシアチブカードをクリックするとそれに対応したコマに移動する。(注:コマは必ず可視状態でなければなりません)", 39 | "AlwaysOnTopN": "常に上", 40 | "AlwaysOnTopH": "Carouselは常に他のUIの上に描画されます。", 41 | "OpenOnCombatCreateN": "Combat Carouselを戦闘開始で表示する", 42 | "OpenOnCombatCreateH": "新たな戦闘が作成されたとき、Carouselを自動的に表示します。" 43 | }, 44 | 45 | "CAROUSEL": { 46 | "NextTurn": "ターンを進める", 47 | "PreviousTurn": "ターンを巻き戻す", 48 | "NextRound": "ラウンドを進める", 49 | "PreviousRound": "ラウンドを巻き戻す", 50 | "NextRoundTooltip": "クリックすると次のラウンドへ進みます。", 51 | "PreviousRoundTooltip": "クリックすると前のラウンドに巻き戻ります。", 52 | "ToggleHint": "クリックすると表示/非表示を切り替えます。右クリックで設定画面を開きます。", 53 | "EndEncounter": "クリックすると遭遇を終了します。", 54 | "DragTooltip": "ドラッグで移動します/右クリックで位置をリセットします。", 55 | 56 | "Controls.CreateEncounter": "遭遇を作成", 57 | "Controls.DeleteEncounter": "遭遇を終了", 58 | "Controls.NextEncounter": "次の遭遇を有効化", 59 | "Controls.PreviousEncounter": "前の遭遇を有効化", 60 | "Controls.RollAll": "すべてロール", 61 | "Controls.RollNPCs": "NPCをロール", 62 | "Controls.ResetAll": "すべてリセット", 63 | "Controls.EncounterConfig": "イニシアチブ表設定" 64 | }, 65 | 66 | "COMBATANT_CARD": { 67 | "ActiveTurn": "アクティブなターン", 68 | "Controls.Hide": "可視/不可視切り替え", 69 | "Controls.Defeated": "敗北マークをつける", 70 | "Controls.Remove": "遭遇から削除", 71 | "RollInitiative": "クリックでロール", 72 | "EditInitiative": "クリックで編集/右クリックでリセット(GMのみ)" 73 | }, 74 | 75 | "ABOUT.Label": "Combat Carouselについて", 76 | 77 | "CONFIG_FORM.ResourceBars": "リソースバー", 78 | "CONFIG_FORM.ResourceBar1": "リソースバー1", 79 | 80 | "OVERLAY_CONFIG.Title": "Combat Carousel | オーバーレイ設定", 81 | "OVERLAY_CONFIG.Name": "オーバーレイ設定", 82 | 83 | "TOGGLE_BUTTON.Tooltip": "Combat Carousel - クリックすると表示/非表示を切り替えます。右クリックで位置をリセットします。", 84 | 85 | "WORDS": { 86 | "Save": "保存", 87 | "Cancel": "キャンセル", 88 | "Hidden": "不可視", 89 | "Defeated": "敗退", 90 | "Encounter": "遭遇", 91 | "Round": "ラウンド", 92 | "Turn": "ターン", 93 | "Property": "プロパティ", 94 | "Name": "名", 95 | "Value": "値", 96 | "BrowseFiles": "ファイルを参照" 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /icons/empty-carousel-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 43 | 46 | 52 | 53 | 59 | 67 | 71 | 72 | 74 | 75 | 77 | 78 | 80 | 81 | 83 | 84 | 86 | 87 | 89 | 90 | 92 | 93 | 95 | 96 | 98 | 99 | 101 | 102 | 104 | 105 | 107 | 108 | 110 | 111 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /icons/empty-carousel.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 43 | 46 | 52 | 56 | 62 | 69 | 73 | 74 | 76 | 77 | 79 | 80 | 82 | 83 | 85 | 86 | 88 | 89 | 91 | 92 | 94 | 95 | 97 | 98 | 100 | 101 | 103 | 104 | 106 | 107 | 109 | 110 | 112 | 113 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /icons/carousel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 12 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "COMBAT_CAROUSEL": { 3 | "SETTINGS": { 4 | "AboutN": "About", 5 | "AboutH": "Info about Combat Carousel", 6 | "EnableModuleN": "Enable Combat Carousel", 7 | "EnableModuleH": "Enables module functionality", 8 | "OverlayConfigH": "Open the form to configure the Combatant Overlay", 9 | "CollapseNavN": "Collapse Navigation Bar", 10 | "CollapseNavH": "Collapse the Scene Navigation Bar when Combat Carousel is rendered", 11 | "ShowBar1N": "Show Resource Bar 1", 12 | "ShowBar1H": "Shows resource bar (if configured). Options are: Always: always show the bar (if any). Active Combatant: only show the bar for the Active Combatant. Hover: only show the bar on hover. Never: never show the bar. Note: additional GM settings may restrict bar visibility.", 13 | "Bar1PermissionN": "Player Bar 1 Permissions", 14 | "Bar1PermissionH": "Define what circumstances to show bar 1 to players. All: show bars on all Combatants. Owned: bars shown for any tokens the player owns. Observer: bars shown for any tokens the player has observer permission on. None: Do not show players any bars.", 15 | "ShowOverlayN": "Show Overlay", 16 | "ShowOverlayH": "Shows overlay (if configured). Options are: Always: always show the overlay. Active Combatant: only show the overlay for the Active Combatant. Hover: only show the overlay on hover. Never: never show the overlay. Note: additional GM settings may restrict overlay visibility.", 17 | "ShowEffectsN": "Show Effects", 18 | "ShowEffectsH": "Shows Active Effects in the Carousel. Works best if Active Effects are used by conditions and similar purposes. All: Show All Effects. None: Show No Effects. Active: Show Only Active Effects. Active and Passive Effects: Show Effects which are either Active or Passive (ie. ignore Inactive Effects).", 19 | "OverlayPermissionN": "Player Overlay Permissions", 20 | "OverlayPermissionH": "Define what circumstances to show Overlay to players. All: show overlay on all Combatants. Owner: overlay shown for any tokens the player owns. Observer: overlay shown for any tokens the player has observer permission on. Limited: overlay shown for any tokens the player has limited permission on. None: Do not show players any overlay.", 21 | "Bar1AttributeN": "Bar 1 Attribute", 22 | "Bar1AttributeH": "Enter a path to an Actor attribute to use for the bar. Single value attributes will show a single number. Attributes with a value and a max property will show a meter that depletes as the value decreases (eg. health).", 23 | "Bar1TitleN": "Bar 1 Name", 24 | "Bar1TitleH": "The name to be displayed on the bar (eg. HP)", 25 | "ShowInitiativeN": "Show Initiative", 26 | "ShowInitiativeH": "Shows initiative value (if any). Options are: Always: always show the value (if any). Active Combatant: only show the value for the Active Combatant. Hover: only show the value on hover. Never: never show the value.", 27 | "ShowInitiativeIconN": "Show Initiative Icon", 28 | "ShowInitiativeIconH": "Shows initiative icon (if any). Options are: Always: always show the icon (if any). With Initiative Value: show icon when initiative is shown. For Unrolled Combatants: show icon when combatant has no initiative value. Never: never show the icon", 29 | "InitiativePermissionN": "Player Initiative Permission", 30 | "InitiativePermissionH": "Define what circumstances to show Initiative to players. All: show initiative on all Combatants. Owner: initiative shown for any tokens the player owns. Observer: initiative shown for any tokens the player has observer permission on. Limited: overlay shown for any tokens the player has limited permission on. None: Do not show players any initiative.", 31 | "ImageTypeN": "Combatant Image", 32 | "ImageTypeH": "Select which Image to use for Combatants in the Carousel. Options are: Actor: the Actor's image. Token Actor: the Token Actor's image (will show Actor image if Token Actor image not set). Token: The token's image", 33 | "CarouselSizeN": "Carousel Size", 34 | "CarouselSizeH": "The size of the Carousel on the screen. HUD/Control elements are not scaled.", 35 | "ControlActiveCombatantTokenN": "Control Active Combatant's Token", 36 | "ControlActiveCombatantTokenH": "Enable to automatically control (select) the token associated to the currently Active Combatant.", 37 | "PanOnClickN": "Pan on Card Click", 38 | "PanOnClickH": "Pans to the matching token when a card is clicked (note: the token must be 'visible' to the current user)", 39 | "AlwaysOnTopN": "Always On Top", 40 | "AlwaysOnTopH": "Always render the Carousel above other UI elements.", 41 | "OpenOnCombatCreateN": "Open Combat Carousel on Combat Creation", 42 | "OpenOnCombatCreateH": "Opens the Carousel when a Combat is created" 43 | }, 44 | 45 | "CAROUSEL": { 46 | "BeginCombat": "Begin Combat", 47 | "NextTurn": "Next Turn", 48 | "PreviousTurn": "Previous Turn", 49 | "NextRound": "Next Round", 50 | "PreviousRound": "Previous Round", 51 | "NextRoundTooltip": "Click to proceed to Next Round", 52 | "PreviousRoundTooltip": "Click to return to Previous Round", 53 | "ToggleHint": "Click to hide/unhide, Right-click for config", 54 | "EndEncounter": "Click to End Encounter", 55 | "DragTooltip": "Click and drag to move, right-click to reset position", 56 | 57 | "Controls.CreateEncounter": "Create Encounter", 58 | "Controls.DeleteEncounter": "Delete Encounter", 59 | "Controls.NextEncounter": "Next Encounter", 60 | "Controls.PreviousEncounter": "Previous Encounter", 61 | "Controls.RollAll": "Roll All", 62 | "Controls.RollNPCs": "Roll NPCs", 63 | "Controls.ResetAll": "Reset All", 64 | "Controls.EncounterConfig": "Combat Tracker Settings" 65 | }, 66 | 67 | "COMBATANT_CARD": { 68 | "ActiveTurn": "Active Turn", 69 | "Controls.Hide": "Toggle Visibility", 70 | "Controls.Defeated": "Mark Defeated", 71 | "Controls.Remove": "Remove from Encounter", 72 | "RollInitiative": "Click to Roll", 73 | "EditInitiative": "Click to Edit, Right-Click to reset (GM Only)" 74 | }, 75 | 76 | "ABOUT.Label": "About Combat Carousel", 77 | 78 | "CONFIG_FORM.ResourceBars": "Resource Bars", 79 | "CONFIG_FORM.ResourceBar1": "Resource Bar 1", 80 | 81 | "OVERLAY_CONFIG.Title": "Combat Carousel | Overlay Config", 82 | "OVERLAY_CONFIG.Name": "Overlay Config", 83 | 84 | "TOGGLE_BUTTON.Tooltip": "Combat Carousel - click to toggle visibility, right-click to reset position", 85 | 86 | "WORDS": { 87 | "Save": "Save", 88 | "Cancel": "Cancel", 89 | "Hidden": "Hidden", 90 | "Defeated": "Defeated", 91 | "Encounter": "Encounter", 92 | "Round": "Round", 93 | "Turn": "Turn", 94 | "Property": "Property", 95 | "Name": "Name", 96 | "Value": "Value", 97 | "BrowseFiles": "Browse Files" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lang/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "I18N": { 3 | "LANGUAGE": "Español", 4 | "MAINTAINERS": "@Viriato139ac#0342" 5 | }, 6 | 7 | "COMBAT_CAROUSEL": { 8 | "SETTINGS": { 9 | "AboutN": "Acerca de", 10 | "AboutH": "Información acerca de Combat Carousel (Carrusel de Combate)", 11 | "EnableModuleN": "Habilitar Combat Carousel", 12 | "EnableModuleH": "Habilita las funcionalidades del módulo", 13 | "OverlayConfigH": "Abrir formulario de configuración de tarjeta de contendiente", 14 | "CollapseNavN": "Ocultar la barra de navegación", 15 | "CollapseNavH": "Pliega la barra de navegación al mostrar el Carrusel de Combate", 16 | "ShowBar1N": "Mostrar barra 1", 17 | "ShowBar1H": "Muestra la barra de recursos (si está configurada). Opciones: Siempre: mostrar siempre la barra (si existe alguna). Contendiente activo: solo mostrar la barra del contendiente activo. Al pasar el ratón: mostrar solo la barra al pasar el ratón por encima. Nunca: no mostrar nunca las barras. Nota: es posible que otros ajustes del GM puedan restringir la visibilidad de las barras", 18 | "Bar1PermissionN": "Permisos de jugador de la barra 1", 19 | "Bar1PermissionH": "Define las circunstancias en las que se muestra la barra 1 a los jugadores. Todos: mostrar las barras de todos los contendientes. Propios: se mostrarán solo las barras de los iconos para los que el jugador tenga permisos de propietario. Observador: bse mostrarán solo las barras de los iconos para los que el jugador tenga permisos de observador. Ninguno: no mostrar ninguna barra a los jugadores", 20 | "ShowOverlayN": "Mostrar tarjeta", 21 | "ShowOverlayH": "Muestra la tarjeta (si se ha configurado). Opciones: Siempre: mostrar siempre la tarjeta. Contendiente activo: solo mostrar la tarjeta del contendiente activo. Al pasar el ratón: mostrar solo la tarjeta al pasar el ratón por encima. Nunca: no mostrar nunca las tarjetas. Nota: es posible que otros ajustes del GM puedan restringir la visibilidad de las tarjetas", 22 | "ShowEffectsN": "Mostrar efectos", 23 | "ShowEffectsH": "Mostrar los efectos activos en el Carrusel. Funciona mejor si los efectos activos se usan para mostrar condiciones u otro propósito similar. All: Muestra todos los efectos. None: No muestra ninguno. Active: Solo los efectos activos. Active and Passive Effects: Muestra los efectos que son o bien activos o pasivos (esto es, ignora los efectos no activos)", 24 | "OverlayPermissionN": "Permisos de jugador de la tarjeta", 25 | "OverlayPermissionH": "Define las circunstancias en las que se muestra la tarjeta a los jugadores. Todos: mostrar las tarjetas de todos los contendientes. Propios: se mostrarán solo las tarjetas de los iconos para los que el jugador tenga permisos de propietario. Observador: bse mostrarán solo las tarjetas de los iconos para los que el jugador tenga permisos de observador. Ninguno: no mostrar ninguna tarjeta a los jugadores", 26 | "Bar1AttributeN": "Atributo de la barra 1", 27 | "Bar1AttributeH": "Ruta el atributo de actor que se debe usar en la barra. Si solo se pone un atributo con valor único solo se mostrará un único valor. Si se especifica un atributo con un valor que tenga una propiedad de máximo se mostrará un medidor que se irá vaciando a conforme el valor desciende (p.ej. salud)", 28 | "Bar1TitleN": "Nombre de la barra 1", 29 | "Bar1TitleH": "Nombre a mostrar en la barra (p.ej. PV)", 30 | "ShowInitiativeN": "Mostrar iniciativa", 31 | "ShowInitiativeH": "Mostrar el valor de iniciativa (si es que existe). Opciones: Siempre: mostrar siempre el valor (si existe). Contendiente activo: solo mostrar el valor del contendiente activo. Al pasar el ratón: mostrar solo el valor al pasar el ratón por encima. Nunca: no mostrar nunca el valor", 32 | "ShowInitiativeIconN": "Mostrar icono de iniciativa", 33 | "ShowInitiativeIconH": "Muestra el icono de iniciativa (si es que existe). Opciones: Siempre: mostrar siempre el icono (si existe). Junto con el valor de iniativa: muestra el icono cuando se muestra la iniciativa. Solo los contendientes que no han tirado: muestra los iconos cuando el contendiente no tiene valor de iniciativa. Nunca: no mostrar nunca el icono", 34 | "InitiativePermissionN": "Permisos de jugador de la iniciativa", 35 | "InitiativePermissionH": "Define las circunstancias en las que se muestra la iniciativa a los jugadores. Todos: mostrar las iniciativas de todos los contendientes. Propios: se mostrarán solo las iniciativas de los iconos para los que el jugador tenga permisos de propietario. Observador: bse mostrarán solo las iniciativas de los iconos para los que el jugador tenga permisos de observador. Ninguno: no mostrar ninguna iniciativa a los jugadores", 36 | "ImageTypeN": "Imagen de contendiente", 37 | "ImageTypeH": "Seleccione la imagen que debe usarse para los contendientes en el carrusel. Opciones: Actor: la imagen del actor. Icono del actor: la imagen del icono del actor (mostrará la imagen del actor si no se ha configurado una imagen de icono). Icono: la imagen del icono", 38 | "CarouselSizeN": "Tamaño del carrusel", 39 | "CarouselSizeH": "El tamaño del carrusel en la pantalla. No se rescalarán los elementos de control ni el HUD", 40 | "ControlActiveCombatantTokenN": "Controlar icono del contendiente activo", 41 | "ControlActiveCombatantTokenH": "Habilita el control automático (seleccionar) del icono asociado al contendiente activo actualmente", 42 | "PanOnClickN": "Centrar al hacer clic en una tarjeta", 43 | "PanOnClickH": "Se centra en el icono correspondiente cuando se hace clic en una tarjeta (nota: el icono debe ser 'visible' para el usuario actual)", 44 | "AlwaysOnTopN": "Siempre visible", 45 | "AlwaysOnTopH": "Mostrar el carrusel siempre encima del resto de elementos de la UI", 46 | "OpenOnCombatCreateN": "Abrir Combat Carousel al crear un combate", 47 | "OpenOnCombatCreateH": "Abre el carrusel cuando se crea un combate" 48 | }, 49 | 50 | "CAROUSEL": { 51 | "BeginCombat": "Comenzar combate", 52 | "NextTurn": "Turno siguiente", 53 | "PreviousTurn": "Turno anterior", 54 | "NextRound": "Asalto siguiente", 55 | "PreviousRound": "Asalto anterior", 56 | "NextRoundTooltip": "Clic para pasar al asalto siguiente", 57 | "PreviousRoundTooltip": "Clic para volver al asalto anterior", 58 | "ToggleHint": "Clic para ocultar/mostrar, clic derecho para ajustes", 59 | "EndEncounter": "Clic para finalizar el encuentro", 60 | "DragTooltip": "Clic y arrastre para mover, clic derecho para volver a la posición original", 61 | 62 | "Controls.CreateEncounter": "Crear encuentro", 63 | "Controls.DeleteEncounter": "Borrar encuentro", 64 | "Controls.NextEncounter": "Encuentro siguiente", 65 | "Controls.PreviousEncounter": "Encuentro anterior", 66 | "Controls.RollAll": "Tirar iniciativa", 67 | "Controls.RollNPCs": "Tirar para PNJs", 68 | "Controls.ResetAll": "Reiniciar iniciativas", 69 | "Controls.EncounterConfig": "Ajustes del asistente de combate" 70 | }, 71 | 72 | "COMBATANT_CARD": { 73 | "ActiveTurn": "Turno activo", 74 | "Controls.Hide": "Alternar visibilidad", 75 | "Controls.Defeated": "Marcar como derrotado", 76 | "Controls.Remove": "Quitar del encuentro", 77 | "RollInitiative": "Clic para tirar", 78 | "EditInitiative": "Clic para editar, clic derecho para reiniciar (solo GM)" 79 | }, 80 | 81 | "ABOUT.Label": "Acerca de Combat Carousel (Carrusel de Combate)", 82 | 83 | "CONFIG_FORM.ResourceBars": "Barras de recursos", 84 | "CONFIG_FORM.ResourceBar1": "Barra de recursos 1", 85 | 86 | "OVERLAY_CONFIG.Title": "Carrusel de Combate | Configuración de tarjeta", 87 | "OVERLAY_CONFIG.Name": "Configuración de tarjeta", 88 | 89 | "TOGGLE_BUTTON.Tooltip": "Carrusel de Combate - clic para alternar visibilidad, clic derecho para reiniciar posición", 90 | 91 | "WORDS": { 92 | "Save": "Guardar", 93 | "Cancel": "Cancelar", 94 | "Hidden": "Oculto", 95 | "Defeated": "Derrotado", 96 | "Encounter": "Encuentro", 97 | "Round": "Asalto", 98 | "Turn": "Turno", 99 | "Property": "Propiedad", 100 | "Name": "Nombre", 101 | "Value": "Valor", 102 | "BrowseFiles": "Explorador de archivos" 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /libs/splide/js/splide-render.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Splide.js 3 | * Version : 4.1.3 4 | * License : MIT 5 | * Copyright: 2022 Naotoshi Fujita 6 | */ 7 | var t,n;t=this,n=function(){"use strict";var s="rtl",u="ttb",o={width:["height"],left:["top","right"],right:["bottom","left"],x:["y"],X:["Y"],Y:["X"],ArrowLeft:["ArrowUp","ArrowRight"],ArrowRight:["ArrowDown","ArrowLeft"]};function e(t,n,e){return{resolve:function(t,n,i){var r=(i=i||e.direction)!==s||n?i===u?0:-1:1;return o[t]&&o[t][r]||t.replace(/width|left|right/i,function(t,n){t=o[t.toLowerCase()][r]||t;return 0 img","display",t.cover?"none":"inline",n)})},n.buildTranslate=function(t){var n=this.Direction,i=n.resolve,n=n.orient,r=[];return r.push(this.cssOffsetClones(t)),r.push(this.cssOffsetGaps(t)),this.isCenter(t)&&(r.push(this.buildCssValue(n(-50),"%")),r.push.apply(r,this.cssOffsetCenter(t))),r.filter(Boolean).map(function(t){return"translate"+i("X")+"("+t+")"}).join(" ")},n.cssOffsetClones=function(t){var n,i=this.Direction,r=i.resolve,i=i.orient,e=this.getCloneCount();return this.isFixedWidth(t)?(n=(r=this.parseCssValue(t[r("fixedWidth")])).value,r=r.unit,this.buildCssValue(i(n)*e,r)):i(100*e/t.perPage)+"%"},n.cssOffsetCenter=function(t){var n,i,r=this.Direction,e=r.resolve,r=r.orient;return this.isFixedWidth(t)?(n=(e=this.parseCssValue(t[e("fixedWidth")])).value,e=e.unit,[this.buildCssValue(r(n/2),e)]):(n=t.perPage,e=t.gap,(t=[]).push(r(50/n)+"%"),e&&(i=(e=this.parseCssValue(e)).value,e=e.unit,t.push(this.buildCssValue(r((i/n-i)/2),e))),t)},n.cssOffsetGaps=function(t){var n,i,r,e=this.getCloneCount();return e&&t.gap?(n=this.Direction.orient,i=(r=this.parseCssValue(t.gap)).value,r=r.unit,this.isFixedWidth(t)?this.buildCssValue(n(i*e),r):(t=t.perPage,this.buildCssValue(n(e/t*i),r))):""},n.resolve=function(t){return T(this.Direction.resolve(t))},n.cssPadding=function(t,n){t=t.padding,n=this.Direction.resolve(n?"right":"left",!0);return t&&G(t[n]||(b(t)?0:t))||"0px"},n.cssTrackHeight=function(t){var n="";return this.isVertical()&&(O(n=this.cssHeight(t),'"height" is missing.'),n="calc("+n+" - "+this.cssPadding(t,!1)+" - "+this.cssPadding(t,!0)+")"),n},n.cssHeight=function(t){return G(t.height)},n.cssSlideWidth=function(t){return t.autoWidth?"":G(t.fixedWidth)||(this.isVertical()?"":this.cssSlideSize(t))},n.cssSlideHeight=function(t){return G(t.fixedHeight)||(this.isVertical()?t.autoHeight?"":this.cssSlideSize(t):this.cssHeight(t))},n.cssSlideSize=function(t){var n=G(t.gap);return"calc((100%"+(n&&" + "+n)+")/"+(t.perPage||1)+(n&&" - "+n)+")"},n.cssAspectRatio=function(t){t=t.heightRatio;return t?""+1/t:""},n.buildCssValue=function(t,n){return""+t+n},n.parseCssValue=function(t){return y(t)?{value:parseFloat(t)||0,unit:t.replace(/\d*(\.\d*)?/,"")||"px"}:{value:t,unit:"px"}},n.parseBreakpoints=function(){var i=this,t=this.options.breakpoints;this.breakpoints.push(["default",this.options]),t&&P(t,function(t,n){i.breakpoints.push([n,X(X({},i.options),t)])})},n.isFixedWidth=function(t){return!!t[this.Direction.resolve("fixedWidth")]},n.isLoop=function(){return"loop"===this.options.type},n.isCenter=function(t){if("center"===t.focus){if(this.isLoop())return!0;if("slide"===this.options.type)return!this.options.trimSpace}return!1},n.isVertical=function(){return this.options.direction===u},n.buildClasses=function(){var t=this.options;return[r,r+"--"+t.type,r+"--"+t.direction,t.drag&&r+"--draggable",t.isNavigation&&r+"--nav",d,!this.config.hidden&&"is-rendered"].filter(Boolean).join(" ")},n.buildAttrs=function(t){var i="";return P(t,function(t,n){i+=t?" "+T(n)+'="'+t+'"':""}),i.trim()},n.buildStyles=function(t){var i="";return P(t,function(t,n){i+=" "+T(n)+":"+t+";"}),i.trim()},n.renderSlides=function(){var n=this,i=this.config.slideTag;return this.slides.map(function(t){return"<"+i+" "+n.buildAttrs(t.attrs)+">"+(t.html||"")+""}).join("")},n.cover=function(t){var n=t.styles,t=t.html,t=void 0===t?"":t;this.options.cover&&!this.options.lazyLoad&&(t=t.match(//))&&t[2]&&(n.background="center/cover no-repeat url('"+t[2]+"')")},n.generateClones=function(r){for(var e=this.options.classes,s=this.getCloneCount(),t=r.slice();t.length')+this.renderArrow(!0)+this.renderArrow(!1)+""},n.renderArrow=function(t){var n=this.options,i=n.classes,n=n.i18n,i={class:i.arrow+" "+(t?i.prev:i.next),type:"button",ariaLabel:t?n.prev:n.next};return"'},n.html=function(){var t=this.config,n=t.rootClass,i=t.listTag,r=t.arrows,e=t.beforeTrack,s=t.afterTrack,u=t.slider,o=t.beforeSlider,t=t.afterSlider,a="";return a=(a+='
    ')+(""),u&&(a=a+(o||"")+'
    '),a+=e||"",r&&(a+=this.renderArrows()),a=(a=(a=a+'
    '+("<"+i+' class="splide__list">'))+this.renderSlides()+(""))+"
    "+(s||""),u&&(a=a+"
    "+(t||"")),a+="
    "},t}()},"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t="undefined"!=typeof globalThis?globalThis:t||self).SplideRenderer=n(); 8 | //# sourceMappingURL=splide-renderer.min.js.map -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Known Issues 4 | - Tested (almost) exclusively with `dnd5e` system -- other systems may encounter errors -- please report in Issue Log 5 | - Initiative icon assumes `d20`-based initiative 6 | - Some strings are still not setup for translation 7 | - When multiple combatants are added to combat at the same time, and the `Control Active Combatant` setting is enabled, there will be an error in console. It doesn't appear to prevent any Carousel behaviour. 8 | - Currently only the attribute set as the 1st Token bar is able to be set as the Carousel 1st bar (eg. if your Token config is set to `system.attributes.hp` then this is the only attribute Combat Carousel will work with for now) 9 | 10 | ## [0.3.3] - 2023-04-08 11 | - Fixed issue with rolling initiative caused by fixing issue with editing initiative in the previous update. For every bug we fix, we must put at least 2 bugs back in. 12 | - Changes to Actor-linked token bar values (eg. HP) will now show in the Carousel again 13 | - Fixed CSS issue with bar1 colouring in webkit (Chrome) browsers for sub-sub-optimum values 14 | 15 | ## [0.3.2] - 2023-02-26 16 | > This update adds compatibility for Foundry VTT v10.291 17 | - Fixed issue with editing initiative 18 | - Make Roll All / Roll NPCs buttons respect **PF2e** Roll Options Dialog setting 19 | - **Spanish** translation updated (thanks @lozanoje ! 🎉) 20 | 21 | ## [0.3.1] - 2022-11-12 22 | > This update adds compatibility for Foundry VTT v10.290 23 | - Combat Carousel now supports combatants with no Actor/Token (thanks @BoltsJ! 🎉) 24 | - Changed the behaviour of the Next Round button on Round 0: it now shows a `B` (for "Begin Combat") and correctly calls the matching functionality in Foundry's native tracker 25 | - Fixed some issues with Combatant visibility (thanks @DavidAremaCarretero! 🎉) 26 | - PF2e: the roll Initiative dialog setting is now respected (eg. dialog won't be shown if it is turned off) 27 | - Made reference to `denim075.png` relative in CSS so that it should work with a wider range of server configurations 28 | - Updated the Splide library to v4.1.3. This should fix some minor UI bugs with Splide 29 | 30 | ## [0.3.0] - 2022-08-12 31 | > This update adds compatibility for Foundry VTT v10 32 | 33 | - Updated to respect PF1E's 'Hide From Tokens' setting on buffs. (thanks @Fair-Strides 🎉) 34 | - Allow Carousel updates to occur even when there is no active combatant (thanks @BoltsJ 🎉) 35 | - The Carousel will now refresh when Active Effects are updated 36 | - Ensure user has permission before controlling token when turn changes 37 | - **Japanese** translation updated (thanks @brothersharper 🎉) 38 | - **Spanish** translation updated (thanks @lozalojo ! 🎉) 39 | 40 | ## [0.2.5] - 2021-12-27 41 | > This update adds compatibility for Foundry VTT V9 42 | 43 | - Combat Carousel no longer throws an error if you have a world with no active combat 44 | - Spanish translation update (again, thanks @lozalojo 🎉) 45 | - Nav bar automagically re-expands after combat if the collapse setting is enabled (thanks @sirrus233 🎉) 46 | - Added a setting to open the Carousel on combat creation 47 | - Lengthened the delay for the fly-in effect on cards when the Carousel is opened 48 | - Occasionally the Carousel got confused about its collapsed/expanded state. Should happen less now 49 | - Added additional update properties for actors and tokens that cause the Carousel UI to update 50 | - Carousel now collapses when combat is deleted 51 | 52 | ## [0.2.3] - 2021-10-11 53 | - Spanish translation updated (thanks @lozalojo ! 🎉) 54 | - Added options for `Limited` Actor permission to settings that support permissions (thanks @SovietVVinter ! 🎉) 55 | - Hovering combatant cards in the carousel hovers the token again 🛸 56 | - Panning for gold is fun, but panning to the active combatant might not be for you, so there's now a setting for that! 57 | - Clicking a combatant card now bubbles that click to the matching token, which provides better support for modules that override the native left click 58 | - Added support for the 🐛`Bug Reporter` module! 59 | 60 | ## [0.2.2] - 2021-09-03 61 | - The evil incantation causing **hidden combatants** to be revealed has been dispelled. GMs: rest easy knowing your secret invisible party-killing superbeast surprise is no longer spoiled. (thanks @JamzTheMan 🎉) 62 | - For SOME REASON some systems don't use `attributes.hp` for health bars. Weird huh! Combat Carousel works with these systems again. 63 | - Combatants who don't make the cut can be **deleted** from the Carousel again. (Yikes Roger, you really screwed up that death save...) 64 | - - ...also added a **confirmation dialog** in case you accidentally click the wrong combatant (like anyone except Roger...) 65 | - Sidebar hidden (eg. by some other module) but the **Carousel doesn't fill the space**? Now it does! (thanks @sPOIDar 🎉) 66 | - **No combat**? No problem (any longer)! (thanks @DavidAremaCarretero 🎉) 67 | - Apparently having every single Active Effect on the Combatant Card wasn't desirable in all situations (PF2e users I see you!). Well now you can **choose which Effects to show** (All, Temporary + Passive, Temporary Only, Passive Only) (thanks @Drental 🎉 for contributing code to this) 68 | - In case you don't like to be in control, there's now a setting to control whether you **control the active Combatant** when it changes 69 | - Updated **Spanish** translation (thanks @lozalojo 🎉) 70 | - Updated **Japanese** translation (thanks `touge` and @brothersharper 🎉) 71 | - Confirmed compatibility with Foundry VTT 0.8.9 72 | 73 | ## [0.2.1] - 2021-07-06 74 | > This release marks the public beta for the 0.2.x series of Combat Carousel. 75 | - Added compatibility for Foundry VTT v0.8.x 76 | - You can now disable the module even if your GM was nice enough to install and enable it! Simply uncheck the `Enable Combat Carousel` setting to disable its functionality... you monster. 77 | - Added `Combatant` option for **Combat Carousel** card images. This will use the image you've set for combatant--overriding their token image--but defaults to the token image if none exists. 78 | - Carousel is no longer forcefully rendered during combat updates *except* when Combat Carousel is enabled and combat is created 79 | - In case you have second thoughts you now get a prompt (same as core Foundry) when deleting a combat/encounter. 80 | - **Combat Carousel** should be approximately 43.51629% more efficient due to more stringent checks when processing actor/token updates. 81 | - Active/Status Effects are visible again. Yes Gary... for the last time, your character is still petrified! 82 | 83 | ## [0.2.0] - 2021-01-24 84 | - The Carousel can now be moved ⬆↗⬅↘↖⬇➡↙: 85 | - - Use the `Drag Handle` ≡ to move the Carousel 86 | - - Carousel position is saved when you finish dragging 87 | - - The Carousel will automatically expand to the right based on the number of Combatants in the combat. 88 | - - ...but the Carousel will automatically resize itself when it butts up against the Sidebar (expanded or collapsed). Just imagine it's magnetised or something. 89 | - Oh and you can also change the overall size of the Carousel! (choose from 4 preset sizes: `Large`, `Medium`, `Small`, `Extra Small`) 90 | - - Note: the HUD elements do not resize. 91 | - The **Combat Carousel** button has been moved to the Scene Controls. The button has the following magic powers: 92 | - - 🖱 Left-click to shown/hide the Carousel 93 | - - 🖱 Right-click to reset the Carousel position on the screen (top-left corner by default) 94 | - - The icons for Carousel state remain the same: unlit empty Carousel for No Combat, lit empty Carousel for Combat with no Combatants, lit full Carousel for Combat with Combatants 95 | - - The `Collapse Indicator` triangle 🔼 shows the state of the Carousel: down for hidden, up for shown 96 | - GMs can control the Encounter directly from the Carousel (ie. Roll, and Reset Initiative, Create and Delete Encounters) 97 | - The Carousel should correctly detect the combat (if any) on the currently viewed Scene 98 | - The `Active Combatant` indicator is now an orange-red triangle 🔻 on the bottom of the `Combatant Card` 99 | - The `Combatant Card` image is now globally configurable. Choose from: `Actor`, `Token Actor`, or `Token` 100 | - - The image is now resized to fit the vertical space of the container 101 | - `Pagination Dots` now show below the Carousel and allow you to jump to a specific `Combatant Card` without changing the `Active Combatant` 102 | - 👉Interacting with the Carousel has changed: 103 | - - The Carousel `HUD` is hidden until the mouse enters its space. 104 | - - 🖱 Left-click the `Encounter Controls` to perform the actions therein (eg. Roll All NPCs) 105 | - - 🖱 Left-click and drag the `Drag Handle` ≡ to move the Carousel 106 | - - 🖱 Right-click the `Drag Handle` ≡ to reset the Carousel position 107 | - 👉Interacting with the Combatant Cards has also changed: 108 | - - 🖱 Left-click to control the token and pan the canvas to it 109 | - - `CTRL` + 🖱 Left-click to change the Active Combatant 110 | - 👉Interacting with the Initiative has changed: 111 | - - 🖱 Left-click the Initiative icon to roll initiative 112 | - - 🖱 Left-click the Initiative Value to edit it 113 | - - 🖱 Right-click the Initiative Value to reset it 114 | - The Overlay Config is now available in the Module Settings 115 | - The `Resource Bar` Attribute and Name are now configurable! The default is `attributes.hp`, `HP` 116 | - A range of configuration/permission options have been added to the Module Settings, including: 117 | - - Overlay Visibility and Permission 118 | - - Initiative Visibility and Permission 119 | - - Resource Bar Visibility, Attribute, Name, and Permission 120 | - Hidden Combatants no longer cause the incorrect Combatant Card to be shown as Active 121 | - Changelog format switched to a more conversational, feature-based style 122 | 123 | ## [0.1.3] - 2020-10-12 124 | ### Added 125 | - You can now End a Combat Encounter by clicking the Encounter icon (fist) on the right side of the Carousel 126 | 127 | ### Changed 128 | - The Active Turn in the current Combat is now indicated by a red bookmark icon at the top of the card (the orange border around card has been removed) 129 | - Interacting with the Combatant Card has changed: 130 | - - Clicking on the card now `selects` / `controls` the matching token 131 | - - Right-clicking on the card now sets that Combatant as the Active Turn in Combat 132 | - - Double-clicking on the card still opens the Actor sheet 133 | 134 | ### Fixed 135 | - Players can no longer reroll initiative 136 | - Players can no longer double-click a Combatant Card to open an Actor sheet which they do not own 137 | - The Combatant Control buttons (which appear when you hover over a Combatant name) are now brighter 138 | - Combat Tracker Indicators (Defeated, and Visibility) now have a background so they are easier to see 139 | 140 | ## [0.1.2] - 2020-09-29 141 | ### Fixed 142 | - Players can now roll initiative again 143 | - Players can no longer change the active combatant by clicking a Combatant Card 144 | 145 | ## [0.1.1] - 2020-09-29 146 | ### Added 147 | - Translations: 148 | - - Español (Spanish) by Viriato139ac#0342 149 | - - Français (French) by Meï#4242 150 | - - 日本語 (Japanese) by Brother Sharp#6921 151 | - - português do Brasil (Brazilian Portuguese) by Miriadis#9152 152 | - Additional translation strings 153 | 154 | ### Changed 155 | - The Read Me link in the About (in Module Settings) now links to the Public Wiki instead 156 | - Patron list is now generated programmatically using the Patreon API 157 | 158 | ### Fixed 159 | - Removed the ability for players to control combat via the Combat Controls (Next/Previous Turn/Round) -- players can still advance the Turn on their Turn 160 | - Removed the ability for players to reroll initiative -- a future update will provide a setting so GMs can toggle this behaviour if they want 161 | 162 | ## [0.1.0] - 2020-09-26 163 | ### Added 164 | - Initial release 165 | 166 | ### Changed 167 | ### Fixed -------------------------------------------------------------------------------- /modules/settings.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Settings module 3 | * @module settings 4 | */ 5 | 6 | import AboutApp from "./about.mjs"; 7 | import CombatCarousel from "./combat-carousel.mjs"; 8 | import CombatCarouselConfig from "./config-form.mjs"; 9 | import { DEFAULT_CONFIG } from "./config.mjs"; 10 | import { SETTING_KEYS } from "./config.mjs"; 11 | import { NAME } from "./config.mjs"; 12 | import { getKeyByValue } from "./util.mjs"; 13 | 14 | /** 15 | * Wrapper to call settings registration 16 | */ 17 | export default function registerSettings() { 18 | 19 | /* -------------------------------------------------------------------------- */ 20 | /* Menus */ 21 | /* -------------------------------------------------------------------------- */ 22 | 23 | game.settings.registerMenu(NAME, SETTING_KEYS.about, { 24 | name: "COMBAT_CAROUSEL.SETTINGS.AboutN", 25 | label: "COMBAT_CAROUSEL.ABOUT.Label", 26 | hint: "COMBAT_CAROUSEL.SETTINGS.AboutH", 27 | icon: "fas fa-question", 28 | type: AboutApp, 29 | restricted: false 30 | }); 31 | 32 | game.settings.registerMenu(NAME, SETTING_KEYS.overlayConfigMenu, { 33 | name: "COMBAT_CAROUSEL.OVERLAY_CONFIG.Name", 34 | label: "COMBAT_CAROUSEL.OVERLAY_CONFIG.Name", 35 | hint: "COMBAT_CAROUSEL.SETTINGS.OverlayConfigH", 36 | icon: "fas fa-th-list", 37 | type: CombatCarouselConfig, 38 | restricted: true 39 | }); 40 | 41 | /* -------------------------------------------------------------------------- */ 42 | /* Basic Settings */ 43 | /* -------------------------------------------------------------------------- */ 44 | 45 | game.settings.register(NAME, SETTING_KEYS.enabled, { 46 | name: "COMBAT_CAROUSEL.SETTINGS.EnableModuleN", 47 | hint: "COMBAT_CAROUSEL.SETTINGS.EnableModuleH", 48 | scope: "client", 49 | type: Boolean, 50 | default: true, 51 | config: true, 52 | onChange: async (s) => { 53 | if (s) { 54 | const controlsHtml = ui.controls.element; 55 | await CombatCarousel._onRenderSceneControls(null, controlsHtml, null); 56 | return CombatCarousel._onReady(); 57 | } 58 | 59 | if (s === false) { 60 | if (ui.combatCarousel?.rendered) ui.combatCarousel.close(); 61 | const controlsHtml = ui.controls.element; 62 | const ccButton = controlsHtml.find("li[data-control='combat-carousel']"); 63 | ccButton.remove(); 64 | } 65 | } 66 | }); 67 | 68 | game.settings.register(NAME, SETTING_KEYS.collapseNav, { 69 | name: "COMBAT_CAROUSEL.SETTINGS.CollapseNavN", 70 | hint: "COMBAT_CAROUSEL.SETTINGS.CollapseNavH", 71 | scope: "client", 72 | type: Boolean, 73 | default: false, 74 | config: true, 75 | onChange: s => { 76 | 77 | } 78 | }); 79 | 80 | game.settings.register(NAME, SETTING_KEYS.openOnCombatCreate, { 81 | name: "COMBAT_CAROUSEL.SETTINGS.OpenOnCombatCreateN", 82 | hint: "COMBAT_CAROUSEL.SETTINGS.OpenOnCombatCreateH", 83 | scope: "client", 84 | type: Boolean, 85 | default: false, 86 | config: true, 87 | onChange: s => { 88 | 89 | } 90 | }); 91 | 92 | game.settings.register(NAME, SETTING_KEYS.carouselSize, { 93 | name: "COMBAT_CAROUSEL.SETTINGS.CarouselSizeN", 94 | hint: "COMBAT_CAROUSEL.SETTINGS.CarouselSizeH", 95 | scope: "client", 96 | type: String, 97 | default: getKeyByValue(DEFAULT_CONFIG.carouselSize.choices, DEFAULT_CONFIG.carouselSize.choices.med), 98 | choices: DEFAULT_CONFIG.carouselSize.choices, 99 | config: true, 100 | onChange: async s => { 101 | if (ui.combatCarousel?.rendered) { 102 | await ui.combatCarousel.render(true); 103 | ui.combatCarousel.element.addClass(s); 104 | } 105 | } 106 | }); 107 | 108 | game.settings.register(NAME, SETTING_KEYS.imageType, { 109 | name: "COMBAT_CAROUSEL.SETTINGS.ImageTypeN", 110 | hint: "COMBAT_CAROUSEL.SETTINGS.ImageTypeH", 111 | scope: "world", 112 | type: String, 113 | default: "actor", 114 | choices: DEFAULT_CONFIG.imageType.choices, 115 | config: true, 116 | onChange: s => { 117 | if (ui.combatCarousel?.rendered) ui.combatCarousel.render(true); 118 | } 119 | }); 120 | 121 | game.settings.register(NAME, SETTING_KEYS.controlActiveCombatantToken, { 122 | name: "COMBAT_CAROUSEL.SETTINGS.ControlActiveCombatantTokenN", 123 | hint: "COMBAT_CAROUSEL.SETTINGS.ControlActiveCombatantTokenH", 124 | scope: "client", 125 | type: Boolean, 126 | default: false, 127 | config: true, 128 | onChange: s => { 129 | } 130 | }); 131 | 132 | game.settings.register(NAME, SETTING_KEYS.panOnClick, { 133 | name: "COMBAT_CAROUSEL.SETTINGS.PanOnClickN", 134 | hint: "COMBAT_CAROUSEL.SETTINGS.PanOnClickH", 135 | scope: "client", 136 | type: Boolean, 137 | default: false, 138 | config: true, 139 | onChange: s => { 140 | } 141 | }); 142 | 143 | game.settings.register(NAME, SETTING_KEYS.alwaysOnTop, { 144 | name: "COMBAT_CAROUSEL.SETTINGS.AlwaysOnTopN", 145 | hint: "COMBAT_CAROUSEL.SETTINGS.AlwaysOnTopH", 146 | scope: "client", 147 | type: Boolean, 148 | default: false, 149 | config: true, 150 | onChange: s => { 151 | if (ui.combatCarousel?.rendered) ui.combatCarousel.render(true); 152 | } 153 | }); 154 | 155 | /* -------------------------------------------------------------------------- */ 156 | /* Overlay Settings */ 157 | /* -------------------------------------------------------------------------- */ 158 | 159 | game.settings.register(NAME, SETTING_KEYS.showOverlay, { 160 | name: "COMBAT_CAROUSEL.SETTINGS.ShowOverlayN", 161 | hint: "COMBAT_CAROUSEL.SETTINGS.ShowOverlayH", 162 | scope: "client", 163 | type: String, 164 | default: getKeyByValue(DEFAULT_CONFIG.showOverlay.choices, DEFAULT_CONFIG.showOverlay.choices.hover), 165 | choices: DEFAULT_CONFIG.showOverlay.choices, 166 | config: true, 167 | onChange: s => { 168 | ui.combatCarousel.render(true); 169 | } 170 | }); 171 | 172 | game.settings.register(NAME, SETTING_KEYS.overlayPermission, { 173 | name: "COMBAT_CAROUSEL.SETTINGS.OverlayPermissionN", 174 | hint: "COMBAT_CAROUSEL.SETTINGS.OverlayPermissionH", 175 | scope: "world", 176 | type: String, 177 | default: "owner", 178 | choices: DEFAULT_CONFIG.overlayPermission.choices, 179 | config: true, 180 | onChange: s => { 181 | if (!game.user.isGM) { 182 | ui.combatCarousel.render(true); 183 | } 184 | } 185 | }); 186 | 187 | game.settings.register(NAME, SETTING_KEYS.showEffects, { 188 | name: "COMBAT_CAROUSEL.SETTINGS.ShowEffectsN", 189 | hint: "COMBAT_CAROUSEL.SETTINGS.ShowEffectsH", 190 | scope: "world", 191 | type: String, 192 | default: "all", 193 | choices: DEFAULT_CONFIG.showEffects.choices, 194 | config: true, 195 | onChange: s => { 196 | ui.combatCarousel.render(true); 197 | } 198 | }); 199 | 200 | /* -------------------------------------------------------------------------- */ 201 | /* Initiative Settings */ 202 | /* -------------------------------------------------------------------------- */ 203 | 204 | game.settings.register(NAME, SETTING_KEYS.showInitiative, { 205 | name: "COMBAT_CAROUSEL.SETTINGS.ShowInitiativeN", 206 | hint: "COMBAT_CAROUSEL.SETTINGS.ShowInitiativeH", 207 | scope: "world", 208 | type: String, 209 | default: "always", 210 | choices: DEFAULT_CONFIG.showInitiative.choices, 211 | config: true, 212 | onChange: s => { 213 | ui.combatCarousel.render(true); 214 | } 215 | }); 216 | 217 | game.settings.register(NAME, SETTING_KEYS.showInitiativeIcon, { 218 | name: "COMBAT_CAROUSEL.SETTINGS.ShowInitiativeIconN", 219 | hint: "COMBAT_CAROUSEL.SETTINGS.ShowInitiativeIconH", 220 | scope: "world", 221 | type: String, 222 | default: "always", 223 | choices: DEFAULT_CONFIG.showInitiativeIcon.choices, 224 | config: true, 225 | onChange: s => { 226 | ui.combatCarousel.render(true); 227 | } 228 | }); 229 | 230 | game.settings.register(NAME, SETTING_KEYS.initiativePermission, { 231 | name: "COMBAT_CAROUSEL.SETTINGS.InitiativePermissionN", 232 | hint: "COMBAT_CAROUSEL.SETTINGS.InitiativePermissionH", 233 | scope: "world", 234 | type: String, 235 | default: "owned", 236 | choices: DEFAULT_CONFIG.initiativePermission.choices, 237 | config: true, 238 | onChange: s => { 239 | ui.combatCarousel.render(true); 240 | } 241 | }); 242 | 243 | /* -------------------------------------------------------------------------- */ 244 | /* Bar Settings */ 245 | /* -------------------------------------------------------------------------- */ 246 | 247 | game.settings.register(NAME, SETTING_KEYS.showBar1, { 248 | name: "COMBAT_CAROUSEL.SETTINGS.ShowBar1N", 249 | hint: "COMBAT_CAROUSEL.SETTINGS.ShowBar1H", 250 | scope: "client", 251 | type: String, 252 | choices: DEFAULT_CONFIG.showBar.choices, 253 | default: "always", 254 | config: true, 255 | onChange: s => { 256 | ui.combatCarousel.render(true); 257 | } 258 | }); 259 | 260 | game.settings.register(NAME, SETTING_KEYS.bar1Permission, { 261 | name: "COMBAT_CAROUSEL.SETTINGS.Bar1PermissionN", 262 | hint: "COMBAT_CAROUSEL.SETTINGS.Bar1PermissionH", 263 | scope: "world", 264 | type: String, 265 | default: "owned", 266 | choices: DEFAULT_CONFIG.bar1Permission.choices, 267 | config: true, 268 | onChange: s => { 269 | if (!game.user.isGM) { 270 | ui.combatCarousel.render(true); 271 | } 272 | } 273 | }); 274 | 275 | game.settings.register(NAME, SETTING_KEYS.bar1Attribute, { 276 | name: "COMBAT_CAROUSEL.SETTINGS.Bar1AttributeN", 277 | hint: "COMBAT_CAROUSEL.SETTINGS.Bar1AttributeH", 278 | scope: "world", 279 | type: String, 280 | default: "attributes.hp", 281 | config: true, 282 | onChange: s => { 283 | ui.combatCarousel.render(true); 284 | } 285 | }); 286 | 287 | game.settings.register(NAME, SETTING_KEYS.bar1Title, { 288 | name: "COMBAT_CAROUSEL.SETTINGS.Bar1TitleN", 289 | hint: "COMBAT_CAROUSEL.SETTINGS.Bar1TitleH", 290 | scope: "world", 291 | type: String, 292 | default: DEFAULT_CONFIG.bar1Title, 293 | config: true, 294 | onChange: s => { 295 | ui.combatCarousel.render(true); 296 | } 297 | }); 298 | 299 | /* -------------------------------------------------------------------------- */ 300 | /* Data Storage */ 301 | /* -------------------------------------------------------------------------- */ 302 | 303 | game.settings.register(NAME, SETTING_KEYS.overlaySettings, { 304 | name: "COMBAT_CAROUSEL.SETTINGS.PropertyOverlayN", 305 | hint: "COMBAT_CAROUSEL.SETTINGS.PropertyOverlayH", 306 | scope: "world", 307 | type: Object, 308 | default: DEFAULT_CONFIG.overlaySettings, 309 | config: false, 310 | onChange: s => { 311 | ui.combatCarousel.render(true); 312 | } 313 | }); 314 | 315 | game.settings.register(NAME, SETTING_KEYS.appPosition, { 316 | name: "COMBAT_CAROUSEL.SETTINGS.AppPositionN", 317 | hint: "COMBAT_CAROUSEL.SETTINGS.AppPositionH", 318 | scope: "client", 319 | type: Object, 320 | default: DEFAULT_CONFIG.appPosition, 321 | config: false, 322 | onChange: s => { 323 | 324 | } 325 | }); 326 | 327 | game.settings.register(NAME, SETTING_KEYS.collapsed, { 328 | name: "COMBAT_CAROUSEL.SETTINGS.CollapsedN", 329 | hint: "COMBAT_CAROUSEL.SETTINGS.CollapsedH", 330 | scope: "client", 331 | type: Boolean, 332 | default: false, 333 | config: false, 334 | onChange: s => { 335 | 336 | } 337 | }); 338 | } -------------------------------------------------------------------------------- /icons/combat-carousel-solid2-2.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 50 | 56 | 57 | 63 | 79 | 83 | 84 | 86 | 87 | 89 | 90 | 92 | 93 | 95 | 96 | 98 | 99 | 101 | 102 | 104 | 105 | 107 | 108 | 110 | 111 | 113 | 114 | 116 | 117 | 119 | 120 | 122 | 123 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /icons/combat-carousel-solid2-3.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 49 | 52 | 58 | 59 | 65 | 81 | 85 | 86 | 88 | 89 | 91 | 92 | 94 | 95 | 97 | 98 | 100 | 101 | 103 | 104 | 106 | 107 | 109 | 110 | 112 | 113 | 115 | 116 | 118 | 119 | 121 | 122 | 124 | 125 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /icons/combat-carousel-solid2.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 47 | 50 | 56 | 57 | 63 | 79 | 83 | 84 | 86 | 87 | 89 | 90 | 92 | 93 | 95 | 96 | 98 | 99 | 101 | 102 | 104 | 105 | 107 | 108 | 110 | 111 | 113 | 114 | 116 | 117 | 119 | 120 | 122 | 123 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /icons/combat-carousel-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 44 | 47 | 53 | 54 | 60 | 76 | 80 | 81 | 83 | 84 | 86 | 87 | 89 | 90 | 92 | 93 | 95 | 96 | 98 | 99 | 101 | 102 | 104 | 105 | 107 | 108 | 110 | 111 | 113 | 114 | 116 | 117 | 119 | 120 | 122 | 123 | 124 | --------------------------------------------------------------------------------