zischenstand

This commit is contained in:
centron\schwoerer
2025-11-14 14:52:43 +01:00
parent 30aa03c6db
commit f054a31b20
8733 changed files with 900639 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
{
"VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS": "Detaily",
"VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED": "Zakázané",
"VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE": "Pasivní",
"VISUAL_ACTIVE_EFFECTS.LABELS.TEMPORARY": "Dočasné",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT": "Počet dnů v týdnu.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME": "Dnů v týdnu",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT": "Počet dnů navíc v roce.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME": "Navíc dnů v roce",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT": "If enabled, any disabled effects will not show up.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME": "Hide Disabled Effects",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT": "If enabled, any passive effects will not show up.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME": "Hide Passive Effects",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT": "Změte velikost ikony v pixelech. Výchozí: 50.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME": "Velikost ikony",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT": "Počet měsíců v roce.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME": "Měsíců v roce",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT": "Počet týdnů v měsíci.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME": "Týdnů v měsíci",
"VISUAL_ACTIVE_EFFECTS.TIME.DAY": "1 den",
"VISUAL_ACTIVE_EFFECTS.TIME.DAYS": "{qty} dnů",
"VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED": "Vypršelo",
"VISUAL_ACTIVE_EFFECTS.TIME.HOUR": "1 hodinu",
"VISUAL_ACTIVE_EFFECTS.TIME.HOURS": "{qty} hodin",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTE": "1 minuta",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTES": "{qty} minut",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTH": "1 měsíc",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTHS": "{qty} měsíců",
"VISUAL_ACTIVE_EFFECTS.TIME.SECOND": "1 vteřina",
"VISUAL_ACTIVE_EFFECTS.TIME.SECONDS": "{qty} vteřin",
"VISUAL_ACTIVE_EFFECTS.TIME.TURN": "1 tah",
"VISUAL_ACTIVE_EFFECTS.TIME.TURNS": "{qty} tahů",
"VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED": "Neomezeně",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEK": "1 týden",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEKS": "{qty} týdnů",
"VISUAL_ACTIVE_EFFECTS.TIME.YEAR": "1 rok",
"VISUAL_ACTIVE_EFFECTS.TIME.YEARS": "{qty} let"
}

View File

@@ -0,0 +1,52 @@
{
"VISUAL_ACTIVE_EFFECTS.CONTENT": "Inhalt",
"VISUAL_ACTIVE_EFFECTS.EDITOR_SAVED": "Textinhalte wurden gespeichert.",
"VISUAL_ACTIVE_EFFECTS.EDITOR_TITLE": "Visual Active Effects Editor: {name}",
"VISUAL_ACTIVE_EFFECTS.FORCE_INCLUDE": "Effekt immer anzeigen:",
"VISUAL_ACTIVE_EFFECTS.INTRO": "Intro",
"VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS": "Details",
"VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED": "Deaktiviert",
"VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE": "Passiv",
"VISUAL_ACTIVE_EFFECTS.LABELS.SOURCE": "Quelle",
"VISUAL_ACTIVE_EFFECTS.LABELS.TEMPORARY": "Vorübergehend",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT": "Anzahl der Tage in der Woche.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME": "Tage pro Woche",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT": "Jeder zusätzliche Tag im Jahr.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME": "Zusätzliche Tage im Jahr",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.HINT": "Ändert die Schriftgröße vom Text der Effekte. Default: 16.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.NAME": "Schriftgröße",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT": "Wenn aktiviert, werden unterbrochene Effekte nicht angezeigt.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME": "Verstecke unterbrochene Effekte",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT": "Wenn aktiv, werden passive Effekte nicht angezeigt.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME": "Passive Effekte verbergen",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT": "Ändere die Größe des Icons (in Pixel). Default: 50.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME": "Icon Größe",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT": "Anzahl der Monate im Jahr.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME": "Monat pro Jahr",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.HINT": "Wenn deaktiviert können Nicht-SL-Nutzer Effekte nicht direkt im Panel umschalten oder löschen.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.NAME": "Spielerinteraktion erlauben",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.HINT": "Ändert den Abstand zwischen dem Effektpanel und dem oberen Fensterrand. Default: 25.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.NAME": "Abstand oben",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT": "Anzahl der Wochen im Monat.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME": "Wochen pro Monat",
"VISUAL_ACTIVE_EFFECTS.STATUS_ID": "Status:",
"VISUAL_ACTIVE_EFFECTS.Statuses": "Stati",
"VISUAL_ACTIVE_EFFECTS.TIME.DAY": "1 Tag",
"VISUAL_ACTIVE_EFFECTS.TIME.DAYS": "{qty} Tage",
"VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED": "Abgelaufen",
"VISUAL_ACTIVE_EFFECTS.TIME.HOUR": "1 Stunde",
"VISUAL_ACTIVE_EFFECTS.TIME.HOURS": "{qty} Stunden",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTE": "1 Minute",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTES": "{qty} Minuten",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTH": "1 Monat",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTHS": "{qty} Monate",
"VISUAL_ACTIVE_EFFECTS.TIME.SECOND": "1 Sekunde",
"VISUAL_ACTIVE_EFFECTS.TIME.SECONDS": "{qty} Sekunden",
"VISUAL_ACTIVE_EFFECTS.TIME.TURN": "1 Zug",
"VISUAL_ACTIVE_EFFECTS.TIME.TURNS": "{qty} Züge",
"VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED": "Unbegrenzt",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEK": "1 Woche",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEKS": "{qty} Wochen",
"VISUAL_ACTIVE_EFFECTS.TIME.YEAR": "1 Jahr",
"VISUAL_ACTIVE_EFFECTS.TIME.YEARS": "{qty} Jahre"
}

View File

@@ -0,0 +1,55 @@
{
"VISUAL_ACTIVE_EFFECTS.CONTENT": "Content",
"VISUAL_ACTIVE_EFFECTS.EDITOR_SAVED": "Text contents have been saved.",
"VISUAL_ACTIVE_EFFECTS.EDITOR_TITLE": "Visual Active Effects Editor: {name}",
"VISUAL_ACTIVE_EFFECTS.FORCE_INCLUDE": "Always show this effect:",
"VISUAL_ACTIVE_EFFECTS.INCLUSION": "Always include or exclude this effect:",
"VISUAL_ACTIVE_EFFECTS.INCLUSION_INCLUDE": "Always Include",
"VISUAL_ACTIVE_EFFECTS.INCLUSION_EXCLUDE": "Always Exclude",
"VISUAL_ACTIVE_EFFECTS.INTRO": "Intro",
"VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS": "Details",
"VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED": "Disabled",
"VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE": "Passive",
"VISUAL_ACTIVE_EFFECTS.LABELS.SOURCE": "Source",
"VISUAL_ACTIVE_EFFECTS.LABELS.TEMPORARY": "Temporary",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT": "The number of days in a week.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME": "Days per Week",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT": "Any extra days per year.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME": "Extra Days per Year",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.HINT": "Change the font size of the text in the effects. Default: 16.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.NAME": "Font Size",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT": "If enabled, any disabled effects will not show up.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME": "Hide Disabled Effects",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT": "If enabled, any passive effects will not show up.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME": "Hide Passive Effects",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT": "Change the pixel size of the icons. Default: 50.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME": "Icon Size",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT": "The number of months in a year.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME": "Months per Year",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.HINT": "If unchecked, non-GM users will not be able to toggle or delete effects directly in the panel.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.NAME": "Allow Player Interaction",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.HINT": "Change the space between the panel of effects and the top of the window. Default: 25.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.NAME": "Top Offset",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT": "The number of weeks in a month.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME": "Weeks per Month",
"VISUAL_ACTIVE_EFFECTS.STATUS_ID": "Status:",
"VISUAL_ACTIVE_EFFECTS.Statuses": "Statuses",
"VISUAL_ACTIVE_EFFECTS.TIME.DAY": "1 day",
"VISUAL_ACTIVE_EFFECTS.TIME.DAYS": "{qty} days",
"VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED": "Expired",
"VISUAL_ACTIVE_EFFECTS.TIME.HOUR": "1 hour",
"VISUAL_ACTIVE_EFFECTS.TIME.HOURS": "{qty} hours",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTE": "1 minute",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTES": "{qty} minutes",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTH": "1 month",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTHS": "{qty} months",
"VISUAL_ACTIVE_EFFECTS.TIME.SECOND": "1 second",
"VISUAL_ACTIVE_EFFECTS.TIME.SECONDS": "{qty} seconds",
"VISUAL_ACTIVE_EFFECTS.TIME.TURN": "1 turn",
"VISUAL_ACTIVE_EFFECTS.TIME.TURNS": "{qty} turns",
"VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED": "Unlimited",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEK": "1 week",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEKS": "{qty} weeks",
"VISUAL_ACTIVE_EFFECTS.TIME.YEAR": "1 year",
"VISUAL_ACTIVE_EFFECTS.TIME.YEARS": "{qty} years"
}

View File

@@ -0,0 +1,52 @@
{
"VISUAL_ACTIVE_EFFECTS.CONTENT": "Zawartość",
"VISUAL_ACTIVE_EFFECTS.EDITOR_SAVED": "Zawartość została zaktualizowana.",
"VISUAL_ACTIVE_EFFECTS.EDITOR_TITLE": "Autor Visual Active Effects: {name}",
"VISUAL_ACTIVE_EFFECTS.FORCE_INCLUDE": "Zawsze pokazuj ten efektt:",
"VISUAL_ACTIVE_EFFECTS.INTRO": "Wprowadzenie",
"VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS": "Szczegóły",
"VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED": "Nieaktywny",
"VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE": "Pasywny",
"VISUAL_ACTIVE_EFFECTS.LABELS.SOURCE": "Żródło",
"VISUAL_ACTIVE_EFFECTS.LABELS.TEMPORARY": "Tymczasowy",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT": "Liczba dni w tygodniu.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME": "Dni tygodnia",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT": "Dodatkowe dni w roku",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME": "Dodatkowe dni w roku",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.HINT": "Zmień rozmiar czcionki w opisie efektu. Domyślnie: 16.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.NAME": "Rozmiar Czcionki",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT": "Gdy zaznaczone, nieaktywne efekty nie będą wyświetlane.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME": "Ukryj Nieaktywne Efekty",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT": "Gdy zaznaczone, pasywne efekty nie będą wyświetlane.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME": "Ukryj Pasywne Efekty",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT": "Zmień liczbę pikseli ikony efektu. Domyślnie: 50.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME": "Rozmiar Ikony",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT": "Liczba miesięcy w roku.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME": "Miesiące w Roku",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.HINT": "Gdy niezaznaczone, gracze będą mogli przełączać efekty lub je usuwać bezpośrednio z panelu.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.NAME": "Zezwól na interakcje graczy",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.HINT": "Zmień odległość pomiędzy panelem efektów a górną krawędzią okna foundry. Domyślnie: 25.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.NAME": "Odległość od Krawędzi",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT": "Liczba tygodni w miesiącu.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME": "Tygodnie w miesiącu",
"VISUAL_ACTIVE_EFFECTS.STATUS_ID": "Status:",
"VISUAL_ACTIVE_EFFECTS.Statuses": "Statusy",
"VISUAL_ACTIVE_EFFECTS.TIME.DAY": "1 dzień",
"VISUAL_ACTIVE_EFFECTS.TIME.DAYS": "{qty} dni",
"VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED": "Wygasły",
"VISUAL_ACTIVE_EFFECTS.TIME.HOUR": "1 godzina",
"VISUAL_ACTIVE_EFFECTS.TIME.HOURS": "{qty} godzin",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTE": "1 minuta",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTES": "{qty} minut",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTH": "1 miesiąc",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTHS": "{qty} miesięcy",
"VISUAL_ACTIVE_EFFECTS.TIME.SECOND": "1 sekunda",
"VISUAL_ACTIVE_EFFECTS.TIME.SECONDS": "{qty} sekund",
"VISUAL_ACTIVE_EFFECTS.TIME.TURN": "1 Tura",
"VISUAL_ACTIVE_EFFECTS.TIME.TURNS": "{qty} Tur(y)",
"VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED": "Nieograniczony",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEK": "1 tydzień",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEKS": "{qty} tygodni(e)",
"VISUAL_ACTIVE_EFFECTS.TIME.YEAR": "1 rok",
"VISUAL_ACTIVE_EFFECTS.TIME.YEARS": "{qty} lat(a)"
}

View File

@@ -0,0 +1,51 @@
{
"VISUAL_ACTIVE_EFFECTS.CONTENT": "Conteúdo",
"VISUAL_ACTIVE_EFFECTS.EDITOR_SAVED": "O conteúdo do texto foi salvo.",
"VISUAL_ACTIVE_EFFECTS.EDITOR_TITLE": "Editor de efeitos visuais ativos: {name}",
"VISUAL_ACTIVE_EFFECTS.FORCE_INCLUDE": "Sempre mostre este efeito:",
"VISUAL_ACTIVE_EFFECTS.INTRO": "Introdução",
"VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS": "Detalhes",
"VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED": "Desabilitado",
"VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE": "Passivo",
"VISUAL_ACTIVE_EFFECTS.LABELS.SOURCE": "Fonte",
"VISUAL_ACTIVE_EFFECTS.LABELS.TEMPORARY": "Temporário",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT": "O número de dias em uma semana.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME": "Dias por semana",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT": "Qualquer dia extra por ano.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME": "Dias extras por ano",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.HINT": "Altere o tamanho da fonte do texto nos efeitos. Padrão: 16.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.NAME": "Tamanho da fonte",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT": "Se ativado, quaisquer efeitos desativados não aparecerão.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME": "Ocultar efeitos desativados",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT": "Se habilitado, quaisquer efeitos passivos não aparecerão.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME": "Ocultar efeitos passivos",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT": "Altere o tamanho do pixel dos ícones. Padrão: 50.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME": "Tamanho do Ícone",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT": "O número de meses em um ano.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME": "Meses por ano",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.HINT": "Se desmarcado, os usuários não-GM não poderão alternar ou excluir efeitos diretamente no painel.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.NAME": "Permitir interação do jogador",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.HINT": "Altere o espaço entre os efeitos do painel desativado e a parte superior da janela. Padrão: 25.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.NAME": "Deslocamento superior",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT": "O número de semanas em um mês.",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME": "Semanas por mês",
"VISUAL_ACTIVE_EFFECTS.STATUS_ID": "Identificação do estado:",
"VISUAL_ACTIVE_EFFECTS.TIME.DAY": "1 dia",
"VISUAL_ACTIVE_EFFECTS.TIME.DAYS": "{qty} dias",
"VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED": "Expirado",
"VISUAL_ACTIVE_EFFECTS.TIME.HOUR": "1 hora",
"VISUAL_ACTIVE_EFFECTS.TIME.HOURS": "{qty} horas",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTE": "1 minuto",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTES": "{qty} minutos",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTH": "1 mês",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTHS": "{qty} meses",
"VISUAL_ACTIVE_EFFECTS.TIME.SECOND": "1 segundo",
"VISUAL_ACTIVE_EFFECTS.TIME.SECONDS": "{qty} segundos",
"VISUAL_ACTIVE_EFFECTS.TIME.TURN": "1 turno",
"VISUAL_ACTIVE_EFFECTS.TIME.TURNS": "{qty} turnos",
"VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED": "Ilimitado",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEK": "1 semana",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEKS": "{qty} semanas",
"VISUAL_ACTIVE_EFFECTS.TIME.YEAR": "1 ano",
"VISUAL_ACTIVE_EFFECTS.TIME.YEARS": "{qty} anos"
}

View File

@@ -0,0 +1,53 @@
{
"VISUAL_ACTIVE_EFFECTS.CONTENT": "详述",
"VISUAL_ACTIVE_EFFECTS.FORCE_INCLUDE": "总是显示该效果:",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME": "每年的额外天数",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT": "如果勾选,任何被动效果都不会显示。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.NAME": "允许玩家互动",
"VISUAL_ACTIVE_EFFECTS.EDITOR_SAVED": "文本内容已保存。",
"VISUAL_ACTIVE_EFFECTS.EDITOR_TITLE": "Visual Active Effects 编辑器: {name}",
"VISUAL_ACTIVE_EFFECTS.INTRO": "简述",
"VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS": "详细信息",
"VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED": "已禁用",
"VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE": "被动",
"VISUAL_ACTIVE_EFFECTS.LABELS.SOURCE": "来源",
"VISUAL_ACTIVE_EFFECTS.LABELS.TEMPORARY": "临时效果",
"VISUAL_ACTIVE_EFFECTS.MISC.DELETE_EFFECT": "删除效果",
"VISUAL_ACTIVE_EFFECTS.MISC.DELETE_ME": "是否删除 {label}?",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT": "配置一周有多少天。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME": "每周的天数",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT": "一年的任何额外天数。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.HINT": "修改效果文本的字体尺寸。默认16。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.NAME": "字体大小",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT": "如果勾选,任何已禁用的效果都不会显示。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME": "隐藏已禁用的效果",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME": "隐藏被动效果",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT": "修改图标的像素尺寸。默认50。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME": "图标大小",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT": "配置一年有多少个月。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME": "每年的月数",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.HINT": "修改效果面板距离窗口顶部的空间。默认25。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.NAME": "顶部偏移量",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT": "配置一个月有多少周。",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME": "每月的周数",
"VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.HINT": "如果不勾选非GM用户将不能直接在面板中切换或删除效果。",
"VISUAL_ACTIVE_EFFECTS.STATUS_ID": "状态 Id:",
"VISUAL_ACTIVE_EFFECTS.TIME.DAY": "1 天",
"VISUAL_ACTIVE_EFFECTS.TIME.DAYS": "{qty} 天",
"VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED": "已过期",
"VISUAL_ACTIVE_EFFECTS.TIME.HOUR": "1 小时",
"VISUAL_ACTIVE_EFFECTS.TIME.HOURS": "{qty} 小时",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTE": "1 分钟",
"VISUAL_ACTIVE_EFFECTS.TIME.MINUTES": "{qty} 分钟",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTH": "1 月",
"VISUAL_ACTIVE_EFFECTS.TIME.MONTHS": "{qty} 月",
"VISUAL_ACTIVE_EFFECTS.TIME.SECOND": "1 秒",
"VISUAL_ACTIVE_EFFECTS.TIME.SECONDS": "{qty} 秒",
"VISUAL_ACTIVE_EFFECTS.TIME.TURN": "1 轮",
"VISUAL_ACTIVE_EFFECTS.TIME.TURNS": "{qty} 轮",
"VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED": "持续",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEK": "1 周",
"VISUAL_ACTIVE_EFFECTS.TIME.WEEKS": "{qty} 周",
"VISUAL_ACTIVE_EFFECTS.TIME.YEAR": "1 年",
"VISUAL_ACTIVE_EFFECTS.TIME.YEARS": "{qty} 年"
}

View File

@@ -0,0 +1,66 @@
{
"id": "visual-active-effects",
"title": "Visual Active Effects",
"version": "11.1.2",
"authors": [
{
"name": "Zhell",
"url": "https://github.com/krbz999",
"discord": "zhell9201",
"ko-fi": "https://ko-fi.com/zhell",
"patreon": "https://patreon.com/zhell"
}
],
"compatibility": {
"minimum": "11",
"maximum": "11",
"verified": "11"
},
"esmodules": [
"scripts/setup.mjs"
],
"styles": [
"styles/visual-active-effects.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json",
"flags": {}
},
{
"lang": "cs",
"name": "Česky",
"path": "lang/cs.json",
"flags": {}
},
{
"lang": "de",
"name": "Deutsch",
"path": "lang/de.json",
"flags": {}
},
{
"lang": "pt-BR",
"name": "Português (Brasil)",
"path": "lang/pt_BR.json"
},
{
"lang": "zh-Hans",
"name": "中文(简体)",
"path": "lang/zh_Hans.json"
},
{
"lang": "pl",
"name": "Polski",
"path": "lang/pl.json"
}
],
"flags": {
"hotReload": false
},
"url": "https://github.com/krbz999/visual-active-effects",
"manifest": "https://github.com/krbz999/visual-active-effects/releases/latest/download/module.json",
"download": "https://github.com/krbz999/visual-active-effects/releases/download/v11.1.2/module.zip"
}

View File

@@ -0,0 +1,12 @@
export const DAYS_PER_WEEK = "daysPerWeek";
export const EXTRA_DAYS_PER_YEAR = "extraDaysPerYear";
export const FONT_SIZE = "fontSize";
export const HIDE_DISABLED = "hideDisabled";
export const HIDE_PASSIVE = "hidePassive";
export const PLAYER_CLICKS = "playerClicks";
export const ICON = "fa-solid fa-pen-fancy";
export const ICON_SIZE = "iconSize";
export const MODULE = "visual-active-effects";
export const MONTHS_PER_YEAR = "monthsPerYear";
export const TOP_OFFSET = "topOffset";
export const WEEKS_PER_MONTH = "weeksPerMonth";

View File

@@ -0,0 +1,160 @@
import {
DAYS_PER_WEEK,
EXTRA_DAYS_PER_YEAR,
FONT_SIZE,
ICON_SIZE,
MODULE,
MONTHS_PER_YEAR,
TOP_OFFSET,
WEEKS_PER_MONTH
} from "./constants.mjs";
import VisualActiveEffectsEditor from "./textEditor.mjs";
/**
* Helper function to get remaining duration.
* @param {ActiveEffect} effect
* @returns {string|null}
*/
export function remainingTimeLabel(effect) {
// Case 1: Duration measured in rounds and turns.
if (effect.duration.type === "turns") {
if (effect.duration.remaining === null) return game.i18n.localize("VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED");
else if (effect.duration.remaining === 0) return game.i18n.localize("VISUAL_ACTIVE_EFFECTS.TIME.EXPIRED");
return effect.duration.label;
}
// Case 2: Duration measured in seconds.
else if (effect.duration.type === "seconds") {
const SECONDS = {
IN_ONE_ROUND: 6,
IN_ONE_MINUTE: 60,
IN_TWO_MINUTES: 120,
IN_ONE_HOUR: 3600,
IN_TWO_HOURS: 7200,
IN_ONE_DAY: 86400,
IN_TWO_DAYS: 172800
};
const daysPerWeek = game.settings.get(MODULE, DAYS_PER_WEEK) ?? 9;
const weeksPerMonth = game.settings.get(MODULE, WEEKS_PER_MONTH) ?? 3;
const monthsPerYear = game.settings.get(MODULE, MONTHS_PER_YEAR) ?? 12;
const extraDaysPerYear = game.settings.get(MODULE, EXTRA_DAYS_PER_YEAR) ?? 1;
SECONDS.IN_ONE_WEEK = SECONDS.IN_ONE_DAY * daysPerWeek;
SECONDS.IN_TWO_WEEKS = SECONDS.IN_ONE_WEEK * 2;
SECONDS.IN_ONE_MONTH = SECONDS.IN_ONE_WEEK * weeksPerMonth;
SECONDS.IN_TWO_MONTHS = SECONDS.IN_ONE_MONTH * 2;
SECONDS.IN_ONE_YEAR = SECONDS.IN_ONE_MONTH * monthsPerYear + SECONDS.IN_ONE_DAY * extraDaysPerYear;
SECONDS.IN_TWO_YEARS = SECONDS.IN_ONE_YEAR * 2;
const remainingSeconds = effect.duration.remaining;
let string = "";
let qty = 1;
if (remainingSeconds >= SECONDS.IN_TWO_YEARS) {
qty = Math.floor(remainingSeconds / SECONDS.IN_ONE_YEAR);
string = "YEARS";
} else if (remainingSeconds >= SECONDS.IN_ONE_YEAR) {
string = "YEAR";
} else if (remainingSeconds >= SECONDS.IN_TWO_MONTHS) {
qty = Math.floor(remainingSeconds / SECONDS.IN_ONE_MONTH);
string = "MONTHS";
} else if (remainingSeconds >= SECONDS.IN_ONE_MONTH) {
string = "MONTH";
} else if (remainingSeconds >= SECONDS.IN_TWO_WEEKS) {
qty = Math.floor(remainingSeconds / SECONDS.IN_ONE_WEEK);
string = "WEEKS";
} else if (remainingSeconds >= SECONDS.IN_ONE_WEEK) {
string = "WEEK";
} else if (remainingSeconds >= SECONDS.IN_TWO_DAYS) {
qty = Math.floor(remainingSeconds / SECONDS.IN_ONE_DAY);
string = "DAYS";
} else if (remainingSeconds >= SECONDS.IN_ONE_DAY) {
string = "DAY";
} else if (remainingSeconds >= SECONDS.IN_TWO_HOURS) {
qty = Math.floor(remainingSeconds / SECONDS.IN_ONE_HOUR);
string = "HOURS";
} else if (remainingSeconds >= SECONDS.IN_ONE_HOUR) {
string = "HOUR";
} else if (remainingSeconds >= SECONDS.IN_TWO_MINUTES) {
qty = Math.floor(remainingSeconds / SECONDS.IN_ONE_MINUTE);
string = "MINUTES";
} else if (remainingSeconds >= SECONDS.IN_ONE_MINUTE) {
string = "MINUTE";
} else if (remainingSeconds >= 2) {
qty = remainingSeconds;
string = "SECONDS";
} else if (remainingSeconds === 1) {
string = "SECOND";
} else {
string = "EXPIRED";
}
return game.i18n.format(`VISUAL_ACTIVE_EFFECTS.TIME.${string}`, {qty});
}
// Case 3: Neither rounds, turns, or seconds, so just return unlimited.
return game.i18n.localize("VISUAL_ACTIVE_EFFECTS.TIME.UNLIMITED");
}
/** Render the editor. */
export function _renderEditor() {
const editor = Object.values(this.apps).find(e => e instanceof VisualActiveEffectsEditor);
if (editor) return editor.render();
return new VisualActiveEffectsEditor(this).render(true);
}
/** Refreshes the style sheet when a user changes the various css-related module settings. */
export function applyStyleSettings() {
const data = {};
data["icon-size"] = Math.max(10, Math.round(game.settings.get(MODULE, ICON_SIZE) || 50));
data["font-size"] = Math.max(6, Math.round(game.settings.get(MODULE, FONT_SIZE) || 16));
data["max-width"] = Math.round(300 * data["font-size"] / 16);
data["top-offset"] = Math.max(0, Math.round(game.settings.get(MODULE, TOP_OFFSET) || 25));
const root = document.querySelector(":root");
Object.entries(data).forEach(([key, val]) => root.style.setProperty(`--${MODULE}-${key}`, `${val}px`));
}
/** Register API functions. */
export function registerAPI() {
game.modules.get(MODULE).api = {
migrateWorldDescriptions: async function() {
ui.notifications.info(`${MODULE.toUpperCase()} | Migrating actors and items in the sidebar. Please be patient.`);
for (const item of game.items) await _migrateDocumentWithEffects(item);
for (const actor of game.actors) await _migrateActor(actor);
ui.notifications.info(`${MODULE.toUpperCase()} | Finished migrating sidebar actors and items.`);
},
migratePackDescriptions: async function(pack) {
const isActor = pack.metadata.type === "Actor";
const isItem = pack.metadata.type === "Item";
if (!(isActor || isItem)) {
console.warn(`${MODULE.toUpperCase()} | ${pack.metadata.label} (${pack.metadata.id}) is not a valid compendium type.`);
return null;
}
ui.notifications.info(`${MODULE.toUpperCase()} | Migrating ${pack.metadata.label} (${pack.metadata.id}). Please be patient.`);
const docs = await pack.getDocuments();
const mig = isActor ? _migrateActor : _migrateDocumentWithEffects;
for (const doc of docs) await mig(doc);
ui.notifications.info(`${MODULE.toUpperCase()} | Finished migrating ${pack.metadata.label} (${pack.metadata.id}).`);
}
};
}
async function _migrateActor(actor) {
await _migrateDocumentWithEffects(actor);
for (const item of actor.items) {
await _migrateDocumentWithEffects(item);
}
}
async function _migrateDocumentWithEffects(doc) {
const updates = [];
for (const effect of doc.effects) {
const data = effect.flags[MODULE]?.data?.intro;
if (data) updates.push({_id: effect.id, description: data});
}
if (updates.length) console.log(`${MODULE.toUpperCase()} | Migrating ${doc.name} (${doc.uuid})`);
return doc.updateEmbeddedDocuments("ActiveEffect", updates);
}

View File

@@ -0,0 +1,119 @@
import {
DAYS_PER_WEEK,
EXTRA_DAYS_PER_YEAR,
ICON_SIZE,
MODULE,
MONTHS_PER_YEAR,
WEEKS_PER_MONTH,
HIDE_DISABLED,
HIDE_PASSIVE,
FONT_SIZE,
TOP_OFFSET,
PLAYER_CLICKS
} from "./constants.mjs";
import {applyStyleSettings} from "./helpers.mjs";
export function registerSettings() {
game.settings.register(MODULE, ICON_SIZE, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.ICON_SIZE.HINT",
scope: "client",
config: true,
type: Number,
default: 50,
requiresReload: false,
onChange: applyStyleSettings
});
game.settings.register(MODULE, FONT_SIZE, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.FONT_SIZE.HINT",
scope: "client",
config: true,
type: Number,
default: 16,
requiresReload: false,
onChange: applyStyleSettings
});
game.settings.register(MODULE, TOP_OFFSET, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.TOP_OFFSET.HINT",
scope: "client",
config: true,
type: Number,
default: 25,
requiresReload: false,
onChange: applyStyleSettings
});
game.settings.register(MODULE, HIDE_DISABLED, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_DISABLED.HINT",
scope: "world",
config: true,
type: Boolean,
default: false,
requiresReload: true
});
game.settings.register(MODULE, HIDE_PASSIVE, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.HIDE_PASSIVE.HINT",
scope: "world",
config: true,
type: Boolean,
default: true,
requiresReload: true
});
game.settings.register(MODULE, PLAYER_CLICKS, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.PLAYER_CLICKS.HINT",
scope: "world",
config: true,
type: Boolean,
default: true,
requiresReload: true
});
game.settings.register(MODULE, DAYS_PER_WEEK, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.DAYS_PER_WEEK.HINT",
scope: "world",
config: true,
type: Number,
default: 9,
requiresReload: true
});
game.settings.register(MODULE, WEEKS_PER_MONTH, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.WEEKS_PER_MONTH.HINT",
scope: "world",
config: true,
type: Number,
default: 3,
requiresReload: true
});
game.settings.register(MODULE, MONTHS_PER_YEAR, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.MONTHS_PER_YEAR.HINT",
scope: "world",
config: true,
type: Number,
default: 12,
requiresReload: true
});
game.settings.register(MODULE, EXTRA_DAYS_PER_YEAR, {
name: "VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.NAME",
hint: "VISUAL_ACTIVE_EFFECTS.SETTINGS.EXTRA_DAYS_PER_YEAR.HINT",
scope: "world",
config: true,
type: Number,
default: 1,
requiresReload: true
});
}

View File

@@ -0,0 +1,32 @@
import {ICON, MODULE} from "./constants.mjs";
import {_renderEditor, applyStyleSettings, registerAPI} from "./helpers.mjs";
import {registerSettings} from "./settings.mjs";
import {VisualActiveEffects} from "./visual-active-effects.mjs";
Hooks.once("init", registerSettings);
Hooks.once("ready", async function() {
await loadTemplates([
"modules/visual-active-effects/templates/effect.hbs",
"modules/visual-active-effects/templates/status.hbs"
]);
registerAPI();
applyStyleSettings();
const panel = new VisualActiveEffects();
await panel.render(true);
Hooks.on("collapseSidebar", panel.handleExpand.bind(panel));
Hooks.on("updateWorldTime", panel.refresh.bind(panel, false));
Hooks.on("controlToken", panel.refresh.bind(panel, true));
for (const hook of ["createActiveEffect", "updateActiveEffect", "deleteActiveEffect"]) {
Hooks.on(hook, function(effect) {
if (effect.target === panel.actor) panel.refresh(true);
});
}
Hooks.on("updateCombat", function(combat, update, context) {
if (context.advanceTime !== 0) return;
if (!context.direction) return;
panel.refresh(false);
});
});
Hooks.on("getActiveEffectConfigHeaderButtons", function(app, array) {
array.unshift({class: MODULE, icon: ICON, onclick: _renderEditor.bind(app.document)});
});

View File

@@ -0,0 +1,124 @@
import {ICON, MODULE} from "./constants.mjs";
export default class VisualActiveEffectsEditor extends FormApplication {
constructor(effect, ...T) {
super(effect, ...T);
this.effect = effect;
}
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
width: 450,
height: 500,
classes: [MODULE, "sheet"],
resizable: true,
scrollY: [],
tabs: [{navSelector: ".tabs", contentSelector: "form", initial: "intro"}],
dragDrop: [],
closeOnSubmit: false
});
}
get title() {
return game.i18n.format("VISUAL_ACTIVE_EFFECTS.EDITOR_TITLE", {name: this.effect.name});
}
get template() {
return `modules/${MODULE}/templates/vae-editor.hbs`;
}
get id() {
return `${MODULE}-editor-${this.effect.uuid.replaceAll(".", "-")}`;
}
/** @override */
async getData() {
const data = await super.getData();
const flag = this.effect.getFlag(MODULE, "data") ?? {};
// Backwards compatibility.
let inclusion;
if ("inclusion" in flag) inclusion = flag.inclusion;
else if (data.forceInclude === true) inclusion = 1;
else inclusion = 0;
foundry.utils.mergeObject(data, {
statuses: this.effect.statuses.size ? this.effect.statuses.toObject() : [""],
inclusion: inclusion,
content: await TextEditor.enrichHTML(flag.content || ""),
intro: await TextEditor.enrichHTML(this.effect.description || ""),
editable: this.isEditable,
ICON: ICON
});
return data;
}
/** @override */
activateListeners(html) {
super.activateListeners(html);
html[0].querySelector("[data-action='add-status']").addEventListener("click", this._onAddStatus.bind(this));
html[0].querySelectorAll("[data-action='delete-status']").forEach(n => n.addEventListener("click", this._onDeleteStatus.bind(this)));
}
/**
* Handle removing an old row for a status.
* @param {PointerEvent} event The initiating click event.
*/
_onDeleteStatus(event) {
event.currentTarget.closest(".form-group").remove();
}
/**
* Handle adding a new row for a status.
* @param {PointerEvent} event The initiating click event.
*/
async _onAddStatus(event) {
const force = event.currentTarget.closest(".config").querySelector(".form-group:last-child");
const div = document.createElement("DIV");
div.innerHTML = await renderTemplate("modules/visual-active-effects/templates/status.hbs", []);
div.querySelector("[data-action='delete-status']").addEventListener("click", this._onDeleteStatus.bind(this));
force.before(div.firstElementChild);
}
/** @override */
async _updateObject(event, formData) {
if (!formData.statuses) formData.statuses = [];
else if (typeof formData.statuses === "string") formData.statuses = [formData.statuses];
formData.statuses = formData.statuses.reduce((acc, s) => {
s = s.trim();
if (s) acc.push(s);
return acc;
}, []);
ui.notifications.info("VISUAL_ACTIVE_EFFECTS.EDITOR_SAVED", {localize: true});
if (event.submitter) this.close();
const sheet = this.effect.sheet;
if (sheet) formData = sheet._getSubmitData(formData);
return this.effect.update(formData);
}
/** @override */
async activateEditor(name, options = {}, initialContent = "") {
options.relativeLinks = false;
options.plugins = {
menu: ProseMirror.ProseMirrorMenu.build(ProseMirror.defaultSchema, {
compact: true,
destroyOnSave: false,
onSave: () => this.saveEditor(name, {remove: true})
})
};
return super.activateEditor(name, options, initialContent);
}
/** @override */
_render(...T) {
this.effect.apps[this.appId] = this;
return super._render(...T);
}
/** @override */
close(...T) {
delete this.effect.apps[this.appId];
return super.close(...T);
}
}

View File

@@ -0,0 +1,290 @@
import {HIDE_DISABLED, HIDE_PASSIVE, MODULE, PLAYER_CLICKS} from "./constants.mjs";
import {remainingTimeLabel} from "./helpers.mjs";
export class VisualActiveEffects extends Application {
/**
* Array of buttons for other modules.
* @type {object[]}
*/
buttons = [];
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: MODULE,
popOut: false,
template: `modules/${MODULE}/templates/${MODULE}.hbs`,
minimizable: false
});
}
/** @constructor */
constructor() {
super();
this._initialSidebarWidth = ui.sidebar.element.outerWidth();
this._playerClicks = game.settings.get(MODULE, PLAYER_CLICKS);
}
/** @override */
async getData() {
const enabledEffects = [];
const disabledEffects = [];
const passiveEffects = [];
const fromActor = this._getEffectsFromActor();
if (!fromActor) return {};
const hideDisabled = game.settings.get(MODULE, HIDE_DISABLED);
const hidePassive = game.settings.get(MODULE, HIDE_PASSIVE);
// Set up effects.
for (const entry of fromActor) {
// Set up the various text (intro and content).
const desc = entry.effect.flags["dfreds-convenient-effects"]?.description;
const data = entry.effect.flags[MODULE]?.data ?? {};
// Get the effect rollData to populate enrichers within the descriptions.
let rollData;
try {
if (entry.effect.origin) {
let origin = fromUuidSync(entry.effect.origin);
if (origin?.documentName === ActiveEffect.documentName) {
// Change the origin to the parent of the ActiveEffect - the originating item or actor.
origin = origin.parent;
}
if (origin?.pack) {
// Origin references a compendium, change the origin to the effect's parent - the local item or actor.
origin = entry.effect.parent;
}
if (typeof origin?.getRollData === "function") {
rollData = origin.getRollData();
}
}
// Fallback to the parent if there's no rollData.
if (!rollData) rollData = entry.effect.parent.getRollData();
} catch (_) {
// Fallback to just an empty object - enrichers will show empty values in this case.
rollData = {};
}
// Backwards compatibility.
let inclusion = 0;
if ("inclusion" in data) inclusion = data.inclusion;
else if (data.forceInclude) inclusion = 1;
// Always exclude?
const forceExclude = data.inclusion === -1;
if (forceExclude) continue;
// Set up intro if it exists.
const intro = entry.effect.description || desc;
if (intro) entry.context.strings.intro = await TextEditor.enrichHTML(intro, {rollData});
// Set up content if it exists.
if (data.content?.length) {
// The 'header' for the collapsible's header with default 'Details'.
entry.context.strings.header = data.header || game.i18n.localize("VISUAL_ACTIVE_EFFECTS.LABELS.DETAILS");
// The collapsible content.
entry.context.strings.content = await TextEditor.enrichHTML(data.content, {rollData});
}
entry.context.hasText = !!intro || !!data.content?.length || !!entry.context.buttons.length;
// Add to either disabled array, enabled array, or passive array.
if (entry.effect.disabled) {
if (!hideDisabled || (inclusion === 1)) disabledEffects.push(entry);
}
else if (entry.effect.isTemporary) enabledEffects.push(entry);
else if (!hidePassive || (inclusion === 1)) passiveEffects.push(entry);
}
return {enabledEffects, disabledEffects, passiveEffects};
}
/**
* Helper method for getData, extracting each effect, filtering the suppressed, and returning it with additional context.
* @returns {object[]} An array with 'effect' and 'context'.
*/
_getEffectsFromActor() {
if (!this.actor) return;
const data = [];
const effects = this.actor.allApplicableEffects();
for (const effect of effects) {
if (effect.isSuppressed) continue;
const context = {strings: {intro: "", content: ""}};
if (effect.isTemporary) {
const rem = effect.duration.remaining;
context.isExpired = Number.isNumeric(rem) && (rem <= 0);
context.isInfinite = rem === null;
context.durationLabel = remainingTimeLabel(effect);
}
const buttons = [];
/**
* A hook that is called such that other modules can add buttons to the description of an effect
* on the panel. Each object pushed into the array must have 'label' and a function 'callback'.
* @param {ActiveEffect} effect The original effect.
* @param {Array} buttons An array of buttons.
*/
Hooks.callAll("visual-active-effects.createEffectButtons", effect, buttons);
// Filter out invalid buttons and push valid ones into this.buttons in one go.
context.buttons = buttons.reduce((acc, b) => {
if (!(typeof b.label === "string") || !(b.callback instanceof Function)) return acc;
b.id = foundry.utils.randomID();
this.buttons.push(b);
acc.push(b);
return acc;
}, []);
data.push({effect, context});
}
return data;
}
/**
* Helper method for getData.
* @param {object} duration An effect's duration object.
* @returns {number} The time remaining.
*/
_getSecondsRemaining(duration) {
if (duration.seconds || duration.rounds) {
const seconds = duration.seconds ?? duration.rounds * (CONFIG.time.roundTime ?? 6);
return duration.startTime + seconds - game.time.worldTime;
} else return Infinity;
}
/**
* The currently selected token's actor, otherwise the user's assigned actor.
* @type {Actor|null}
*/
get actor() {
return canvas.tokens.controlled[0]?.actor ?? game.user.character;
}
/** @override */
async _render(force = false, options = {}) {
if (!force && this.element[0].closest(".panel").classList.contains("hovered")) {
this._needsRefresh = true;
return;
}
await super._render(force, options);
this._needsRefresh = false;
if (ui.sidebar._collapsed) this.element.css("right", "50px");
else this.element.css("right", `${this._initialSidebarWidth + 18}px`);
}
/**
* Debounce rendering of the app.
* @param {boolean} force Whether to force the rendering of the app.
* @returns {Promise<VisualActiveEffects>} This application.
*/
async refresh(force) {
return foundry.utils.debounce(this.render.bind(this, force), 100)();
}
/** @override */
activateListeners(html) {
if (this._playerClicks || game.user.isGM) {
html[0].querySelectorAll(".effect-icon").forEach(n => n.addEventListener("contextmenu", this.onIconRightClick.bind(this)));
html[0].querySelectorAll(".effect-icon").forEach(n => n.addEventListener("dblclick", this.onIconDoubleClick.bind(this)));
}
html[0].querySelectorAll(".collapsible-header").forEach(n => n.addEventListener("click", this.onCollapsibleClick.bind(this)));
html[0].querySelectorAll("[data-action='custom-button']").forEach(n => n.addEventListener("click", this.onClickCustomButton.bind(this)));
html[0].addEventListener("mouseover", this._onMouseOver.bind(this));
html[0].addEventListener("mouseout", this._onMouseOver.bind(this));
html[0].addEventListener("mouseover", this.bringToTop.bind(this));
html[0].querySelectorAll(".effect-item").forEach(n => n.addEventListener("mouseenter", this._onMouseEnter.bind(this)));
}
/**
* Set the maximum height of effect descriptions.
* @param {Event} event Initiating hover event.
*/
_onMouseEnter(event) {
const info = event.currentTarget.querySelector(".effect-intro");
if (!info) return;
const win = window.innerHeight;
info.style.maxHeight = `${win - info.getBoundingClientRect().top - 350}px`;
}
/** @override */
bringToTop(event) {
const element = event.currentTarget;
const z = document.defaultView.getComputedStyle(element).zIndex;
if (z < _maxZ) {
element.style.zIndex = Math.min(++_maxZ, 99999);
ui.activeWindow = this;
}
}
/**
* Save whether the application is being moused over.
* @param {Event} event The initiating mouseover or mouseout event.
* @returns {Promise<void|VisualActiveEffects>}
*/
_onMouseOver(event) {
const state = event.type === "mouseover";
const target = event.currentTarget;
target.classList.toggle("hovered", state);
if (!state && (this._needsRefresh === true)) return this.render();
}
/**
* When a button on the panel is clicked.
* @param {Event} event The initiating click event.
* @returns {Promise} Result of the callback function.
*/
async onClickCustomButton(event) {
const id = event.currentTarget.dataset.id;
const button = this.buttons.find(b => b.id === id);
return button.callback(event);
}
/**
* Helper method to move the panel when the sidebar is collapsed or expanded.
* @param {Sidebar} _ The sidebar.
* @param {boolean} bool Whether it was collapsed.
*/
handleExpand(_, bool) {
if (!bool) {
const right = `${this._initialSidebarWidth + 18}px`;
this.element.css("right", right);
} else this.element.delay(50).animate({right: "50px"}, 500);
}
/**
* Handle deleting an effect when right-clicked.
* @param {Event} event The initiating click event.
* @returns {Promise<ActiveEffect|boolean>} Either the deleted effect, or the result of the prompt.
*/
async onIconRightClick(event) {
const alt = event.shiftKey;
const effect = await fromUuid(event.currentTarget.closest("[data-effect-uuid]").dataset.effectUuid);
return (alt && game.user.isGM) ? effect.delete() : effect.deleteDialog();
}
/**
* Handle enabling/disabling an effect when double-clicked, or showing its sheet.
* @param {Event} event The initiating click event.
* @returns {Promise<ActiveEffect|ActiveEffectConfig>} The updated effect or its sheet.
*/
async onIconDoubleClick(event) {
const alt = event.ctrlKey;
const effect = await fromUuid(event.currentTarget.closest("[data-effect-uuid]").dataset.effectUuid);
return alt ? effect.sheet.render(true) : effect.update({disabled: !effect.disabled});
}
/**
* Handle collapsing the description of an effect.
* @param {Event} event The initiating click event.
*/
onCollapsibleClick(event) {
const section = event.currentTarget.closest(".collapsible-section");
section.classList.toggle("active");
const div = section.querySelector(".collapsible-content");
const header = event.currentTarget;
const win = window.innerHeight;
div.style.maxHeight = `${win - (150 + header.getBoundingClientRect().bottom)}px`;
}
}

View File

@@ -0,0 +1,344 @@
:root {
--vae-border-color-temporary: rgba(158, 192, 255, 0.705);
--vae-border-color-passive: rgba(255, 242, 167, 0.664);
--vae-border-color-disabled: rgba(194, 0, 0, 0.61);
--vae-dropshadow: drop-shadow(3px 3px 4px rgb(83, 83, 83));
--vae-backdrop-color: rgb(49, 49, 49);
--vae-button-color-font: rgba(255, 255, 255, 0.664);
--vae-button-color-backdrop: rgb(100, 100, 100);
--vae-name-color: rgb(255, 255, 255);
--vae-name-border: 1px dashed rgba(133, 133, 133, 0.582);
--vae-name-font: 'Amiri';
--vae-tag-border: 1px solid rgb(197, 197, 197);
--vae-tag-color-duration: rgba(100, 100, 255, 0.5);
--vae-tag-color-disabled: rgba(255, 100, 100, 0.5);
--vae-tag-color-source: rgba(255, 100, 255, 0.5);
}
/* ------------------------- */
/* */
/* EDITOR */
/* */
/* ------------------------- */
.visual-active-effects.sheet {
min-width: 450px;
min-height: 400px;
& form {
display: grid;
grid-template-rows: 1fr 0fr;
.inputs {
display: grid;
grid-template-rows: 0fr 0fr 1fr;
}
.sections {
position: relative;
.tab {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
}
.inclusion label {
flex: 1;
white-space: nowrap;
}
.inclusion select {
flex: none;
}
}
[data-action="delete-status"] {
flex: 0;
padding-right: 8px;
}
& label {
white-space: nowrap;
}
.status-header {
height: 28px;
background: rgba(0, 0, 0, 0.1);
border: 1px solid var(--color-border-light-tertiary);
font-weight: bold;
padding: 3px 7px;
display: flex;
align-items: center;
justify-content: space-between;
}
}
/* ------------------------- */
/* */
/* EFFECTS PANEL */
/* */
/* ------------------------- */
.visual-active-effects.panel {
position: fixed;
z-index: 100;
top: var(--visual-active-effects-top-offset);
filter: var(--vae-dropshadow);
display: flex;
flex-direction: column;
align-items: flex-end;
gap: calc(var(--visual-active-effects-icon-size)/5);
overflow: visible;
max-height: calc(100% - 50px);
.effect-item {
display: flex;
justify-content: flex-end;
height: var(--visual-active-effects-icon-size);
max-width: var(--visual-active-effects-icon-size);
&:hover {
max-width: unset;
.effect-info {
height: fit-content;
margin: 0 calc(max(1.5rem, var(--visual-active-effects-icon-size)/5)) 0 0;
padding: 8px;
visibility: visible;
opacity: 1;
max-width: var(--visual-active-effects-max-width);
min-width: var(--visual-active-effects-max-width);
}
.effect-icon {
transform: scale(1.2);
z-index: 100;
}
}
.effect-info {
visibility: hidden;
opacity: 0;
max-width: 0;
min-width: 0;
filter: var(--vae-dropshadow);
background-color: var(--vae-backdrop-color);
transition: opacity 0.15s linear;
border-left: 10px solid;
border-radius: 2px;
overflow: hidden auto;
&.temporary {
border-color: var(--vae-border-color-temporary);
}
&.passive {
border-color: var(--vae-border-color-passive);
}
&.disabled {
border-color: var(--vae-border-color-disabled);
}
.effect-info-header {
display: flex;
flex-direction: column;
&:not(:last-child) {
border-bottom: var(--vae-name-border);
}
&:last-child {
border: none;
}
.name {
color: var(--vae-name-color);
text-align: center;
padding: 0 4px;
margin: 0;
font-size: 1.6rem;
font-family: var(--vae-name-font);
text-transform: uppercase;
}
}
&.simple .effect-tags {
margin-bottom: 0;
}
}
.effect-icon {
background-size: contain;
box-shadow: 0 0 0 1px silver, 0 0 0 2px gray, inset 0 0 4px rgba(0, 0, 0, 0.5);
height: var(--visual-active-effects-icon-size);
width: var(--visual-active-effects-icon-size);
min-width: var(--visual-active-effects-icon-size);
position: relative;
transition: transform 0.15s;
&.disabled {
filter: brightness(0.25) grayscale(1);
}
.badge {
bottom: 5%;
color: white;
display: inline-block;
left: 5%;
position: absolute;
text-shadow: 0 0 5px black;
font-size: calc(var(--visual-active-effects-icon-size)/3);
pointer-events: none;
&.unlimited {
font-size: calc(var(--visual-active-effects-icon-size)/4);
}
&.expired {
color: orange;
}
}
}
}
.effect-info-details {
gap: 0.5em;
display: flex;
flex-direction: column;
margin: 0.5em 0;
overflow: hidden;
.inline-roll, .content-link {
color: black;
}
}
.effect-intro,
.collapsible-content {
color: rgb(192, 192, 192);
padding: 0 0.5rem;
font-style: italic;
font-family: sans-serif;
overflow: hidden auto;
font-size: var(--visual-active-effects-font-size);
}
.collapsible-section {
.collapsible-header {
cursor: pointer;
background-color: var(--vae-button-color-backdrop);
border: 1px solid #e2ffca50;
border-bottom: none;
border-radius: 2px 2px 0 0;
color: var(--vae-button-color-font);
font-weight: bold;
font-family: "Modesto Condensed";
display: flex;
justify-content: space-between;
align-items: center;
font-size: 18px;
padding: 4px;
padding-left: 10px;
&::after {
content: "\02795";
float: right;
filter: brightness(10);
font-size: 14px;
}
}
.collapsible-content {
overflow: hidden;
border: 1px solid #e2ffca50;
border-top: none;
border-radius: 0 0 2px 2px;
transition: max-height 0.15s ease-out, opacity 0.15s ease-out;
font-size: calc(var(--visual-active-effects-font-size) - 2px);
}
&.active {
.collapsible-header::after {
content: "\2796";
}
.collapsible-content {
opacity: 1;
overflow-y: auto;
}
}
&:not(.active) {
.collapsible-content {
max-height: 0 !important;
opacity: 0;
}
}
}
.effect-tags {
display: flex;
gap: 3px;
color: white;
margin-bottom: 1rem;
flex-wrap: wrap;
justify-content: center;
.effect-tag {
border-radius: 4px;
padding: 5px;
text-transform: uppercase;
position: relative;
white-space: nowrap;
font-size: 11px;
&::after {
content: "";
position: absolute;
inset: 1px;
border-radius: 2px;
border: var(--vae-tag-border);
}
&.temporary {
background-color: var(--vae-tag-color-duration);
}
&.disabled {
background-color: var(--vae-tag-color-disabled);
}
&.source {
background-color: var(--vae-tag-color-source);
}
}
}
& hr.divider {
margin-right: 0;
width: var(--visual-active-effects-icon-size);
}
.vae-buttons {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
.vae-button {
z-index: 1;
background-color: var(--vae-button-color-backdrop);
color: var(--vae-button-color-font);
font-family: 'Modesto Condensed';
font-size: 20px;
transition: box-shadow 150ms;
&:hover {
box-shadow: 0 0 5px white;
}
&:focus {
box-shadow: none;
}
}
}
}

View File

@@ -0,0 +1,68 @@
<div class="effect-item" data-effect-id="{{effect._id}}" data-effect-uuid="{{effect.uuid}}">
<div class="effect-info {{cssClass}} {{#unless context.hasText}} simple {{/unless}}">
<h1 class="effect-info-header">
<span class="name">{{effect.name}}</span>
<div class="effect-tags">
<div class="effect-tag temporary">
{{#if context.durationLabel}}
<i class="fa-solid fa-hourglass"></i>
{{context.durationLabel}}
{{else}}
<i class="fa-solid fa-feather"></i>
{{localize "VISUAL_ACTIVE_EFFECTS.LABELS.PASSIVE"}}
{{/if}}
</div>
{{#if effect.disabled}}
<div class="effect-tag disabled">
<i class="fa-solid fa-ban"></i>
{{localize "VISUAL_ACTIVE_EFFECTS.LABELS.DISABLED"}}
</div>
{{/if}}
{{#if effect.sourceName}}
<div class="effect-tag source">
<i class="fa-solid fa-tree"></i>
{{localize "VISUAL_ACTIVE_EFFECTS.LABELS.SOURCE"}}: {{effect.sourceName}}
</div>
{{/if}}
</div>
</h1>
{{#if (or context.strings.intro context.strings.content)}}
<div class="effect-info-details">
{{#if context.strings.intro}}
<div class="effect-intro">{{{context.strings.intro}}}</div>
{{/if}}
{{#if context.strings.content}}
<section class="collapsible-section">
<header class="collapsible-header">{{context.strings.header}}</header>
<div class="collapsible-content">{{{context.strings.content}}}</div>
</section>
{{/if}}
</div>
{{/if}}
{{#if context.buttons.length}}
<div class="vae-buttons">
{{#each context.buttons}}
<button type="button" class="vae-button" data-action="custom-button" data-id="{{id}}">{{label}}</button>
{{/each}}
</div>
{{/if}}
</div>
<div class="effect-icon {{#if effect.disabled}}disabled{{/if}}" style="background-image: url({{effect.icon}})">
{{#if effect.isTemporary}}
{{#if context.isExpired}}
<i class="expired badge fa-solid fa-clock"></i>
{{else if context.isInfinite}}
<i class="unlimited badge fa-solid fa-infinity"></i>
{{else}}
<i class="badge fa-solid fa-clock"></i>
{{/if}}
{{/if}}
</div>
</div>

View File

@@ -0,0 +1,7 @@
<div class="form-group status">
<label>{{localize "VISUAL_ACTIVE_EFFECTS.STATUS_ID"}}</label>
<div class="form-fields">
<input type="text" name="statuses" value="{{this}}">
<a data-action="delete-status"><i class="fa-solid fa-trash"></i></a>
</div>
</div>

View File

@@ -0,0 +1,52 @@
<form class="visual-active-effects" autocomplete="off">
<div class="inputs">
<div class="config">
<div class="status-header">
{{localize "VISUAL_ACTIVE_EFFECTS.Statuses"}}
<a data-action="add-status"><i class="fa-solid fa-plus-square"></i></a>
</div>
{{#each statuses}}
{{> "modules/visual-active-effects/templates/status.hbs"}}
{{/each}}
<div class="form-group">
<label>{{localize "VISUAL_ACTIVE_EFFECTS.INCLUSION"}}</label>
<div class="form-fields inclusion">
<select data-dtype="Number" name="flags.visual-active-effects.data.inclusion">
{{#select inclusion}}
<option value="0">&mdash;</option>
<option value="1">{{localize "VISUAL_ACTIVE_EFFECTS.INCLUSION_INCLUDE"}}</option>
<option value="-1">{{localize "VISUAL_ACTIVE_EFFECTS.INCLUSION_EXCLUDE"}}</option>
{{/select}}
</select>
</div>
</div>
</div>
<nav class="sheet-tabs tabs" aria-role="Form Tab Navigation">
<a class="item" data-tab="intro">
<i class="{{ICON}}"></i>
{{localize "VISUAL_ACTIVE_EFFECTS.INTRO"}}
</a>
<a class="item" data-tab="content">
<i class="{{ICON}}"></i>
{{localize "VISUAL_ACTIVE_EFFECTS.CONTENT"}}
</a>
</nav>
<div class="sections">
<div class="tab flexrow" data-tab="intro">
{{editor intro target="description" button=true editable=editable engine="prosemirror" collaborate=false}}
</div>
<div class="tab flexrow" data-tab="content">
{{editor content target="flags.visual-active-effects.data.content" button=true editable=editable engine="prosemirror" collaborate=false}}
</div>
</div>
</div>
<footer class="sheet-footer">
<button type="submit"><i class="fa-solid fa-save"></i> {{localize "Save"}}</button>
</footer>
</form>

View File

@@ -0,0 +1,23 @@
<section id="visual-active-effects" class="panel visual-active-effects">
{{#each enabledEffects}}
{{> "modules/visual-active-effects/templates/effect.hbs" cssClass="temporary"}}
{{/each}}
{{#if (and passiveEffects.length enabledEffects.length)}}
<hr class="divider">
{{/if}}
{{#each passiveEffects}}
{{> "modules/visual-active-effects/templates/effect.hbs" cssClass="passive"}}
{{/each}}
{{#if (and disabledEffects.length (or enabledEffects.length passiveEffects.length))}}
<hr class="divider">
{{/if}}
{{#each disabledEffects}}
{{> "modules/visual-active-effects/templates/effect.hbs" cssClass="disabled"}}
{{/each}}
</section>