diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e15a5b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +# Makefile pour Vermine2047 +# Ce fichier fournit des commandes courantes pour le développement +# Utilise uniquement LESS comme préprocesseur CSS + +.PHONY: help install build build-less build-dev build-css watch clean lint + +# Couleurs pour l'affichage +GREEN := \033[0;32m +YELLOW := \033[1;33m +NC := \033[0m # No Color + +help: ## Affiche cette aide + @echo "Commandes disponibles pour Vermine2047:" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[0;32m%-20s\033[0m %s\n", $$1, $$2}' + @echo "" + +install: ## Installe les dépendances npm + @echo "$(YELLOW)Installation des dépendances npm...$(NC)" + npm install + @echo "$(GREEN)✓ Dépendances installées$(NC)" + +build: build-less ## Compile LESS → vermine2047.min.css + +build-less: ## Compile le LESS en CSS minifié (vermine2047.min.css) + @echo "$(YELLOW)Compilation du LESS...$(NC)" + npm run build:less + @echo "$(GREEN)✓ LESS compilé$(NC)" + +build-dev: ## Compile le LESS en CSS non minifié (vermine2047.dev.css) + @echo "$(YELLOW)Compilation du LESS (mode dev)...$(NC)" + npm run build:less:dev + @echo "$(GREEN)✓ LESS compilé en mode dev$(NC)" + +build-css: build-less build-dev ## Compile tout le CSS (minifié + dev) + +watch: ## Lance le mode watch (recompilation automatique) + @echo "$(YELLOW)Lancement du mode watch...$(NC)" + npm run watch + +clean: clean-css ## Nettoie les fichiers CSS générés + +clean-css: ## Supprime les fichiers CSS compilés (garde vermine2047.css original) + @echo "$(YELLOW)Nettoyage des fichiers CSS...$(NC)" + npm run clean:css + @echo "$(GREEN)✓ Fichiers CSS nettoyés$(NC)" + +rebuild: clean build ## Reconstruit tout le CSS + @echo "$(YELLOW)Reconstruction complète du CSS...$(NC)" + npm run rebuild:css + @echo "$(GREEN)✓ CSS reconstruit$(NC)" + +lint: lint-less ## Lance le linting du code LESS + +lint-less: ## Vérifie la qualité du code LESS + @echo "$(YELLOW)Linting du code LESS...$(NC)" + npm run lint:less + @echo "$(GREEN)✓ Linting terminé$(NC)" + +# Commandes utilitaires + +launch-foundry: ## Lance FoundryVTT + npm run launch_Foundry12 + +push-yaml: ## Push LDB vers YAML + node ./tools/pushLDBtoYAML.mjs + +pull-yaml: ## Pull YAML vers LDB + node ./tools/pullYAMLtoLDB.mjs diff --git a/assets/dice/d10c-1.webp b/assets/dice/d10c-1.webp new file mode 100644 index 0000000..d144e04 Binary files /dev/null and b/assets/dice/d10c-1.webp differ diff --git a/assets/dice/d10c-10.webp b/assets/dice/d10c-10.webp new file mode 100644 index 0000000..1059b8e Binary files /dev/null and b/assets/dice/d10c-10.webp differ diff --git a/assets/dice/d10c-2.webp b/assets/dice/d10c-2.webp new file mode 100644 index 0000000..a8a2867 Binary files /dev/null and b/assets/dice/d10c-2.webp differ diff --git a/assets/dice/d10c-3.webp b/assets/dice/d10c-3.webp new file mode 100644 index 0000000..9b131df Binary files /dev/null and b/assets/dice/d10c-3.webp differ diff --git a/assets/dice/d10c-4.webp b/assets/dice/d10c-4.webp new file mode 100644 index 0000000..fa5ef8d Binary files /dev/null and b/assets/dice/d10c-4.webp differ diff --git a/assets/dice/d10c-5.webp b/assets/dice/d10c-5.webp new file mode 100644 index 0000000..1809b7e Binary files /dev/null and b/assets/dice/d10c-5.webp differ diff --git a/assets/dice/d10c-6.webp b/assets/dice/d10c-6.webp new file mode 100644 index 0000000..a31530a Binary files /dev/null and b/assets/dice/d10c-6.webp differ diff --git a/assets/dice/d10c-7.webp b/assets/dice/d10c-7.webp new file mode 100644 index 0000000..2f7bf12 Binary files /dev/null and b/assets/dice/d10c-7.webp differ diff --git a/assets/dice/d10c-8.webp b/assets/dice/d10c-8.webp new file mode 100644 index 0000000..913b664 Binary files /dev/null and b/assets/dice/d10c-8.webp differ diff --git a/assets/dice/d10c-9.webp b/assets/dice/d10c-9.webp new file mode 100644 index 0000000..b0ac3d3 Binary files /dev/null and b/assets/dice/d10c-9.webp differ diff --git a/assets/dice/d10s-1.webp b/assets/dice/d10s-1.webp new file mode 100644 index 0000000..743ebc1 Binary files /dev/null and b/assets/dice/d10s-1.webp differ diff --git a/assets/dice/d10s-10.webp b/assets/dice/d10s-10.webp new file mode 100644 index 0000000..7f6584e Binary files /dev/null and b/assets/dice/d10s-10.webp differ diff --git a/assets/dice/d10s-2.webp b/assets/dice/d10s-2.webp new file mode 100644 index 0000000..ed577d1 Binary files /dev/null and b/assets/dice/d10s-2.webp differ diff --git a/assets/dice/d10s-3.webp b/assets/dice/d10s-3.webp new file mode 100644 index 0000000..645fb08 Binary files /dev/null and b/assets/dice/d10s-3.webp differ diff --git a/assets/dice/d10s-4.webp b/assets/dice/d10s-4.webp new file mode 100644 index 0000000..d29f63e Binary files /dev/null and b/assets/dice/d10s-4.webp differ diff --git a/assets/dice/d10s-5.webp b/assets/dice/d10s-5.webp new file mode 100644 index 0000000..3f1420a Binary files /dev/null and b/assets/dice/d10s-5.webp differ diff --git a/assets/dice/d10s-6.webp b/assets/dice/d10s-6.webp new file mode 100644 index 0000000..671c251 Binary files /dev/null and b/assets/dice/d10s-6.webp differ diff --git a/assets/dice/d10s-7.webp b/assets/dice/d10s-7.webp new file mode 100644 index 0000000..7af17a7 Binary files /dev/null and b/assets/dice/d10s-7.webp differ diff --git a/assets/dice/d10s-8.webp b/assets/dice/d10s-8.webp new file mode 100644 index 0000000..cedbe4a Binary files /dev/null and b/assets/dice/d10s-8.webp differ diff --git a/assets/dice/d10s-9.webp b/assets/dice/d10s-9.webp new file mode 100644 index 0000000..007d997 Binary files /dev/null and b/assets/dice/d10s-9.webp differ diff --git a/assets/icons/items/batisseur.webp b/assets/icons/items/batisseur.webp new file mode 100644 index 0000000..2cd8667 Binary files /dev/null and b/assets/icons/items/batisseur.webp differ diff --git a/assets/icons/items/charognard.webp b/assets/icons/items/charognard.webp new file mode 100644 index 0000000..d9ccf9b Binary files /dev/null and b/assets/icons/items/charognard.webp differ diff --git a/assets/icons/items/horde.webp b/assets/icons/items/horde.webp new file mode 100644 index 0000000..e1ed9c5 Binary files /dev/null and b/assets/icons/items/horde.webp differ diff --git a/assets/icons/items/parasite.webp b/assets/icons/items/parasite.webp new file mode 100644 index 0000000..7d6dee3 Binary files /dev/null and b/assets/icons/items/parasite.webp differ diff --git a/assets/icons/items/predateur.webp b/assets/icons/items/predateur.webp new file mode 100644 index 0000000..deb504e Binary files /dev/null and b/assets/icons/items/predateur.webp differ diff --git a/assets/icons/items/ruche.webp b/assets/icons/items/ruche.webp new file mode 100644 index 0000000..ba09600 Binary files /dev/null and b/assets/icons/items/ruche.webp differ diff --git a/assets/icons/items/solitaire.webp b/assets/icons/items/solitaire.webp new file mode 100644 index 0000000..8c549d1 Binary files /dev/null and b/assets/icons/items/solitaire.webp differ diff --git a/assets/icons/items/symbiote.webp b/assets/icons/items/symbiote.webp new file mode 100644 index 0000000..256183b Binary files /dev/null and b/assets/icons/items/symbiote.webp differ diff --git a/char-sheet-fixed.png b/char-sheet-fixed.png new file mode 100644 index 0000000..d6b2665 Binary files /dev/null and b/char-sheet-fixed.png differ diff --git a/char-sheet.png b/char-sheet.png new file mode 100644 index 0000000..f1735fb Binary files /dev/null and b/char-sheet.png differ diff --git a/char-sheet.txt b/char-sheet.txt new file mode 100644 index 0000000..a19f24e --- /dev/null +++ b/char-sheet.txt @@ -0,0 +1,680 @@ +uid=78_0 RootWebArea "Foundry Virtual Tabletop" url="https://localhost:31000/game" + uid=78_1 ignored + uid=78_2 generic + uid=78_3 list + uid=78_4 generic + uid=78_5 generic + uid=78_6 generic + uid=78_7 generic + uid=78_8 generic + uid=78_9 generic + uid=78_10 list + uid=78_11 listitem level="1" + uid=78_12 tab "Outils de token" selectable + uid=78_13 listitem level="1" + uid=78_14 tab "Outils de tuile" selectable + uid=78_15 listitem level="1" + uid=78_16 tab "Outils de dessin" selectable + uid=78_17 listitem level="1" + uid=78_18 tab "Outils de mur" selectable + uid=78_19 listitem level="1" + uid=78_20 tab "Outils de lumière" selectable + uid=78_21 listitem level="1" + uid=78_22 tab "Outils de son d’ambiance" selectable + uid=78_23 listitem level="1" + uid=78_24 tab "Outils de région" selectable + uid=78_25 listitem level="1" + uid=78_26 tab "Notes" selectable + uid=78_27 list + uid=78_28 listitem level="1" + uid=78_29 button "Sélection de tokens" pressed + uid=78_30 listitem level="1" + uid=78_31 button "Sélection de cibles" + uid=78_32 listitem level="1" + uid=78_33 button "Règle" + uid=78_34 listitem level="1" + uid=78_35 button "Déplacement sans contrainte" + uid=78_36 generic + uid=78_37 generic + uid=78_38 list + uid=78_39 listitem level="1" + uid=78_40 ignored + uid=78_41 StaticText "Gamemaster [MJ]" + uid=78_42 InlineTextBox "Gamemaster [MJ]" + uid=78_43 generic + uid=78_44 generic + uid=78_45 LabelText + uid=78_46 StaticText "Latence" + uid=78_42 InlineTextBox "Latence" + uid=78_47 StaticText " " + uid=78_42 InlineTextBox " " + uid=78_48 StaticText "1ms" + uid=78_42 InlineTextBox "1ms" + uid=78_49 generic + uid=78_50 LabelText + uid=78_51 StaticText "IPS" + uid=78_42 InlineTextBox "IPS" + uid=78_52 StaticText " " + uid=78_42 InlineTextBox " " + uid=78_53 StaticText "--" + uid=78_42 InlineTextBox "--" + uid=78_54 button + uid=78_55 generic + uid=78_56 navigation + uid=78_57 list + uid=78_58 generic + uid=78_59 sectionheader + uid=78_60 generic + uid=78_61 generic + uid=78_62 LabelText + uid=78_63 LabelText + uid=78_64 sectionfooter + uid=78_65 generic roledescription="Barre de raccourcis" + uid=78_66 generic + uid=78_67 button "Couper le son" + uid=78_68 button "Menu principal" + uid=78_69 list + uid=78_70 button "Emplacement vide" + uid=78_71 ignored + uid=78_72 StaticText "1" + uid=78_42 InlineTextBox "1" + uid=78_73 button "Emplacement vide" + uid=78_74 ignored + uid=78_75 StaticText "2" + uid=78_42 InlineTextBox "2" + uid=78_76 button "Emplacement vide" + uid=78_77 ignored + uid=78_78 StaticText "3" + uid=78_42 InlineTextBox "3" + uid=78_79 button "Emplacement vide" + uid=78_80 ignored + uid=78_81 StaticText "4" + uid=78_42 InlineTextBox "4" + uid=78_82 button "Emplacement vide" + uid=78_83 ignored + uid=78_84 StaticText "5" + uid=78_42 InlineTextBox "5" + uid=78_85 button "Emplacement vide" + uid=78_86 ignored + uid=78_87 StaticText "6" + uid=78_42 InlineTextBox "6" + uid=78_88 button "Emplacement vide" + uid=78_89 ignored + uid=78_90 StaticText "7" + uid=78_42 InlineTextBox "7" + uid=78_91 button "Emplacement vide" + uid=78_92 ignored + uid=78_93 StaticText "8" + uid=78_42 InlineTextBox "8" + uid=78_94 button "Emplacement vide" + uid=78_95 ignored + uid=78_96 StaticText "9" + uid=78_42 InlineTextBox "9" + uid=78_97 button "Emplacement vide" + uid=78_98 ignored + uid=78_99 StaticText "0" + uid=78_42 InlineTextBox "0" + uid=78_100 generic + uid=78_101 navigation + uid=78_102 button "Page suivante" + uid=78_103 ignored + uid=78_104 StaticText "1" + uid=78_42 InlineTextBox "1" + uid=78_105 button "Page précédente" + uid=78_106 ignored + uid=78_107 button "Verrouiller la barre de raccourcis" + uid=78_108 button "Effacer la barre de raccourcis" + uid=78_109 generic + uid=78_110 generic + uid=78_111 generic + uid=78_112 ignored + uid=78_113 list + uid=78_114 generic "Tchat" + uid=78_115 generic + uid=78_116 generic value=" +" + uid=78_117 paragraph + uid=78_118 LineBreak " +" + uid=78_42 InlineTextBox " +" + uid=78_119 ignored + uid=78_42 generic + uid=78_42 StaticText "Entrer un message" + uid=78_42 InlineTextBox "Entrer un message" + uid=78_120 generic + uid=78_121 generic + uid=78_122 button "Public en tant qu'utilisateur" pressed + uid=78_123 button "Privé pour les maîtres de jeu" + uid=78_124 button "Aveugle pour les maîtres de jeu" + uid=78_125 button "Seulement pour soi-même" + uid=78_126 button "Public en tant que personnage" + uid=78_127 generic + uid=78_128 tablist orientation="horizontal" + uid=78_129 list + uid=78_130 listitem level="1" + uid=78_131 tab "Messages du tchat" selectable + uid=78_132 listitem level="1" + uid=78_133 tab "Rencontres de combat" selectable + uid=78_134 listitem level="1" + uid=78_135 tab "Scènes" selectable + uid=78_136 listitem level="1" + uid=78_137 tab "Objets plaçables" selectable + uid=78_138 listitem level="1" + uid=78_139 tab "Acteurs" selectable + uid=78_140 listitem level="1" + uid=78_141 tab "Objets" selectable + uid=78_142 listitem level="1" + uid=78_143 tab "Journaux" selectable + uid=78_144 listitem level="1" + uid=78_145 tab "Tables aléatoires" selectable + uid=78_146 listitem level="1" + uid=78_147 tab "Jeux de cartes" selectable + uid=78_148 listitem level="1" + uid=78_149 tab "Macros" selectable + uid=78_150 listitem level="1" + uid=78_151 tab "Playlists" selectable + uid=78_152 listitem level="1" + uid=78_153 tab "Compendiums" selectable + uid=78_154 listitem level="1" + uid=78_155 tab "Paramètres" selectable + uid=78_156 listitem level="1" + uid=78_157 button "Réduire" + uid=78_158 generic + uid=78_159 ignored + uid=78_160 ignored + uid=78_161 ignored + uid=78_162 ignored + uid=78_163 generic + uid=78_164 sectionheader + uid=78_165 ignored + uid=78_166 button "Créer un acteur" + uid=78_167 ignored + uid=78_168 ignored + uid=78_169 StaticText "Créer un acteur" + uid=78_42 InlineTextBox "Créer un acteur" + uid=78_170 button "Créer un dossier" + uid=78_171 ignored + uid=78_172 ignored + uid=78_173 StaticText "Créer un dossier" + uid=78_42 InlineTextBox "Créer un dossier" + uid=78_174 search + uid=78_175 button "Recherche par nom uniquement" + uid=78_176 searchbox "Chercher dans les Acteurs" + uid=78_177 ignored + uid=78_178 ignored + uid=78_179 generic + uid=78_180 button "Trier par ordre alphabétique" + uid=78_181 button "Réduire tous les dossiers" + uid=78_182 list + uid=78_183 listitem level="1" + uid=78_184 image "Acteur" url="https://localhost:31000/systems/vermine2047/assets/icons/actors/creature.webp" + uid=78_185 generic + uid=78_186 StaticText "Acteur" + uid=78_42 InlineTextBox "Acteur" + uid=78_187 listitem level="1" + uid=78_188 image "Acteur (2)" url="https://localhost:31000/systems/vermine2047/assets/icons/actors/group.webp" + uid=78_189 generic + uid=78_190 StaticText "Acteur (2)" + uid=78_42 InlineTextBox "Acteur (2)" + uid=78_191 listitem level="1" + uid=78_192 image "Acteur (3)" url="https://localhost:31000/systems/vermine2047/assets/icons/actors/character.webp" + uid=78_193 generic + uid=78_194 StaticText "Acteur (3)" + uid=78_42 InlineTextBox "Acteur (3)" + uid=78_195 sectionfooter + uid=78_196 ignored + uid=78_197 ignored + uid=78_198 ignored + uid=78_199 ignored + uid=78_200 ignored + uid=78_201 ignored + uid=78_202 ignored + uid=78_203 ignored + uid=78_204 ignored + uid=78_205 ignored + uid=78_206 ignored + uid=78_207 ignored + uid=78_208 ignored + uid=78_209 ignored + uid=78_210 ignored + uid=78_211 ignored + uid=78_212 ignored + uid=78_213 ignored + uid=78_214 ignored + uid=78_215 ignored + uid=78_216 ignored + uid=78_217 ignored + uid=78_218 ignored + uid=78_219 figure + uid=78_220 image url="https://localhost:31000/ui/pause.svg" + uid=78_221 Figcaption + uid=78_222 StaticText "JEU EN PAUSE" + uid=78_42 InlineTextBox "JEU EN PAUSE" + uid=78_223 form + uid=78_224 banner + uid=78_225 heading "Personnage: Acteur (3)" level="1" + uid=78_226 StaticText "Personnage: Acteur (3)" + uid=78_42 InlineTextBox "Personnage: Acteur (3)" + uid=78_227 button "Basculer les contrôles" + uid=78_228 button "Copier l'UUID du document" + uid=78_229 button "Fermer la fenêtre" + uid=78_230 generic + uid=78_231 ignored + uid=78_232 ignored + uid=78_233 button "MODE ÉDITION" + uid=78_234 StaticText "MODE ÉDITION" + uid=78_42 InlineTextBox "MODE ÉDITION" + uid=78_235 image "logo Vermine" url="https://localhost:31000/systems/vermine2047/assets/images/ui/logo.webp" + uid=78_236 ignored + uid=78_237 image "Acteur (3)" url="https://localhost:31000/systems/vermine2047/assets/icons/actors/character.webp" + uid=78_238 generic + uid=78_239 heading "ADAPTATION" level="3" + uid=78_240 StaticText "ADAPTATION" + uid=78_42 InlineTextBox "ADAPTATION" + uid=78_241 heading "L'Humain" level="5" + uid=78_242 StaticText "L'Humain" + uid=78_42 InlineTextBox "L'Humain" + uid=78_243 image url="https://localhost:31000/systems/vermine2047/assets/images/ui/totems/human.webp" + uid=78_244 heading "L'adapté" level="5" + uid=78_245 StaticText "L'adapté" + uid=78_42 InlineTextBox "L'adapté" + uid=78_246 image url="https://localhost:31000/systems/vermine2047/assets/images/ui/totems/adapted.webp" + uid=78_247 ignored + uid=78_248 ignored + uid=78_249 ignored + uid=78_250 generic + uid=78_251 generic + uid=78_252 ignored + uid=78_253 ignored + uid=78_254 ignored + uid=78_255 ignored + uid=78_256 generic + uid=78_257 generic + uid=78_258 ignored + uid=78_259 ignored + uid=78_260 list + uid=78_261 listitem level="1" + uid=78_262 ignored + uid=78_263 heading "LA HORDE" level="4" + uid=78_264 StaticText "LA HORDE" + uid=78_42 InlineTextBox "LA HORDE" + uid=78_265 image "La Horde" url="https://localhost:31000/systems/vermine2047/assets/images/ui/totems/horde.webp" + uid=78_266 navigation roledescription="Navigation dans l’onglet des feuilles de personnages" + uid=78_267 generic + uid=78_268 ignored + uid=78_269 StaticText "Caractéristiques et compétences" + uid=78_42 InlineTextBox "Caractéristiques " + uid=78_42 InlineTextBox "et compétences" + uid=78_270 generic + uid=78_271 ignored + uid=78_272 StaticText "Totem et ajustements" + uid=78_42 InlineTextBox "Totem et " + uid=78_42 InlineTextBox "ajustements" + uid=78_273 generic + uid=78_274 ignored + uid=78_275 StaticText "Matériel" + uid=78_42 InlineTextBox "Matériel" + uid=78_276 generic + uid=78_277 ignored + uid=78_278 StaticText "Histoire" + uid=78_42 InlineTextBox "Histoire" + uid=78_279 generic + uid=78_280 ignored + uid=78_281 StaticText "Combat et reserves" + uid=78_42 InlineTextBox "Combat " + uid=78_42 InlineTextBox "et " + uid=78_42 InlineTextBox "reserves" + uid=78_282 generic + uid=78_283 sectionheader + uid=78_284 generic + uid=78_285 heading "Nom Acteur (3)" level="1" + uid=78_286 LabelText + uid=78_287 StaticText "Nom" + uid=78_42 InlineTextBox "Nom" + uid=78_288 ignored + uid=78_289 StaticText "Acteur (3)" + uid=78_42 InlineTextBox "Acteur (3)" + uid=78_290 ignored + uid=78_291 LabelText + uid=78_292 StaticText "Profil" + uid=78_42 InlineTextBox "Profil" + uid=78_293 ignored + uid=78_294 ignored + uid=78_295 LabelText + uid=78_296 StaticText "Age" + uid=78_42 InlineTextBox "Age" + uid=78_297 ignored + uid=78_298 ignored + uid=78_299 StaticText "15" + uid=78_42 InlineTextBox "15" + uid=78_300 generic + uid=78_301 StaticText "(Jeune)" + uid=78_42 InlineTextBox "(Jeune)" + uid=78_302 generic + uid=78_303 heading "Totem La Horde" level="1" + uid=78_304 LabelText + uid=78_305 StaticText "Totem" + uid=78_42 InlineTextBox "Totem" + uid=78_306 generic + uid=78_307 StaticText "La Horde" + uid=78_42 InlineTextBox "La Horde" + uid=78_308 ignored + uid=78_309 LabelText + uid=78_310 StaticText "Réputation" + uid=78_42 InlineTextBox "Réputation" + uid=78_311 ignored + uid=78_312 StaticText "0" + uid=78_42 InlineTextBox "0" + uid=78_313 LabelText + uid=78_314 StaticText "Expérience" + uid=78_42 InlineTextBox "Expérience" + uid=78_315 ignored + uid=78_316 StaticText "0" + uid=78_42 InlineTextBox "0" + uid=78_317 heading "CARACTÉRISTIQUES" level="3" + uid=78_318 StaticText "CARACTÉRISTIQUES" + uid=78_42 InlineTextBox "CARACTÉRISTIQUES" + uid=78_319 ignored + uid=78_320 ignored + uid=78_321 heading "PHYSIQUE" level="4" + uid=78_322 StaticText "PHYSIQUE" + uid=78_42 InlineTextBox "PHYSIQUE" + uid=78_323 generic + uid=78_324 LabelText + uid=78_325 StaticText "Vigueur" + uid=78_42 InlineTextBox "Vigueur" + uid=78_326 generic + uid=78_327 LabelText + uid=78_328 StaticText "Santé" + uid=78_42 InlineTextBox "Santé" + uid=78_329 ignored + uid=78_330 heading "MANUEL" level="4" + uid=78_331 StaticText "MANUEL" + uid=78_42 InlineTextBox "MANUEL" + uid=78_332 generic + uid=78_333 LabelText + uid=78_334 StaticText "Précision" + uid=78_42 InlineTextBox "Précision" + uid=78_335 generic + uid=78_336 LabelText + uid=78_337 StaticText "Réflexes" + uid=78_42 InlineTextBox "Réflexes" + uid=78_338 ignored + uid=78_339 heading "MENTAL" level="4" + uid=78_340 StaticText "MENTAL" + uid=78_42 InlineTextBox "MENTAL" + uid=78_341 generic + uid=78_342 LabelText + uid=78_343 StaticText "Savoir" + uid=78_42 InlineTextBox "Savoir" + uid=78_344 generic + uid=78_345 LabelText + uid=78_346 StaticText "Perception" + uid=78_42 InlineTextBox "Perception" + uid=78_347 ignored + uid=78_348 heading "SOCIAL" level="4" + uid=78_349 StaticText "SOCIAL" + uid=78_42 InlineTextBox "SOCIAL" + uid=78_350 generic + uid=78_351 LabelText + uid=78_352 StaticText "Volonté" + uid=78_42 InlineTextBox "Volonté" + uid=78_353 generic + uid=78_354 LabelText + uid=78_355 StaticText "Empathie" + uid=78_42 InlineTextBox "Empathie" + uid=78_356 heading "COMPÉTENCES" level="3" + uid=78_357 StaticText "COMPÉTENCES" + uid=78_42 InlineTextBox "COMPÉTENCES" + uid=78_358 ignored + uid=78_359 ignored + uid=78_360 heading "L'HOMME" level="4" + uid=78_361 StaticText "L'HOMME" + uid=78_42 InlineTextBox "L'HOMME" + uid=78_362 generic + uid=78_363 LabelText + uid=78_364 StaticText "Arts " + uid=78_42 InlineTextBox "Arts " + uid=78_365 superscript + uid=78_366 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_367 generic + uid=78_368 LabelText + uid=78_369 StaticText "Civilisation " + uid=78_42 InlineTextBox "Civilisation " + uid=78_370 superscript + uid=78_371 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_372 generic + uid=78_373 LabelText + uid=78_374 StaticText "Psychologie " + uid=78_42 InlineTextBox "Psychologie " + uid=78_375 superscript + uid=78_376 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_377 generic + uid=78_378 LabelText + uid=78_379 StaticText "Rumeurs" + uid=78_42 InlineTextBox "Rumeurs" + uid=78_380 generic + uid=78_381 LabelText + uid=78_382 StaticText "Soins " + uid=78_42 InlineTextBox "Soins " + uid=78_383 superscript + uid=78_384 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_385 ignored + uid=78_386 heading "L'ANIMAL" level="4" + uid=78_387 StaticText "L'ANIMAL" + uid=78_42 InlineTextBox "L'ANIMAL" + uid=78_388 generic + uid=78_389 LabelText + uid=78_390 StaticText "Animalisme " + uid=78_42 InlineTextBox "Animalisme " + uid=78_391 superscript + uid=78_392 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_393 generic + uid=78_394 LabelText + uid=78_395 StaticText "Dissection " + uid=78_42 InlineTextBox "Dissection " + uid=78_396 superscript + uid=78_397 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_398 generic + uid=78_399 LabelText + uid=78_400 StaticText "Faune " + uid=78_42 InlineTextBox "Faune " + uid=78_401 superscript + uid=78_402 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_403 generic + uid=78_404 LabelText + uid=78_405 StaticText "Répulsion" + uid=78_42 InlineTextBox "Répulsion" + uid=78_406 generic + uid=78_407 LabelText + uid=78_408 StaticText "Pistage" + uid=78_42 InlineTextBox "Pistage" + uid=78_409 ignored + uid=78_410 heading "LA MACHINE" level="4" + uid=78_411 StaticText "LA MACHINE" + uid=78_42 InlineTextBox "LA MACHINE" + uid=78_412 generic + uid=78_413 LabelText + uid=78_414 StaticText "Artisanat " + uid=78_42 InlineTextBox "Artisanat " + uid=78_415 superscript + uid=78_416 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_417 generic + uid=78_418 LabelText + uid=78_419 StaticText "Bricolage" + uid=78_42 InlineTextBox "Bricolage" + uid=78_420 generic + uid=78_421 LabelText + uid=78_422 StaticText "Mécanique " + uid=78_42 InlineTextBox "Mécanique " + uid=78_423 superscript + uid=78_424 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_425 generic + uid=78_426 LabelText + uid=78_427 StaticText "Pilotage " + uid=78_42 InlineTextBox "Pilotage " + uid=78_428 superscript + uid=78_429 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_430 generic + uid=78_431 LabelText + uid=78_432 StaticText "Technologie " + uid=78_42 InlineTextBox "Technologie " + uid=78_433 superscript + uid=78_434 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_435 ignored + uid=78_436 heading "L'ARME" level="4" + uid=78_437 StaticText "L'ARME" + uid=78_42 InlineTextBox "L'ARME" + uid=78_438 generic + uid=78_439 LabelText + uid=78_440 StaticText "Armes à feu " + uid=78_42 InlineTextBox "Armes à feu " + uid=78_441 superscript + uid=78_442 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_443 generic + uid=78_444 LabelText + uid=78_445 StaticText "Tir à l'arc" + uid=78_42 InlineTextBox "Tir à l'arc" + uid=78_446 generic + uid=78_447 LabelText + uid=78_448 StaticText "Armurerie " + uid=78_42 InlineTextBox "Armurerie " + uid=78_449 superscript + uid=78_450 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_451 generic + uid=78_452 LabelText + uid=78_453 StaticText "Lancer" + uid=78_42 InlineTextBox "Lancer" + uid=78_454 generic + uid=78_455 LabelText + uid=78_456 StaticText "Mêlée" + uid=78_42 InlineTextBox "Mêlée" + uid=78_457 ignored + uid=78_458 heading "LA SURVIE" level="4" + uid=78_459 StaticText "LA SURVIE" + uid=78_42 InlineTextBox "LA SURVIE" + uid=78_460 generic + uid=78_461 LabelText + uid=78_462 StaticText "Vigilance" + uid=78_42 InlineTextBox "Vigilance" + uid=78_463 generic + uid=78_464 LabelText + uid=78_465 StaticText "Athlétisme" + uid=78_42 InlineTextBox "Athlétisme" + uid=78_466 generic + uid=78_467 LabelText + uid=78_468 StaticText "Alimentation" + uid=78_42 InlineTextBox "Alimentation" + uid=78_469 generic + uid=78_470 LabelText + uid=78_471 StaticText "Discrétion" + uid=78_42 InlineTextBox "Discrétion" + uid=78_472 generic + uid=78_473 LabelText + uid=78_474 StaticText "Corps-à-corps" + uid=78_42 InlineTextBox "Corps-à-corps" + uid=78_475 ignored + uid=78_476 heading "LA TERRE" level="4" + uid=78_477 StaticText "LA TERRE" + uid=78_42 InlineTextBox "LA TERRE" + uid=78_478 generic + uid=78_479 LabelText + uid=78_480 StaticText "Environnement " + uid=78_42 InlineTextBox "Environnement " + uid=78_481 superscript + uid=78_482 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_483 generic + uid=78_484 LabelText + uid=78_485 StaticText "Flore " + uid=78_42 InlineTextBox "Flore " + uid=78_486 superscript + uid=78_487 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_488 generic + uid=78_489 LabelText + uid=78_490 StaticText "Route" + uid=78_42 InlineTextBox "Route" + uid=78_491 generic + uid=78_492 LabelText + uid=78_493 StaticText "Toxiques " + uid=78_42 InlineTextBox "Toxiques " + uid=78_494 superscript + uid=78_495 StaticText "(II)" + uid=78_42 InlineTextBox "(II)" + uid=78_496 generic + uid=78_497 LabelText + uid=78_498 StaticText "Vestiges " + uid=78_42 InlineTextBox "Vestiges " + uid=78_499 superscript + uid=78_500 StaticText "(I)" + uid=78_42 InlineTextBox "(I)" + uid=78_501 ignored + uid=78_502 ignored + uid=78_503 ignored + uid=78_504 ignored + uid=78_505 ignored + uid=78_506 ignored + uid=78_507 ignored + uid=78_508 ignored + uid=78_509 ignored + uid=78_510 ignored + uid=78_511 ignored + uid=78_512 ignored + uid=78_513 ignored + uid=78_514 ignored + uid=78_515 ignored + uid=78_516 ignored + uid=78_517 ignored + uid=78_518 ignored + uid=78_519 ignored + uid=78_520 ignored + uid=78_521 ignored + uid=78_522 ignored + uid=78_523 ignored + uid=78_524 ignored + uid=78_525 ignored + uid=78_526 ignored + uid=78_527 ignored + uid=78_528 ignored + uid=78_529 ignored + uid=78_530 ignored + uid=78_531 ignored + uid=78_532 ignored + uid=78_533 ignored + uid=78_534 ignored + uid=78_535 ignored + uid=78_536 ignored + uid=78_537 ignored + uid=78_538 ignored + uid=78_539 ignored + uid=78_540 ignored + uid=78_541 ignored + uid=78_542 ignored + uid=78_543 ignored + uid=78_544 ignored + uid=78_545 ignored + uid=78_546 ignored + uid=78_547 ignored + uid=78_548 ignored + uid=78_549 ignored + uid=78_550 ignored + uid=78_551 ignored + uid=78_552 ignored + uid=78_553 ignored + uid=78_554 ignored + uid=78_555 generic diff --git a/css/vermine2047.css b/css/vermine2047.css deleted file mode 100644 index fcbdb7b..0000000 --- a/css/vermine2047.css +++ /dev/null @@ -1,583 +0,0 @@ -@import"https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap";@font-face{font-family:"DistressBlack";src:url("../assets/fonts/dcc_sharp_distress_black_by_dccanim.otf")}.sans-font{font-family:"DistressBlack",sans-serif}.app{box-shadow:0 0 20px #7e7544;color:#dfdfdf}.vermine2047.sheet.actor header.sheet-header,.vermine2047.sheet.actor .windoow-content{min-height:fit-content}.sheet .charname input{color:#191813;font-family:"DistressBlack",sans-serif;font-size:30px;font-style:normal}img.profile-img{filter:drop-shadow(0px 0px 20px rgb(110, 133, 27));height:auto;width:100%;max-width:10rem}body.system-vermine2047 img#logo{content:url("/systems/vermine2047/assets/images/ui/logo_vermine_foundry.webp");height:auto}#chat-log .message{background:url(/systems/vermine2047/assets/images/ui/box_background.webp) repeat}img{border:none}ul.unstyled{list-style-type:none;padding:0;margin:0}ul.unstyled li{padding:0;margin:0}.padding-with-frieze{margin-left:18% !important;margin-right:10% !important}.padding-with-frieze li{max-width:100%}.w-full{width:100%}.mx-auto{margin-left:auto;margin-right:auto}.system-vermine2047 .sheet .window-content{background:url(/systems/vermine2047/assets/images/ui/box_background.webp);padding:0;overflow-y:hidden}.system-vermine2047 .dialog .window-content{background:url(/systems/vermine2047/assets/images/ui/fond_chat_box.webp);padding:.5rem;overflow-y:hidden}.window-content .row.smb{margin-bottom:.25rem}.window-content .row.mdb{margin-bottom:.5rem}.window-content .row.lgb{margin-bottom:1rem}.actor.sheet form .form{display:flex;align-items:flex-start;height:100%;overflow:auto}.actor.sheet .form aside{min-width:max-content;background-image:url(/systems/vermine2047/assets/images/ui/barre_laterale.webp);background-repeat:no-repeat;background-size:auto 200%;border-right:2px #000;height:100%;padding:0 .3rem;box-shadow:-20px 0px 100px 15px rgba(0,0,0,.7098039216) inset}.actor.sheet .form aside .major-totem{position:relative}.actor.sheet .form aside .major-totem h4{position:absolute;transform:rotate(-8deg);opacity:1;transition:.2s}.actor.sheet .form main{box-shadow:10px 0px 100px rgba(0,0,0,.7098039216) inset;padding-left:1rem;grid-row:span 1/span 1;height:100%}.actor.sheet .form aside .image-wrapper{text-align:center}.actor.sheet .form aside .image-wrapper img{width:80%;height:auto}.actor.sheet .form aside .paper{margin-top:1rem;height:350px}.actor.sheet .form aside .second-paper{margin-top:4rem;height:150px}.actor.sheet .form h3{font-family:"DistressBlack",sans-serif;text-align:center;text-transform:uppercase;color:#4e564c;font-size:1.7rem;border-bottom:none;margin:0}.actor.sheet .form h4,.item.sheet .form h4{font-family:"DistressBlack",sans-serif;font-size:1.4em;text-transform:uppercase;margin:0 0 .2rem}.actor.sheet .form .characteristics h4{font-size:1.25rem;margin-top:0 .1rem}.actor.sheet .form .tab.totem h4,.actor.sheet .form .tab.equipment h4,.actor.sheet .form .tab.stories h4{margin-top:.875rem}.system-vermine2047 .char-header{font-family:"DistressBlack",sans-serif}.system-vermine2047 .char-header section{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start}.system-vermine2047 .char-header h1.char-name,.system-vermine2047 .char-vermine2047{border-bottom:none;line-height:2rem}.system-vermine2047 .char-vermine2047{font-size:1.5rem}.system-vermine2047 .sheet.actor form{width:100%;height:100%;overflow:hidden}.system-vermine2047 .sheet.actor form div.hexa{clip-path:polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);background:radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);height:1.5rem;max-width:1.5rem;color:#000;transform:rotate(90deg);transition:.2s;margin:.2rem}.system-vermine2047 .sheet.actor form div.hexa:hover{background:radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%)}.system-vermine2047 .sheet.actor form div.hexa input{opacity:1;min-width:100%;min-height:100%;opacity:0}.system-vermine2047 .sheet.actor form div.hexa.checked{background:radial-gradient(circle, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0.288) 100%)}.system-vermine2047 .sheet.actor form div.hexa.checked:hover{background:radial-gradient(circle, rgb(43, 43, 43) 0%, rgba(0, 0, 0, 0.288) 100%)}.system-vermine2047 .sheet.actor form div.hexa.unavailable{background:radial-gradient(circle, rgba(66, 15, 15, 0.664) 0%, rgba(131, 70, 70, 0.432) 100%)}.system-vermine2047 .sheet.actor .totem-details{position:relative}.system-vermine2047 .sheet.actor .totem-details img.img-totem{transform-origin:50% 50%;filter:grayscale(1);opacity:.15;position:absolute;width:30%;height:auto;pointer-events:none;aspect-ratio:1/1;left:35%}.system-vermine2047 .sheet.actor div.minor-totems{position:relative;background-color:rgba(146,156,111,.5215686275)}.system-vermine2047 .sheet.actor div.minor-totems h5{position:absolute;top:0}.system-vermine2047 .sheet.actor div.minor-totems h5 img{max-width:2rem;position:absolute;bottom:-2rem}.system-vermine2047 .sheet.actor div.minor-totems h5.human,.system-vermine2047 .sheet.actor div.minor-totems h5.adapted{transition:.3s}.system-vermine2047 .sheet.actor div.minor-totems h5.human img.img-totem,.system-vermine2047 .sheet.actor div.minor-totems h5.adapted img.img-totem{filter:drop-shadow(0px 0px 20px rgb(0, 0, 0))}.system-vermine2047 .sheet.actor div.minor-totems h5.human.major,.system-vermine2047 .sheet.actor div.minor-totems h5.adapted.major{transform:scale(1.1)}.system-vermine2047 .sheet.actor div.minor-totems h5.human.major img,.system-vermine2047 .sheet.actor div.minor-totems h5.adapted.major img{filter:drop-shadow(0px 0px 10px red)}.system-vermine2047 .sheet.actor div.minor-totems .totem-dice .human-dice,.system-vermine2047 .sheet.actor div.minor-totems .totem-dice .adapted-dice{display:flex;flex-direction:row;margin-left:2rem}.system-vermine2047 .sheet.actor div.minor-totems .totem-dice .human-dice i,.system-vermine2047 .sheet.actor div.minor-totems .totem-dice .adapted-dice i{padding-top:.5rem;color:#064930}.system-vermine2047 .sheet.actor div.minor-totems .totem-dice .adapted-dice{justify-content:flex-end;margin-left:0;margin-right:2rem;transform:rotate(180deg)}.system-vermine2047 .sheet.actor div.minor-totems .totem-dice .adapted-dice i{transform:rotate(180deg);padding-top:.5rem;color:#553402}.system-vermine2047 .sheet.actor div.minor-totems .human{left:0}.system-vermine2047 .sheet.actor div.minor-totems .human img{left:0}.system-vermine2047 .sheet.actor div.minor-totems .adapted{right:0}.system-vermine2047 .sheet.actor div.minor-totems .adapted img{right:0}.system-vermine2047 .sheet.actor form input[type=text],.system-vermine2047 .sheet.actor form input[type=number]{width:calc(100% - 2px);height:calc(100% - 2px);background:none;padding:0;margin:1px 0;color:#333;border:1px solid rgba(0,0,0,0)}.system-vermine2047 .sheet.actor form input[type=text].hexa,.system-vermine2047 .sheet.actor form input[type=number].hexa{clip-path:polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);background:radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);height:1.5rem;max-width:1.5rem;color:#000}.system-vermine2047 .sheet.actor form input[disabled],.system-vermine2047 .sheet.actor form select[disabled]{cursor:not-allowed}.system-vermine2047 .sheet.actor form input[type=text]:hover:not(:disabled),.system-vermine2047 .sheet.actor form input[type=text]:focus,.system-vermine2047 .sheet.actor form select:hover:not(:disabled),.system-vermine2047 .sheet.actor form select:focus,.system-vermine2047 .sheet.actor form input[type=number]:hover:not(:disabled),.system-vermine2047 .sheet.actor form input[type=number]:focus,.system-vermine2047 .sheet.actor form textarea:hover:not(:disabled),.system-vermine2047 .sheet.actor form textarea:focus{box-shadow:0 0 10px #005a3c inset}.system-vermine2047 .sheet.actor form select{font-size:.6rem;border:none;appearance:none;min-width:fit-content;max-width:fit-content;padding:0 .2rem;margin:0 .2rem;cursor:help}.system-vermine2047 .sheet.actor form label{display:block}.system-vermine2047 .sheet.actor form .mce-panel span{display:inherit}.system-vermine2047 .sheet.actor form.editable .rollable:hover,.system-vermine2047 .sheet.actor form.editable a:hover{color:#000;text-shadow:0 5px 5px #1fa832;cursor:pointer}.system-vermine2047 .sheet.actor form .sheet-tabs{font-weight:500;height:30px}.system-vermine2047 .sheet.actor form .sheet-tabs>.list-row{line-height:24px;padding-top:3px;font-size:2rem;text-align:center}.system-vermine2047 .sheet.actor form .sheet-tabs>.list-row:last-of-type{padding-right:4px}.system-vermine2047 .sheet.actor form .sheet-tabs>.list-row.active{color:#000;font-weight:700}.system-vermine2047 .sheet.actor form .tab{flex:1;overflow:hidden}.system-vermine2047 .sheet.actor form .tag-legacy{float:left;margin:0 2px 2px 0;padding:0 3px;font-size:var(--font-size-10);line-height:16px;border:1px solid #999;border-radius:3px;white-space:normal;font-weight:500}.system-vermine2047 .sheet.actor,.system-vermine2047 .sheet.actor .window-content{min-width:690px}.system-vermine2047 .sheet.actor .sidebar{flex:.2}.system-vermine2047 .sheet.actor .floatright{float:right}.system-vermine2047 .sheet.actor .sheet-upper{height:268px}.system-vermine2047 .sheet.actor .sheet-upper .sheet-header{height:48px}.system-vermine2047 .sheet.actor .sheet-upper .sheet-profile,.system-vermine2047 .sheet.actor .sheet-upper .sheet-showcase{height:220px}.system-vermine2047 .sheet.actor .sheet-content{padding:4px}.system-vermine2047 .sheet.actor .sheet-sidebar{height:calc(100% - 48px);display:flex;flex-direction:column;flex-wrap:nowrap;overflow-x:hidden;overflow-y:auto}.system-vermine2047 .sheet.actor .sheet-sidebar>*{flex:1}.system-vermine2047 .sheet.actor .sheet-sidebar .sidebar-summary{overflow-y:hidden}.system-vermine2047 .sheet.actor.npc-sheet .sheet-upper{height:220px}.system-vermine2047 .sheet.actor.npc-sheet .sheet-upper .sheet-showcase{height:172px}.system-vermine2047 .sheet.actor.npc-sheet .sheet-lower{height:calc(100% - 220px - 32px)}.system-vermine2047 .sheet.actor .sheet-navigation{border-top:1px solid var(--secondary-background);border-bottom:1px solid var(--primary-background)}.system-vermine2047 .sheet.actor .sheet-navigation .sheet-tabs>.list-row{border-radius:5px 5px 0 0}.system-vermine2047 .sheet.actor .sheet-navigation .sheet-tabs>.list-row.active{border:1px solid #666;border-bottom:none;background:var(--primary-background);color:#fff;text-shadow:none;color:#000;text-shadow:0 0 10px #00005a;cursor:pointer}.actor.sheet nav.sheet-navigation{display:inline-flex;justify-content:space-around;align-items:center;height:54px;background:url(../assets/images/ui/barre_haut.webp) no-repeat right top;background-size:100% 100%;width:100%;position:relative;padding-right:4rem;font-size:1.4rem}.actor.sheet nav.sheet-navigation.tabs .item{height:2.4rem;display:inline-block;z-index:1;transition:all .1s ease-out;color:#606060;box-shadow:0px 0px 0px rgba(0,0,0,.404)}.actor.sheet nav.sheet-navigation.tabs .item:hover,.actor.sheet nav.sheet-navigation.tabs .item.active{color:#000;text-shadow:0 5px 5px #1fa832;cursor:pointer}.actor.sheet nav.sheet-navigation.tabs .item:hover{text-shadow:0 5px 5px rgba(30,82,37,.6039215686)}.system-vermine2047 .sheet.actor form nav.sheet-navigation.sheet-tabs{height:54px}@container ability-row (max-width: 240px){.skill-dots,span.hexa{display:none}}.system-vermine2047 .sheet.actor .ability{padding-right:.6rem;font-size:.8rem;border-bottom:1px solid rgba(170,170,152,.664);box-shadow:0px 0px 15px rgba(128,128,128,0) inset;transition:.2s;position:relative;flex-wrap:nowrap;min-width:min-content;container-type:inline-size;container-name:ability-row}.system-vermine2047 .sheet.actor .ability:hover{box-shadow:0px 0px 15px gray inset}.system-vermine2047 .sheet.actor .ability label{min-width:40%;flex:1.3}.system-vermine2047 .sheet.actor .ability span{max-width:fit-content;margin:0 1rem;flex:.5}.system-vermine2047 .sheet.actor .ability div.specialties{position:absolute;bottom:-0.2rem;font-size:.7rem}.system-vermine2047 .sheet.actor .ability .skill-dots{height:100%;align-self:center;flex:1.5;min-width:fit-content}.system-vermine2047 .sheet.actor .ability .skill-dots>div{max-width:.7rem;height:.7rem;aspect-ratio:1/1;border-radius:50%;font-weight:700;text-align:center;padding-bottom:.2rem;font-style:oblique;align-self:flex-start}.system-vermine2047 .sheet.actor .ability .skill-dots>div.dice-pool-dot{background:radial-gradient(circle, rgb(94, 90, 77) 25%, rgb(0, 0, 0) 100%);max-width:.7rem;aspect-ratio:1/1;border-radius:50%}.system-vermine2047 .sheet.actor .ability .skill-dots>div.dice-reroll-dot{background:radial-gradient(circle, rgb(187, 182, 165) 25%, rgb(0, 0, 0) 100%)}.system-vermine2047 .sheet.actor .skill-category{padding:.3rem}.system-vermine2047 .sheet.actor .skill-category.preferred{box-shadow:0px 0px 30px rgba(0,128,0,.306) inset}.system-vermine2047 .sheet.actor .skill-category.preferred h4,.system-vermine2047 .sheet.actor .skill-category.preferred label{text-shadow:0px 0px 5px rgba(0,128,0,.411)}.system-vermine2047 .sheet.actor #edit{background-color:#000;color:#fff;width:100%}.system-vermine2047 .sheet.actor .reserve-grid{line-height:.5rem;transform-origin:0% 50%;max-width:fit-content;align-items:center;display:flex;flex-direction:column}.system-vermine2047 .sheet.actor .reserve-grid div.flexrow,.system-vermine2047 .sheet.actor .reserve-grid input,.system-vermine2047 .sheet.actor .reserve-grid .hexa{margin:0;padding:0;min-width:1rem;min-height:1rem}.system-vermine2047 .sheet.actor .reserve-grid>.flexrow{position:relative;max-width:fit-content;justify-content:center}.hexa{text-align:center;clip-path:polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);background:radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);max-height:1.5rem;max-width:1.5rem;min-width:1.5rem;aspect-ratio:1/1;color:#000;vertical-align:center}.hexa.checked{background:radial-gradient(circle, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0.288) 100%)}.hexa input{width:1rem}.hexa input[type=radio]{opacity:0}.hexa input[type=radio]::after,.hexa input[type=radio]::before{display:none}.window-app{font-family:"Roboto",sans-serif;box-shadow:0px 0px 30px #454e2c}.rollable:hover,.rollable:focus{color:#000;text-shadow:0 0 10px red;cursor:pointer}.grid,.grid-2col{display:grid;grid-column:span 2/span 2;grid-template-columns:repeat(2, minmax(0, 1fr));gap:10px;margin:5px 0;padding:0}.grid-3col{grid-column:span 3/span 3;grid-template-columns:repeat(3, minmax(0, 1fr))}.grid-4col{grid-column:span 4/span 4;grid-template-columns:repeat(4, minmax(0, 1fr))}.grid-5col{grid-column:span 5/span 5;grid-template-columns:repeat(5, minmax(0, 1fr))}.grid-6col{grid-column:span 6/span 6;grid-template-columns:repeat(6, minmax(0, 1fr))}.grid-7col{grid-column:span 7/span 7;grid-template-columns:repeat(7, minmax(0, 1fr))}.grid-8col{grid-column:span 8/span 8;grid-template-columns:repeat(8, minmax(0, 1fr))}.grid-9col{grid-column:span 9/span 9;grid-template-columns:repeat(9, minmax(0, 1fr))}.grid-10col{grid-column:span 10/span 10;grid-template-columns:repeat(10, minmax(0, 1fr))}.grid-11col{grid-column:span 11/span 11;grid-template-columns:repeat(11, minmax(0, 1fr))}.grid-12col{grid-column:span 12/span 12;grid-template-columns:repeat(12, minmax(0, 1fr))}.grid-start-2{grid-column-start:2}.grid-start-3{grid-column-start:3}.grid-start-4{grid-column-start:4}.grid-start-5{grid-column-start:5}.grid-start-6{grid-column-start:6}.grid-start-7{grid-column-start:7}.grid-start-8{grid-column-start:8}.grid-start-9{grid-column-start:9}.grid-start-10{grid-column-start:10}.grid-start-11{grid-column-start:11}.grid-start-12{grid-column-start:12}.grid-span-2{grid-column-end:span 2}.grid-span-3{grid-column-end:span 3}.grid-span-4{grid-column-end:span 4}.grid-span-5{grid-column-end:span 5}.grid-span-6{grid-column-end:span 6}.grid-span-7{grid-column-end:span 7}.grid-span-8{grid-column-end:span 8}.grid-span-9{grid-column-end:span 9}.grid-span-10{grid-column-end:span 10}.grid-span-11{grid-column-end:span 11}.grid-span-12{grid-column-end:span 12}.flex-group-center,.flex-group-left,.flex-group-right{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;text-align:center}.flex-group-left{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:left}.flex-group-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:right}.flex-align-left{align-items:flex-start}.flex-align-right{align-items:flex-end}.gap-xs{gap:2px}.gap-sm{gap:4px}.gap-md{gap:8px}.gap-lg{gap:16px}.flexshrink{-webkit-box-flex:0;-ms-flex:0;flex:0}.flex-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.flexlarge{-webkit-box-flex:2;-ms-flex:2;flex:2}.align-left{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:left}.align-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:right}.align-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.system-vermine2047 .item-form{font-family:"Roboto",sans-serif}.system-vermine2047 .sheet-header{-webkit-box-flex:0;-ms-flex:0 auto;flex:0 auto;overflow:hidden;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;margin-bottom:10px}.system-vermine2047 .sheet-header .profile-img{-webkit-box-flex:0;-ms-flex:0 0 100px;flex:0 0 100px;height:100px;margin-right:10px}.system-vermine2047 .sheet-header .header-fields{-webkit-box-flex:1;-ms-flex:1;flex:1}.system-vermine2047 .sheet-header h1.charname{height:50px;padding:0px;margin:5px 0;border-bottom:0}.system-vermine2047 .sheet-header h1.charname input{width:100%;height:100%;margin:0}.system-vermine2047 .sheet-tabs{-webkit-box-flex:0;-ms-flex:0;flex:0}.system-vermine2047 .sheet-body .tab,.editor{height:100%;width:100%}.editor{min-height:75px;margin-bottom:1rem;min-width:100%}.editor .editor-content{min-width:100%;min-height:3rem}editor:hover .editor-edit{display:block}.system-vermine2047 .tox{min-height:25vh}.system-vermine2047 .tox .tox-editor-container{background:#fff}.system-vermine2047 .tox .tox-edit-area{padding:0 8px}.system-vermine2047 .resource-label{font-weight:bold}.system-vermine2047 .items-header{height:28px;margin:2px 0;padding:0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background:rgba(0,0,0,.05);border:2px groove #eeede0;font-weight:bold}.system-vermine2047 .items-header>*{font-size:14px;text-align:center}.system-vermine2047 .items-header .item-name{font-weight:bold;padding-left:5px;text-align:left;display:-webkit-box;display:-ms-flexbox;display:flex}.system-vermine2047 .items-list{list-style:none;margin:0;padding:0;overflow-y:auto;scrollbar-width:thin;color:#444}.system-vermine2047 .items-list .item-list{list-style:none;margin:0;padding:0}.system-vermine2047 .items-list .item-name{-webkit-box-flex:2;-ms-flex:2;flex:2;margin:0;overflow:hidden;font-size:13px;text-align:left;-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.system-vermine2047 .items-list .item-name h3,.system-vermine2047 .items-list .item-name h4{margin:0;white-space:nowrap;overflow-x:hidden}.system-vermine2047 .items-list .item-controls{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.system-vermine2047 .items-list .item-controls a{font-size:12px;text-align:center;margin:0 6px}.system-vermine2047 .items-list .item{-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 2px;border-bottom:1px solid #c9c7b8}.system-vermine2047 .items-list .item:last-child{border-bottom:none}.system-vermine2047 .items-list .item .item-name{color:#191813}.system-vermine2047 .items-list .item .item-name .item-image{-webkit-box-flex:0;-ms-flex:0 0 30px;flex:0 0 30px;height:30px;background-size:30px;border:none;margin-right:5px}.system-vermine2047 .items-list .item-prop{text-align:center;border-left:1px solid #c9c7b8;border-right:1px solid #c9c7b8;font-size:12px}.system-vermine2047 .items-list .items-header{height:28px;margin:2px 0;padding:0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background:rgba(0,0,0,.05);border:2px groove #eeede0;font-weight:bold}.system-vermine2047 .items-list .items-header>*{font-size:12px;text-align:center}.system-vermine2047 .items-list .items-header .item-name{padding-left:5px;text-align:left}.system-vermine2047 .item-formula{-webkit-box-flex:0;-ms-flex:0 0 200px;flex:0 0 200px;padding:0 8px}.system-vermine2047 .effects .item .effect-source,.system-vermine2047 .effects .item .effect-duration,.system-vermine2047 .effects .item .effect-controls{text-align:center;border-left:1px solid #c9c7b8;border-right:1px solid #c9c7b8;font-size:12px}.system-vermine2047 .effects .item .effect-controls{border:none}.chat-message .message-header{line-height:20px;color:#fff;text-shadow:0px 0px 5px #000;background:rgba(25,24,19,.368627451)}span.game-mode{font-family:"DistressBlack",sans-serif;position:absolute;margin-left:auto;color:rgba(0,0,0,0);top:1rem;z-index:900;width:55%;text-align:center;text-transform:uppercase;font-weight:900;background:linear-gradient(180deg, rgba(255, 255, 255, 0.767) 0%, rgba(0, 0, 0, 0.61) 17%, rgba(0, 0, 0, 0.548) 19%, rgba(222, 255, 221, 0.575) 24%, rgba(255, 255, 255, 0.637) 43%, rgba(0, 0, 0, 0.486) 47%, rgba(254, 255, 254, 0.466) 50%, rgba(0, 0, 0, 0.699) 63%, rgba(134, 160, 137, 0.479) 64%, rgba(213, 248, 210, 0.493) 100%);background-clip:text}span.game-mode#game-mode-1{color:rgba(235,218,143,.8)}span.game-mode#game-mode-2{color:#83f883}span.game-mode#game-mode-3{color:rgba(245,124,124,.8)}.shadow{box-shadow:0 .5rem 2rem #000;margin:2rem 0}ol#chat-log header.message-header{background-color:#000;padding:0 1rem}ol#chat-log .vermine-roll-message{overflow:hidden;box-shadow:0px 0px 30px #fff inset;padding:0;position:relative}ol#chat-log .vermine-roll-message .flexrow{align-items:center;box-shadow:0px 5px 10px 0px #000}ol#chat-log .vermine-roll-message h3,ol#chat-log .vermine-roll-message h4{text-transform:uppercase;font-family:"DistressBlack";margin-top:1rem;border-bottom:none;font-weight:900;background:50% 0%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp)}ol#chat-log .vermine-roll-message h3+span,ol#chat-log .vermine-roll-message h4+span{font-family:"DistressBlack";font-size:large;text-transform:unset;padding-left:2rem;background:-100% 0%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp)}ol#chat-log .vermine-roll-message h3+span#allowed_reroll,ol#chat-log .vermine-roll-message h4+span#allowed_reroll{font-size:large}ol#chat-log .vermine-roll-message h3{background:url(/systems/vermine2047/assets/images/ui/scotch.webp);background-position:center;background-size:200%}ol#chat-log .vermine-roll-message h4{text-align:center}ol#chat-log .vermine-roll-message div.roll-total{transform:rotate(-3deg) scale(1.2) translateX(2rem) translateY(0.5rem);background:url(/systems/vermine2047/assets/images/ui/scotch.webp);background-position:center;background-size:200%;margin-bottom:2rem;padding:0;z-index:1;width:75%}ol#chat-log .vermine-roll-message div.reroll{transition:.3s;max-height:1px;overflow:hidden;justify-content:end;text-align:center;align-items:center}ol#chat-log .vermine-roll-message div.reroll button{text-transform:uppercase;font-family:"DistressBlack";padding:0 1rem;max-width:fit-content;box-shadow:0px 0px 2px #000;background:50% 0%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp)}ol#chat-log .vermine-roll-message div.reroll.visible{max-height:15rem}ol#chat-log .vermine-roll-message ul.roll-results{list-style:none}ol#chat-log .vermine-roll-message ul.roll-results li.die{position:relative;max-width:3rem;line-height:3rem;float:left;margin:.2rem;background-image:url(/icons/dice/d10black.svg);background-position:center;background-repeat:no-repeat;background-size:contain;font-weight:800;font-size:1rem;color:#fff;text-align:center;transition:.3s;border-bottom:5px solid red;border-radius:2rem}ol#chat-log .vermine-roll-message ul.roll-results li.die::after{content:"";position:absolute;top:-1rem;text-wrap:nowrap;color:#fff;font-weight:100;font-size:smaller;text-align:center;opacity:0;text-shadow:0px 0px 5px #000}ol#chat-log .vermine-roll-message ul.roll-results li.die:hover::after{opacity:1;color:#fff}ol#chat-log .vermine-roll-message ul.roll-results li.die.human,ol#chat-log .vermine-roll-message ul.roll-results li.die.adapted{border-top:5px solid #ffd900}ol#chat-log .vermine-roll-message ul.roll-results li.die.human::after,ol#chat-log .vermine-roll-message ul.roll-results li.die.adapted::after{content:""}ol#chat-log .vermine-roll-message ul.roll-results li.die.rerollable{cursor:pointer}ol#chat-log .vermine-roll-message ul.roll-results li.die.rerollable:hover{transform:translateY(0.5rem)}ol#chat-log .vermine-roll-message ul.roll-results li.die.success{border-bottom:5px solid #008f07}ol#chat-log .vermine-roll-message ul.roll-results li.die.adapted::after{content:"adapté"}ol#chat-log .vermine-roll-message ul.roll-results li.die.human::after{content:"humain"}ol#chat-log .vermine-roll-message ul.roll-results li.die.rerolled{transform:translateY(0rem)}ol#chat-log .vermine-roll-message ul.roll-results li.die span{text-align:center;font-size:larger;text-shadow:0px 0px 8px #000}ol#chat-log div.item-card header img{max-width:30%}.sheet.item.vermine2047 .window-content{padding:0 1rem}.sheet.item.vermine2047 .window-content .flexrow{align-items:center}.sheet.item.vermine2047 .window-content header,.sheet.item.vermine2047 .window-content h1,.sheet.item.vermine2047 .window-content h2,.sheet.item.vermine2047 .window-content h3,.sheet.item.vermine2047 .window-content h4,.sheet.item.vermine2047 .window-content h5{background:50% 50%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp);text-transform:uppercase;font-family:"DistressBlack";margin-top:1rem;border-bottom:none}.sheet.item.vermine2047 .window-content h2,.sheet.item.vermine2047 .window-content h3,.sheet.item.vermine2047 .window-content h4{text-align:center}.sheet.item.vermine2047 .window-content h5{margin-bottom:0}.sheet.item.vermine2047 .window-content .resource{border:none;border-left:1px solid gray;padding:.2rem 1rem;text-align:center}.sheet.item.vermine2047 .window-content .resource .flexrow{min-width:5rem;box-shadow:none}.sheet.item.vermine2047 .window-content .damages-row{margin:0}.sheet.item.vermine2047 .window-content .damages-row .radios{margin:0;padding:.5rem}.sheet.item.vermine2047 .window-content .damage-pannes,.sheet.item.vermine2047 .window-content .damage-state,.sheet.item.vermine2047 .window-content .damage-effect{text-align:center;font-family:"DistressBlack"}.sheet.item.vermine2047 .window-content .traits{box-shadow:0px 5px 15px #000}.sheet.item.vermine2047 .window-content .traits h3{margin:0}.sheet.item.vermine2047 .window-content .traits h4{margin:0}.window-app.vermineDialog{max-width:50vw;height:fit-content}.window-app.vermineDialog .window-content{background:url(/systems/vermine2047/assets/images/ui/box_background.webp) repeat;color:#000}.window-app.vermineDialog details>summary::after{content:"▶️";position:relative;right:40%}.window-app.vermineDialog details[open]>summary::after{content:"🔽"}.window-app.vermineDialog .grid{justify-content:space-around;box-shadow:0px 1px 10px rgba(0,0,0,.555);align-items:center;padding:.5rem .2rem}.window-app.vermineDialog .grid>*{margin:0 .3rem}.window-app.vermineDialog label{font-family:"DistressBlack",sans-serif;font-size:larger}.window-app.vermineDialog select{max-width:fit-content}.window-app.vermineDialog select option{max-width:fit-content}.window-app.vermineDialog .dialog-buttons{display:flex;justify-content:space-around;flex-direction:row}.window-app.vermineDialog .dialog-buttons button{display:block;flex:.3}input[type=range]{appearance:none;background:rgba(0,0,0,0);cursor:pointer;width:100%}input[type=range]::-webkit-slider-runnable-track{background:url(../assets/images/ui/scotch.webp) no-repeat center;background-size:100% auto;height:.4rem;border:none;box-shadow:0px 0px 13px rgba(31,26,26,.979) inset}input[type=range]::-webkit-slider-thumb{appearance:none;margin-top:-0.3rem;height:1rem;width:1rem;border:none;border-radius:50%;background:url(/systems/vermine2047/assets/images/ui/totems/human.webp);background-size:cover;filter:contrast(2);box-shadow:0px 0px 10px #000}input[type=range]:focus::-webkit-slider-thumb{box-shadow:0px 0px 10px #ff0}select{border:none;background:url(../assets/images/ui/scotch.webp);background-size:100% 100%;box-shadow:0px 0px 3px rgba(31,26,26,.979) inset}select[disabled]{color:#000;text-shadow:0px 0px 15px #000}select option{appearance:none;border:none;background:url(../assets/images/ui/scotch.webp);background-size:100% 100%}input[type=checkbox],input[type=radio]{-webkit-appearance:none;appearance:none;background:rgba(0,0,0,0);box-shadow:0px 0px 3px #85854e;cursor:pointer;width:1.5rem;height:1rem;border-radius:.4rem;transition:.3s;clip-path:polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);box-shadow:0px 0px 6px #000 inset;background-color:rgba(61,11,11,.658)}input[type=checkbox][disabled=true],input[type=radio][disabled=true]{filter:grayscale(1)}input[type=checkbox]:after,input[type=radio]:after{content:" ";background:url(/systems/vermine2047/assets/images/ui/totems/human.webp);background-size:50% 150%;background-repeat:no-repeat;position:relative;top:10%;left:0%;width:100%;height:80%;display:block;border-radius:0%;padding:0;transition:.3s}input[type=checkbox]:checked,input[type=radio]:checked{background-color:rgba(26,107,12,.658)}input[type=checkbox]:checked:after,input[type=radio]:checked:after{font-weight:900;background-color:rgba(26,1,1,0);left:50%}iframe{min-height:500px}iframe .tabs.moods-headings{max-width:1px}input[type=radio]{width:1rem;height:1rem}input[type=radio]:after{width:.8rem;background-size:100% 100%;top:5%;left:5%;width:90%;height:90%;background-size:30% 30%;background-position:center}input[type=radio]:not([disabled]):hover::after{background-size:90% 90%}input[type=radio]:checked::after{content:"";background-size:70% 70%;top:5%;left:5%;position:relative;background-color:rgba(26,1,1,0)}.app .actor.choose div.actor{position:relative}.app .actor.choose div.actor img{border-radius:50%;box-shadow:0px 0px 8px #000}.app .actor.choose div.actor span.actor-name{position:absolute;text-align:center;background-color:rgba(255,255,255,.562);border:5px;width:100%;padding:0 1rem;border-radius:5px;box-shadow:0px 0px 8px #000}.flex-group-center,.flex-group-left,.flex-group-right{justify-content:center;align-items:center;text-align:center}.flex-group-left{justify-content:flex-start;text-align:left}.flex-group-right{justify-content:flex-end;text-align:right}.flexshrink{flex:0}.flex-center{align-items:center;justify-content:center}.flex-between{justify-content:space-between}.flex-around{justify-content:space-around}.flexlarge{flex:2}.flexsmall{flex:.5}.align-left{justify-content:flex-start;text-align:left}.align-right{justify-content:flex-end;text-align:right}.align-center{align-items:center;justify-content:center;text-align:center}.app.vermine2047.trait-selector .form-group{box-shadow:0 0 30px gray;padding:.3rem .5rem;border:3px solid #8e9010}.app.vermine2047.trait-selector .form-group:has(input[type=checkbox]:checked){border:3px solid green}.app.vermine2047.trait-selector .form-group label{display:inline-flex;align-items:center;justify-content:space-around;width:100%;text-align:center;border-bottom:2px solid #000}:root{--color-text-light-highlight: #96d696;--color-text-light-heading: #9fd8a8;--color-text-light-primary: #a4b5b3;--color-text-dark-primary: #131919;--color-text-dark-secondary: #444b4a;--color-text-dark-header: #1d2223;--color-text-dark-inactive: #71797a;--color-text-hyperlink: #5aaf0a;--color-text-light-0: #fff;--color-text-light-1: #e0f0f0;--color-text-light-2: #c9e0c0;--color-text-light-3: #90c4a4;--color-text-light-4: #80c08b;--color-text-light-5: #60b06b;--color-text-light-6: #40a05d;--color-text-light-7: #208028;--color-text-dark-1: #111;--color-text-dark-2: #222;--color-text-dark-3: #444;--color-text-dark-4: #555;--color-text-dark-5: #666;--color-text-dark-6: #777;--color-border-light-1: #b0d9b0;--color-border-light-2: #80c0c0;--color-border-dark-1: #131919;--color-border-dark-2: #1d2223;--color-border-dark-3: #2d3333;--color-border-dark-4: #3d4444;--color-border-dark-5: #668888;--color-shadow-primary: #7bb60d;--color-shadow-highlight: #85cc01d0;--color-shadow-dark: #000;--color-underline-inactive: #71797a;--color-underline-active: #1a1944;--color-underline-header: #228247;--color-border-light-highlight: #b0d9b0;--color-border-light-primary: #a4b5b3;--color-border-light-secondary: #9fc7d8;--color-border-light-tertiary: #71797a;--color-border-dark: #000;--color-border-dark-primary: #131919;--color-border-dark-secondary: #1d2223;--color-border-dark-tertiary: #444b4a;--color-border-highlight: #85c019;--color-border-highlight-alt: #70c008;--color-bg-btn-minor-inactive: #9fc7d8;--color-bg-btn-minor-active: #a4b5b3;--color-bg-option: #ccdada;--color-checkbox-checked: #666;--color-ownership-none: #00ff55;--color-ownership-observer: #71797a;--color-ownership-owner: #a4b5b3;--z-index-canvas: 0;--z-index-app: 30;--z-index-ui: 60;--z-index-window: 100;--z-index-tooltip: 9999;--sidebar-width: 300px;--sidebar-header-height: 32px;--sidebar-item-height: 48px;--hotbar-height: 52px;--hotbar-width: 578px;--macro-size: 50px;--players-width: 200px;--form-field-height: 26px;--font-primary: "Signika", sans-serif;--font-mono: monospace;--font-awesome: "Font Awesome 6 Pro";--font-size-11: 0.6875rem;--font-size-12: 0.75rem;--font-size-13: 0.8125rem;--font-size-14: 0.875rem;--font-size-16: 1rem;--font-size-18: 1.125rem;--font-size-20: 1.25rem;--font-size-24: 1.5rem;--font-size-28: 1.75rem;--font-size-32: 2rem;--font-size-48: 3rem;--line-height-12: 0.75rem;--line-height-16: 1rem;--line-height-20: 1.25rem;--line-height-30: 1.875rem;--color-level-info: #b95c87;--color-level-warning: #04b184;--color-level-error: #03750;--color-level-success: #3c266c}::-webkit-scrollbar-thumb{outline:none;border-radius:3px;background:#577822;border:1px solid var(--color-border-highlight)}::-webkit-scrollbar{width:3px;height:3px} - -/* ============================================ - NPC SHEET SPECIFIC STYLES - ============================================ */ - -/* NPC Sheet Container */ -.sheet.npc .ability-value, -.sheet.npc .resource-value { - min-width: 30px; - text-align: center; - font-weight: bold; - margin-left: 8px; -} - -/* Card styling for NPC threat/experience/role */ -.sheet.npc .card, -.sheet.npc .npc-card { - border: 1px solid #444; - border-radius: 4px; - padding: 10px; - margin-bottom: 15px; - background: rgba(0, 0, 0, 0.1); -} - -.sheet.npc .card h4, -.sheet.npc .npc-card h4 { - margin-top: 0; - border-bottom: 1px solid #666; - padding-bottom: 5px; - text-align: center; -} - -.sheet.npc .card h4 i, -.sheet.npc .npc-card h4 i { - margin-right: 5px; -} - -/* Skills container */ -.sheet.npc .skills-container { - display: flex; - flex-direction: column; - gap: 15px; -} - -.sheet.npc .skill-category { - padding: 10px; - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; -} - -.sheet.npc .skill-category h4 { - margin-top: 0; - margin-bottom: 8px; - font-size: 14px; - font-weight: bold; -} - -/* Skill item */ -.sheet.npc .skill-item { - display: flex; - align-items: center; - gap: 8px; - padding: 5px; - border-bottom: 1px solid #333; -} - -.sheet.npc .skill-item:last-child { - border-bottom: none; -} - -.sheet.npc .skill-item label { - flex: 1; - font-size: 12px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.sheet.npc .skill-control { - display: flex; - align-items: center; - gap: 5px; -} - -.sheet.npc .skill-value { - min-width: 25px; - text-align: center; - font-weight: bold; -} - -/* Rarity badge */ -.sheet.npc .rarity-badge { - display: inline-block; - padding: 1px 4px; - border-radius: 3px; - font-size: 10px; - font-weight: bold; - margin-left: 5px; -} - -.sheet.npc .rarity-badge.rarity-0 { - background: #444; - color: #aaa; -} - -.sheet.npc .rarity-badge.rarity-1 { - background: #5a7a5a; - color: #fff; -} - -.sheet.npc .rarity-badge.rarity-2 { - background: #7a9a7a; - color: #000; -} - -.sheet.npc .rarity-badge.rarity-3 { - background: #9a5a9a; - color: #fff; -} - -/* Skill category selector */ -.sheet.npc .skill-category-selector { - margin-bottom: 15px; - padding: 10px; - background: rgba(0, 0, 0, 0.15); - border-radius: 4px; -} - -.sheet.npc .skill-category-selector label { - margin-right: 10px; - font-weight: bold; -} - -/* Wounds card */ -.sheet.npc .wounds-card { - margin-top: 15px; -} - -.sheet.npc .wound-item { - display: flex; - flex-direction: column; - gap: 5px; -} - -.sheet.npc .wound-item label { - font-size: 12px; - font-weight: bold; - text-align: center; -} - -.sheet.npc .wound-control { - display: flex; - align-items: center; - gap: 8px; -} - -.sheet.npc .wound-control input { - flex: 1; -} - -.sheet.npc .wound-control span { - font-size: 12px; - min-width: 30px; - text-align: center; -} - -/* Ability sections */ -.sheet.npc .ability-section { - margin-bottom: 15px; - padding: 10px; - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; -} - -.sheet.npc .ability-category-header { - margin-top: 0; - margin-bottom: 8px; - font-size: 14px; - font-weight: bold; - text-transform: uppercase; - border-bottom: 1px solid #444; - padding-bottom: 5px; -} - -.sheet.npc .ability-item { - display: flex; - align-items: center; - gap: 8px; - padding: 5px 0; -} - -.sheet.npc .ability-item label { - flex: 1; - font-size: 12px; - white-space: nowrap; -} - -.sheet.npc .ability-control { - display: flex; - align-items: center; - gap: 8px; -} - -/* Navigation */ -.sheet.npc .sheet-tabs { - font-weight: 500; - height: 30px; -} - -.sheet.npc .sheet-tabs > .item { - line-height: 24px; - padding-top: 3px; - font-size: 14px; - text-align: center; -} - -.sheet.npc .sheet-tabs > .item:last-of-type { - padding-right: 4px; -} - -.sheet.npc .sheet-tabs > .item.active { - color: #000; - font-weight: 700; -} - -.sheet.npc .sheet-tabs > .item:hover { - color: #000; - text-shadow: 0 5px 5px rgba(30, 82, 37, 0.604); -} - -/* Form groups */ -.sheet.npc .form-group { - margin-bottom: 10px; -} - -.sheet.npc .form-group label { - display: block; - margin-bottom: 3px; - font-weight: bold; - font-size: 12px; -} - -.sheet.npc .form-group input, -.sheet.npc .form-group select { - width: 100%; - font-size: 12px; -} - -/* Tab content */ -.sheet.npc .tab { - padding: 10px; - overflow-y: auto; -} - -/* Header fields */ -.sheet.npc .header-fields { - flex: 1; - display: flex; - flex-direction: column; - gap: 10px; -} - -.sheet.npc .resources { - display: grid; - gap: 8px; -} - -.sheet.npc .resource-label { - font-size: 12px; - font-weight: bold; - white-space: nowrap; -} - -.sheet.npc .resource-content { - display: flex; - align-items: center; -} - -.sheet.npc .resource-content select { - font-size: 12px; - padding: 3px 5px; - min-width: 100px; -} - -/* Editor styling */ -.sheet.npc .editor { - min-height: 100px; - margin-bottom: 10px; -} - -.sheet.npc .editor-content { - min-height: 80px; - font-size: 12px; -} - -/* ============================================ - GROUP SHEET SPECIFIC STYLES - ============================================ */ - -/* Group Sheet Container */ -.sheet.group { - min-width: 700px; -} - -/* Group Reserve and Morale Controls */ -.sheet.group .reserve-control, -.sheet.group .morale-control { - padding: 8px; - margin: 5px 0; - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; -} - -.sheet.group .reserve-control label, -.sheet.group .morale-control label { - font-weight: bold; - margin-right: 8px; - min-width: 80px; -} - -.sheet.group .reserve-control input, -.sheet.group .morale-control input, -.sheet.group .reserve-control select { - margin-right: 8px; - min-width: 50px; - text-align: center; -} - -/* Group Member and Encounter Lists */ -.sheet.group .actor-list { - list-style: none; - padding: 0; - margin: 0; -} - -.sheet.group .actor-list li.actor { - padding: 5px; - border-bottom: 1px solid #333; -} - -.sheet.group .actor-list li.actor:last-child { - border-bottom: none; -} - -.sheet.group .actor-list .actor-name, -.sheet.group .actor-list .item-name { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* Group Objectives */ -.sheet.group .objective-item { - padding: 5px; - border-bottom: 1px solid #444; -} - -.sheet.group .objective-item input { - flex: 1; - width: 100%; -} - -/* Group Totem Display */ -.sheet.group .totem-display { - padding: 8px; - background: rgba(0, 0, 0, 0.1); - border-radius: 4px; - margin: 5px 0; -} - -.sheet.group .totem-display span { - font-weight: bold; -} - -/* Group Info Section */ -.sheet.group .identity-field { - margin-bottom: 8px; -} - -.sheet.group .identity-field label { - font-weight: bold; - display: block; - margin-bottom: 3px; -} - -.sheet.group .identity-field input { - width: 100%; -} - -/* Group Level and Reputation */ -.sheet.group .level input, -.sheet.group .reputation input { - width: 60px; - text-align: center; -} - -/* Group Header */ -.sheet.group header.char-header { - background: url(/systems/vermine2047/assets/images/ui/barre_haut.webp) no-repeat right top; - background-size: 100% 100%; -} - -.sheet.group .char-details h1.char-name input, -.sheet.group .char-vermine2047 a.chooseTotem { - font-family: "DistressBlack", sans-serif; - text-transform: uppercase; -} - -/* Group Tab Icons */ -.sheet.group nav.sheet-navigation .item[data-tab="description"]::before { - content: "\f005"; /* fa-star*/ - font-family: "Font Awesome 6 Free"; -} - -.sheet.group nav.sheet-navigation .item[data-tab="gear"]::before { - content: "\f013"; /*fa-gear*/ - font-family: "Font Awesome 6 Free"; -} - -.sheet.group nav.sheet-navigation .item[data-tab="totem"]::before { - content: "\f079"; /*fa-route*/ - font-family: "Font Awesome 6 Free"; -} - -.sheet.group nav.sheet-navigation .item[data-tab="reserve"]::before { - content: "\f0c0"; /*fa-users*/ - font-family: "Font Awesome 6 Free"; -} - - -/* ============================================ - CREATURE SHEET SPECIFIC STYLES - ============================================ */ - -/* Creature Sheet Container */ -.sheet.creature { - min-width: 650px; -} - -/* Creature Header */ -.sheet.creature .sheet-header { - padding: 10px; - background: rgba(0, 0, 0, 0.1); -} - -.sheet.creature .header-fields { - flex: 1; -} - -.sheet.creature .resources { - margin-bottom: 10px; -} - -.sheet.creature .resource { - padding: 5px; - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; - margin: 0 5px; -} - -.sheet.creature .resource-label { - font-weight: bold; - margin-right: 8px; - min-width: 60px; - font-size: 12px; -} - -.sheet.creature .resource-content { - display: flex; - align-items: center; -} - -.sheet.creature .resource-content select { - margin-right: 8px; - min-width: 80px; -} - -.sheet.creature .charname { - margin: 0; -} - -.sheet.creature .charname input { - width: 100%; - font-size: 18px; - font-weight: bold; - text-align: center; -} - -/* Stats Grid */ -.sheet.creature .stats-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 8px; -} - -.sheet.creature .stat { - padding: 5px; - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; - display: flex; - justify-content: space-between; -} - -.sheet.creature .stat label { - font-weight: bold; -} - -.sheet.creature .stat span { - font-weight: bold; - color: #4e564c; -} - -/* Creature Stats Tab */ -.sheet.creature .mdb { - padding: 10px; - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; - margin-bottom: 10px; -} - -.sheet.creature .mdb h4 { - margin-top: 0; - border-bottom: 1px solid #444; - padding-bottom: 5px; - text-align: center; - font-size: 14px; -} - -.sheet.creature .mdb ul.unstyled { - margin: 0; - padding: 0; - list-style: none; -} - -.sheet.creature .mdb li { - padding: 3px 0; - border-bottom: 1px solid #333; -} - -.sheet.creature .mdb li:last-child { - border-bottom: none; -} - -/* Creature Identity Fields */ -.sheet.creature input[type="text"] { - width: 100%; - padding: 5px; - margin-bottom: 8px; -} - -/* Creature Modes */ -.sheet.creature .grid-3col { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 10px; -} - -.sheet.creature .grid-3col label { - display: flex; - align-items: center; - gap: 5px; - padding: 5px; -} - -/* Creature Wounds Display */ -.sheet.creature .row.mdb { - display: flex; - align-items: center; - gap: 8px; -} - -/* Creature Tab Navigation */ -.sheet.creature nav.sheet-tabs { - border-bottom: 1px solid #444; -} - -.sheet.creature nav.sheet-tabs .item { - padding: 8px 15px; - text-align: center; - font-size: 14px; -} diff --git a/css/vermine2047.css b/css/vermine2047.css new file mode 120000 index 0000000..d4d6dc1 --- /dev/null +++ b/css/vermine2047.css @@ -0,0 +1 @@ +vermine2047.min.css \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index aa6f51a..74a01c3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,40 +1,83 @@ -'use strict'; - -const gulp = require('gulp'); -const sass = require('gulp-sass')(require('sass')); -var browserSync = require('browser-sync').create(); - -function buildStyles() { - return gulp.src('./scss/vermine2047.scss') - .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError)) - .pipe(gulp.dest('./css')) - .pipe(browserSync.stream()); - - -}; -function reloadTemplatesHTML() { - - return browserSync.reload("templates/**/*.html") -} - -function reloadTemplatesHBS() { - - return browserSync.reload("templates/**/*.hbs") -} -exports.buildStyles = buildStyles; -exports.watch = function () { - browserSync.init( - { - server: false, - proxy: { - target: "https://localhost:30000/", - ws: true, - } - - } - ); - gulp.watch("./templates/**/*.html").on('change', reloadTemplatesHTML); - gulp.watch("./templates/**/*.hbs").on('change', reloadTemplatesHBS); - gulp.watch(['./scss/**/*.scss', './scss/*.scss'], buildStyles); - -}; +'use strict'; + +const gulp = require('gulp'); +const less = require('gulp-less'); +const autoprefixer = require('gulp-autoprefixer'); +const cleanCSS = require('gulp-clean-css'); +const rename = require('gulp-rename'); +var browserSync = require('browser-sync').create(); + +// ============================================ +// LESS Tasks +// ============================================ + +function buildLess() { + return gulp.src('./less/vermine2047.less') + .pipe(less().on('error', function(err) { + console.error('LESS compilation error:', err.message); + this.emit('end'); + })) + .pipe(autoprefixer({ + overrideBrowserslist: ['> 1%', 'last 2 versions', 'Firefox ESR'], + cascade: false + })) + .pipe(cleanCSS({ compatibility: 'ie11' })) + .pipe(rename({ suffix: '.min' })) + .pipe(gulp.dest('./css')) + .pipe(browserSync.stream()); +} + +// Build LESS without minification (for debugging) +function buildLessDev() { + return gulp.src('./less/vermine2047.less') + .pipe(less().on('error', function(err) { + console.error('LESS compilation error:', err.message); + this.emit('end'); + })) + .pipe(autoprefixer({ + overrideBrowserslist: ['> 1%', 'last 2 versions', 'Firefox ESR'], + cascade: false + })) + .pipe(rename({ suffix: '.dev' })) + .pipe(gulp.dest('./css')) + .pipe(browserSync.stream()); +} + +function watchLess() { + gulp.watch(['./less/**/*.less', './less/*.less'], buildLess); +} + +function reloadTemplatesHTML() { + return browserSync.reload("templates/**/*.html"); +} + +function reloadTemplatesHBS() { + return browserSync.reload("templates/**/*.hbs"); +} + +// ============================================ +// Exports +// ============================================ + +exports.buildLess = buildLess; +exports.buildLessDev = buildLessDev; +exports.buildCSS = gulp.series(buildLess); +exports.buildAllCSS = gulp.series(buildLess, buildLessDev); +exports.watch = function () { + browserSync.init({ + server: false, + proxy: { + target: "https://localhost:30000/", + ws: true, + } + }); + + // Watch templates + gulp.watch("./templates/**/*.html").on('change', reloadTemplatesHTML); + gulp.watch("./templates/**/*.hbs").on('change', reloadTemplatesHBS); + + // Watch LESS files + gulp.watch(['./less/**/*.less', './less/*.less'], buildLess); +}; + +exports.default = buildLess; diff --git a/inactive-tabs.png b/inactive-tabs.png new file mode 100644 index 0000000..b5cc2a8 Binary files /dev/null and b/inactive-tabs.png differ diff --git a/lang/en.json b/lang/en.json index a36710c..955638d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -13,6 +13,22 @@ }, "VERMINE.WorldSettings.GameMode.Name": "Game Mode Selection", "VERMINE.WorldSettings.GameMode.Hint": "Just like some video games offer different modes, Vermine 2047 lets players choose their Game Mode and set the degree of realism, supernatural and dangerosity of the universe themselves.", + "TYPES.Item.item": "Item", + "TYPES.Item.weapon": "Weapon", + "TYPES.Item.defense": "Defense", + "TYPES.Item.vehicle": "Vehicle", + "TYPES.Item.ability": "Ability", + "TYPES.Item.specialty": "Specialty", + "TYPES.Item.background": "Background", + "TYPES.Item.trauma": "Trauma", + "TYPES.Item.evolution": "Adaptation", + "TYPES.Item.rumor": "Rumor", + "TYPES.Item.target": "Target", + "TYPES.Item.rite": "Rite", + "TYPES.Actor.character": "Character", + "TYPES.Actor.npc": "NPC", + "TYPES.Actor.group": "Group", + "TYPES.Actor.creature": "Creature", "GAME_MODES": { "heroic": { "name": "Heroic" @@ -22,7 +38,10 @@ }, "legendary": { "name": "Legendary" - } + }, + "survival": "Survival", + "nightmare": "Nightmare", + "apocalypse": "Apocalypse" }, "ROLLS": { "tool": "Dice Roller", @@ -86,7 +105,8 @@ "relations": "Relations", "morale": "Morale", "morale_level": "Morale level", - "notes": "Notes" + "notes": "Notes", + "biography": "Biography" }, "ADVERSITY": { "threat": "Threat", @@ -108,7 +128,11 @@ "skills": "Skills", "pattern": "Pattern", "size": "Size", - "pack": "Group" + "pack": "Group", + "gear_hindrance": "Gear Hindrance", + "skills_placeholder": "Skills Placeholder", + "threat_details": "Threat Details", + "role_details": "Role Details" }, "VERMINE": { "name": "Vermine", @@ -180,7 +204,106 @@ "encounters": "Encounters", "road": "The Road", "totem_picker": "Totem picker", - "actor_picker": "Actor picker" + "actor_picker": "Actor picker", + "ability_category": { + "physical": "Physical", + "manual": "Manual", + "mental": "Mental", + "social": "Social" + }, + "skill_category": { + "man": "Man", + "animal": "Animal", + "tool": "Tool", + "weapon": "Weapon", + "survival": "Survival", + "world": "World" + }, + "CharacterNamePlaceholder": "Character Name", + "none": "None", + "bonuses": "Bonuses", + "choose_ability": "Choose Ability", + "choose_skill": "Choose Skill", + "grant_reroll": "Grant Reroll", + "handicap": "Handicap", + "keep_totem": "Keep Totem", + "morale_crisis": "Morale Crisis", + "morale_high": "High Morale", + "morale_low": "Low Morale", + "morale_normal": "Normal Morale", + "rerolls_possible": "Rerolls Possible", + "reserve": "Reserve", + "score": "Score", + "success_count": "Success Count", + "success_required": "Success Required", + "test_of": "Test of", + "total": "Total", + "totem_dice": "Totem Dice", + "totem_hint": "Totem Hint", + "FightTool": "Fight Tool", + "RollTool": "Roll Tool", + "Roll4Fight": "Roll for Fight", + "Selected": "Selected", + "PurposeTrait": "Purpose Trait", + "SpleenTrait": "Spleen Trait", + "ConfrontationHint": "Confrontation Hint", + "Achievement": "Achievement", + "Conservation": "Conservation", + "error_cannot_reroll": "Cannot reroll", + "error_no_rerolls_left": "No rerolls left", + "error_no_actor_selected": "No actor selected", + "error_not_enough_self_control": "Not enough self control", + "error_select_ability": "Select an ability", + "tabs": { + "abilities": "Abilities", + "combat": "Combat", + "equipment": "Equipment", + "stories": "Stories", + "totem": "Totem" + }, + "base_values": "Base Values", + "computed_values": "Computed Values", + "cost": "Cost", + "creature": "Creature", + "effect": "Effect", + "error_select_skill": "Select a skill", + "error_unknown_actor": "Unknown actor", + "instincts": "Instincts", + "instincts_placeholder": "eg: Aggressive, Defensive", + "learn": "Learn", + "major_objectives": "Major Objectives", + "minor_objectives": "Minor Objectives", + "morale": "Morale", + "needed": "Needed", + "objective_placeholder": "eg: Survive, Explore", + "objectives": "Objectives", + "pack": "Pack", + "pattern": "Pattern", + "preferred_category": "Preferred Category", + "prohibits": "Prohibits", + "prohibits_placeholder": "eg: Fire, Water", + "rarity_0": "Common", + "rarity_1": "Rare", + "rarity_2": "Very Rare", + "reserves": "Reserves", + "ritual": "Ritual", + "sexes": { + "female": "Female", + "male": "Male" + }, + "size": "Size", + "total_attack": "Total Attack", + "total_damage": "Total Damage", + "trance": "Trance", + "vote_reserve": "Vote Reserve", + "wound_thresholds": "Wound Thresholds", + "types": { + "shield": "Shield" + }, + "roll": "Roll", + "cancel": "Cancel", + "playMode": "Play Mode", + "editMode": "Edit Mode" }, "UI": { "add": "Add", @@ -195,9 +318,15 @@ "temporary": "Temporary effects", "passive": "Passive effects", "inactive": "Inactive effects" - } + }, + "effects.inactive": "Inactive", + "effects.passive": "Passive", + "effects.temporary": "Temporary" }, "ITEMS": { + "item": "Item", + "items": "Items", + "new_item": "New item", "defense": "Protection", "defenses": "Protections", "new_defense": "New protection", @@ -216,6 +345,12 @@ "abilities": "Abilities", "specialties": "Specialties", "new_specialty": "New specialty", + "target": "Target", + "targets": "Targets", + "new_target": "New target", + "rite": "Rite", + "rites": "Rites", + "new_rite": "New rite", "evolution": "Adaptation", "new_evolution": "New adaptation", "evolutions": "Adaptations", @@ -227,7 +362,8 @@ "rituel": "Ritual", "transe": "Trance", "effects": "Effects", - "details": "Details" + "details": "Details", + "shield": "Shield" }, "ABILITIES": { "vigor": { @@ -498,10 +634,26 @@ }, "ruins": { "name": "Ruins" + }, + "athletics": { + "name": "Athletics" + }, + "fauna": { + "name": "Fauna" } }, "SEXES": { "male": "Masculine", "female": "Feminine" + }, + "COMBAT": { + "Encounter": "Encounter", + "None": "None", + "Round": "Round" + }, + "SIZE_LEVELS": { + "small": "Small", + "medium": "Medium", + "large": "Large" } } \ No newline at end of file diff --git a/lang/fr.json b/lang/fr.json index 99fa255..a3f403f 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -1,4 +1,26 @@ { + "NAME": "Nom", + "NONE": "Aucun", + "ORIGIN": "Origine", + "PROFILE": "Profil", + "THEME": "Concept", + "TOTEM": "Totem", + "TYPES.Item.item": "Objet", + "TYPES.Item.weapon": "Arme", + "TYPES.Item.defense": "Protection", + "TYPES.Item.vehicle": "Véhicule", + "TYPES.Item.ability": "Capacité", + "TYPES.Item.specialty": "Spécialité", + "TYPES.Item.background": "Historique", + "TYPES.Item.trauma": "Traumatisme", + "TYPES.Item.evolution": "Adaptation", + "TYPES.Item.rumor": "Rumeur", + "TYPES.Item.target": "Cible", + "TYPES.Item.rite": "Rite", + "TYPES.Actor.character": "Personnage", + "TYPES.Actor.npc": "PNJ", + "TYPES.Actor.group": "Groupe", + "TYPES.Actor.creature": "Créature", "SETTINGS": { "world": { "game_mode": { @@ -132,9 +154,13 @@ "preferred_category": "Catégorie préférée", "wounds": { "name": "Blessures", - "light": "Légères", - "heavy": "Graves", - "deadly": "Mortelles" + "threshold": "Seuil", + "light": "Légère", + "heavy": "Grave", + "deadly": "Mortelle", + "light_wounds": "Blessure légère", + "heavy_wounds": "Blessure grave", + "deadly_wounds": "Blessure mortelle" }, "ability_category": { "physical": "Physiques", @@ -153,6 +179,7 @@ "rarity_0": "Commun", "rarity_1": "Peu commun", "rarity_2": "Rare", + "rarity_3": "Très rare", "test_of": "test de", "rerolls_possible": "relances possibles", "grant_reroll": "accorder des relances", @@ -162,7 +189,7 @@ "error_cannot_reroll": "Vous ne pouvez pas relancer un dés sur ce jet", "error_no_rerolls_left": "Plus de relances possibles", "error_select_ability": "Veuillez sélectionner une caractéristique", - "error_not_enough_self_control": "Pas assez de points de sang-froid", + "error_not_enough_self_control": "Vous n'avez pas assez de Sang-Froid", "FightTool": "Outil de combat", "Roll4Fight": "Lancer pour le combat", "Selected": "Sélectionné", @@ -174,7 +201,9 @@ "group": "Groupe", "abilities": "Caractéristiques", "ability": "Caractéristique", + "skills": "Compétences", "skills_title": "Compétences", + "skill": "Compétence", "skill_title": "Compétence", "skill_mastery": "Maîtrise", "bonus": "Bonus", @@ -182,6 +211,8 @@ "reroll": "Relance", "equipment": "Equipement", "specialty": "Spécialité", + "specificLevel": "Niveau spécifique", + "competence": "Compétence", "technique": "Technique", "techniques": "Techniques", "difficulty": "Difficulté", @@ -200,16 +231,6 @@ "rarity_sm": "Rar.", "reliability": "Fiabilité", "reliability_sm": "Fiab.", - "wounds": { - "name": "Blessures", - "threshold": "Seuil", - "light": "Légère", - "heavy": "Grave", - "deadly": "Mortelle", - "light_wounds": "Blessure légère", - "heavy_wounds": "Blessure grave", - "deadly_wounds": "Blessure mortelle" - }, "choose_ability": "Choisissez une caractéristique", "choose_skill": "Choisissez une compétence", "none": "Aucun", @@ -220,8 +241,6 @@ "totem_dice": "Dés de totem", "keep_totem": "Garder le totem", "totem_hint": "Cochez pour utiliser les dés de totem (double réussite possible)", - "error_not_enough_self_control": "Vous n'avez pas assez de Sang-Froid", - "error_select_ability": "Veuillez sélectionner une caractéristique", "error_select_skill": "Veuillez sélectionner une compétence", "needed": "nécessaire", "cost": "Coût", @@ -243,11 +262,15 @@ "ammo_sm": "Mun", "trait": "Trait", "traits": "Traits", + "traits_selector": "Sélecteur de traits", "clew": "Indice", "combat": "Combat", "stories": "Histoires", "gear": "Matériel", "information": "Informations", + "editMode": "Mode édition", + "playMode": "Mode jeu", + "modes": "Modes de jeu", "boost": "boost", "group_abilities": "Capacités de groupe", "totem_abilities": "Capacités de totem", @@ -268,7 +291,6 @@ "minor_objectives": "Objectifs mineurs", "instincts": "Instincts", "prohibits": "Interdictions", - "group_abilities": "Capacités de groupe", "vote_reserve": "Vote pour utiliser la réserve", "morale_high": "Haut", "morale_normal": "Normal", @@ -286,7 +308,15 @@ "base_values": "Valeurs de base", "total_attack": "Attaque totale", "total_damage": "Dégâts totaux", - "wound_thresholds": "Seuils de blessures" + "wound_thresholds": "Seuils de blessures", + "CharacterNamePlaceholder": "Nom du personnage", + "sexes": { + "female": "Féminin", + "male": "Masculin" + }, + "RollTool": "Outil de lancer de dés", + "roll": "Lancer", + "cancel": "Annuler" }, "UI": { "add": "Ajouter", @@ -304,6 +334,9 @@ } }, "ITEMS": { + "item": "Objet", + "items": "Objets", + "new_item": "Nouvel objet", "defense": "Protection", "defenses": "Protections", "new_defense": "Nouvelle protection", @@ -322,6 +355,12 @@ "abilities": "Capacités", "specialties": "Spécialités", "new_specialty": "Nouvelle spécialité", + "target": "Cible", + "targets": "Cibles", + "new_target": "Nouvelle cible", + "rite": "Rite", + "rites": "Rites", + "new_rite": "Nouveau rite", "shield": "Bouclier", "evolution": "Adaptation", "new_evolution": "Nouvelle adaptation", @@ -522,7 +561,7 @@ "name": "Arts" }, "civilization": { - "name": "Civilisations" + "name": "Civilisation" }, "psychology": { "name": "Psychologie" @@ -539,14 +578,14 @@ "dissection": { "name": "Dissection" }, - "wildlife": { + "fauna": { "name": "Faune" }, "repulsion": { "name": "Répulsion" }, "tracks": { - "name": "Traces" + "name": "Pistage" }, "crafting": { "name": "Artisanat" @@ -566,42 +605,39 @@ "firearms": { "name": "Armes à feu" }, + "archery": { + "name": "Tir à l'arc" + }, "armory": { "name": "Armurerie" }, - "shield": { - "name": "Bouclier" - }, - "close": { - "name": "Corps-à-corps" - }, - "archery": { - "name": "Armes de tir" - }, "throwing": { "name": "Lancer" }, "melee": { "name": "Mêlée" }, - "atletics": { - "name": "Atlétisme" - }, - "stealth": { - "name": "Discrétion" - }, "alertness": { "name": "Vigilance" }, - "flora": { - "name": "Flore" + "athletics": { + "name": "Athlétisme" }, "food": { "name": "Alimentation" }, + "stealth": { + "name": "Discrétion" + }, + "close": { + "name": "Corps-à-corps" + }, "environment": { "name": "Environnement" }, + "flora": { + "name": "Flore" + }, "road": { "name": "Route" }, @@ -610,42 +646,28 @@ }, "ruins": { "name": "Vestiges" + }, + "shield": { + "name": "Bouclier" + }, + "wildlife": { + "name": "Faune" + }, + "atletics": { + "name": "Athlétisme" } }, "SEXES": { "male": "Masculin", "female": "Féminin" }, - "SKILLS": { - "arts": "Arts", - "civilization": "Civilisation", - "psychology": "Psychologie", - "rumors": "Rumeurs", - "healing": "Soins", - "animalism": "Animalisme", - "dissection": "Dissection", - "fauna": "Faune", - "repulsion": "Répulsion", - "tracks": "Pistage", - "crafting": "Artisanat", - "diy": "Bricolage", - "mecanical": "Mécanique", - "piloting": "Pilotage", - "technology": "Technologie", - "firearms": "Armes à feu", - "archery": "Tir à l'arc", - "armory": "Armurerie", - "throwing": "Lancer", - "melee": "Mêlée", - "alertness": "Vigilance", - "athletics": "Athlétisme", - "food": "Alimentation", - "stealth": "Discrétion", - "close": "Corps-à-corps", - "environment": "Environnement", - "flora": "Flore", - "road": "Route", - "toxics": "Toxiques", - "ruins": "Vestiges" + "COMBAT": { + "Begin": "Commencer", + "Encounter": "Rencontre", + "End": "Terminer", + "None": "Aucun", + "NotStarted": "Non commencé", + "Round": "Tour", + "TurnEnd": "Fin de tour" } } \ No newline at end of file diff --git a/less/actor/actor.less b/less/actor/actor.less new file mode 100644 index 0000000..e31f8aa --- /dev/null +++ b/less/actor/actor.less @@ -0,0 +1,641 @@ +@import "totem"; + +.system-vermine2047 .vermine2047.actor { + + // ── Fix contrast: dark text on light parchment bg ─────────────────── + .window-content, + & { + color: @theme-color-dark; + + label, span, p, li, a:not(.active) { + color: @theme-color-dark; + } + + h4, h5 { + color: @color-text-dark-header; + } + + h3 { + color: @theme-color-light; + } + + input:not([type="radio"]):not([type="checkbox"]), + select, textarea { + color: @color-text-light-0; + line-height: 1.2; + padding: 1px 4px; + } + + .char-header { + h1 label { + font-size: 0.9rem; + } + .char-name-value { + font-size: 1.5rem; + } + input:not([type="radio"]):not([type="checkbox"]) { + font-size: 1rem; + } + } + + .ability input.hexa { + font-size: 0.8rem; + } + + .resource-label { + color: @color-text-dark-secondary; + } + + .ability label { + color: @color-text-dark-header; + } + + .char-header { + color: @theme-color-dark; + + label { + color: @color-text-dark-header; + } + } + + .hexa { + color: @color-text-dark-primary; + } + + .sheet-tabs a { + color: @color-text-dark-inactive; + &.active { + color: @theme-color-dark; + } + } + } + + // ── Two-column layout for character sheets ────────────────────────── + &.character { + display: grid !important; + grid-template-columns: 1fr !important; + grid-template-rows: auto 1fr auto !important; + + > header.window-header { + grid-column: 1; + grid-row: 1; + } + + > section.window-content { + grid-column: 1; + grid-row: 2; + } + + > div.window-resize-handle { + grid-column: 1; + grid-row: 3; + } + } + + &.character .window-content { + display: grid; + grid-template-columns: 240px 1fr; + grid-template-rows: auto 1fr; + overflow: hidden; + + > .tab.main { + grid-column: 1; + grid-row: 1 / -1; + display: flex !important; + flex-direction: column; + overflow-y: auto; + padding: 8px; + border-right: 1px solid rgba(255, 255, 255, 0.1); + } + + > nav.tabs { + grid-column: 2; + grid-row: 1; + } + + > .tab:not(.main) { + grid-column: 2; + grid-row: 2; + display: none; + overflow-y: auto; + padding: 8px; + + &.active { + display: flex; + flex-direction: column; + } + } + } + + // ── Stacked layout for NPC sheets (header + tabs + content) ──────── + &.npc { + display: grid !important; + grid-template-columns: 1fr !important; + grid-template-rows: auto 1fr auto !important; + + > header.window-header { + grid-column: 1; + grid-row: 1; + } + + > section.window-content { + grid-column: 1; + grid-row: 2; + } + + > div.window-resize-handle { + grid-column: 1; + grid-row: 3; + } + } + + &.npc .window-content { + display: grid; + grid-template-rows: auto auto 1fr; + grid-template-columns: 1fr; + overflow: hidden; + + > .tab.main { + grid-column: 1; + grid-row: 1; + display: flex !important; + flex-direction: column; + overflow-y: auto; + padding: 8px; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } + + > nav.tabs { + grid-column: 1; + grid-row: 2; + } + + > .tab:not(.main) { + grid-column: 1; + grid-row: 3; + display: none; + overflow-y: auto; + padding: 8px; + + &.active { + display: flex; + flex-direction: column; + } + } + } + + form { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + + > .tab { + flex: 1; + overflow-y: auto; + padding: 4px; + } + + .charname input { + color: @theme-color-dark; + font-family: "DistressBlack", sans-serif; + font-size: 30px; + font-style: normal; + } + + div.hexa { + .hexa-style(); + .transition(); + margin: 0.2rem; + + &:hover { + .hexa-style(rgba(255, 255, 255, 0.425), rgba(0, 0, 0, 0.288)); + } + + input { + opacity: 1; + min-width: 100%; + min-height: 100%; + opacity: 0; + } + + &.checked { + .hexa-style(rgb(0, 0, 0), rgba(0, 0, 0, 0.288)); + &:hover { .hexa-style(rgb(43, 43, 43), rgba(0, 0, 0, 0.288)); } + } + + &.unavailable { + background: radial-gradient(circle, rgba(66, 15, 15, 0.664) 0%, rgba(131, 70, 70, 0.432) 100%); + pointer-events: none; + } + } + + input[type="text"], + input[type="number"] { + width: calc(100% - 2px); + height: calc(100% - 2px); + background: none; + padding: 0; + margin: 1px 0; + color: #333; + border: 1px solid rgba(0, 0, 0, 0); + + &.hexa { .hexa-style(); } + } + + input[disabled], + select[disabled] { + cursor: not-allowed; + } + + input[type="text"]:hover:not(:disabled), + input[type="text"]:focus, + select:hover:not(:disabled), + select:focus, + input[type="number"]:hover:not(:disabled), + input[type="number"]:focus, + textarea:hover:not(:disabled), + textarea:focus { + box-shadow: 0 0 10px @theme-color-highlight inset; + } + + select { + font-size: 0.6rem; + border: none; + appearance: none; + min-width: fit-content; + max-width: fit-content; + padding: 0 0.2rem; + margin: 0 0.2rem; + cursor: help; + } + + label { + display: block; + } + + .mce-panel span { + display: inherit; + } + + .rollable:hover, + a:hover { + color: #000; + text-shadow: 0 5px 5px @theme-color-accent; + cursor: pointer; + } + + .chooseTotem { + font-family: "DistressBlack", sans-serif; + font-size: 0.9rem; + cursor: pointer; + + &:hover { + text-shadow: 0 0 10px @theme-color-highlight; + } + } + } + + .sheet-header-toggle { + text-align: right; + margin-bottom: 0.5rem; + + button { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + padding: 0.2rem 1rem; + cursor: pointer; + .background-image(url("@{ui-path}/scotch.webp"), no-repeat, cover, 50% 0%); + border: none; + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.979) inset; + color: @theme-color-dark; + + &:hover { + box-shadow: 0 0 10px @theme-color-highlight inset; + } + } + } + + .image-wrapper { + text-align: center; + margin-bottom: 1rem; + + img { + width: 80%; + height: auto; + } + } + + .padding-with-frieze { + margin-left: 18% !important; + margin-right: 10% !important; + + li { + max-width: 100%; + } + + .major-totem { + position: relative; + + h4 { + position: absolute; + transform: rotate(-8deg); + opacity: 1; + transition: 0.2s; + } + } + + .paper { + margin-top: 1rem; + height: 350px; + } + + .second-paper { + margin-top: 4rem; + height: 150px; + } + } + + .char-header { + font-family: "DistressBlack", sans-serif; + + section { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + } + + h1.char-name, + .char-vermine2047 { + border-bottom: none; + line-height: 2rem; + } + + .char-vermine2047 { + font-size: 1.5rem; + } + } + + h3 { + font-family: "DistressBlack", sans-serif; + text-align: center; + text-transform: uppercase; + color: @theme-color-light; + font-size: 1.7rem; + border-bottom: none; + margin: 0; + } + + h4 { + font-family: "DistressBlack", sans-serif; + font-size: 1.4em; + text-transform: uppercase; + margin: 0 0 0.2rem; + + &.characteristics { + font-size: 1.25rem; + margin-top: 0.1rem; + } + + .tab.totem &, + .tab.equipment &, + .tab.stories & { + margin-top: 0.875rem; + } + } + + nav.tabs[data-group="sheet"] { + display: inline-flex; + justify-content: space-around; + align-items: center; + height: 54px; + .background-image(url("@{ui-path}/barre_haut.webp"), no-repeat, auto, right top); + background-size: 100% 100%; + width: 100%; + position: relative; + padding-right: 4rem; + font-size: 1.4rem; + + .item { + height: 2.4rem; + display: inline-flex; + align-items: center; + gap: 0.3rem; + z-index: 1; + transition: all 0.1s ease-out; + color: #606060; + box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.404); + + &:hover, + &.active { + color: #000; + text-shadow: 0 5px 5px @theme-color-accent; + cursor: pointer; + } + + &:hover { + text-shadow: 0 5px 5px rgba(30, 82, 37, 0.6039215686); + } + } + } + + .ability { + padding-right: 0.6rem; + font-size: 0.8rem; + border-bottom: 1px solid rgba(170, 170, 152, 0.664); + box-shadow: 0px 0px 15px rgba(128, 128, 128, 0) inset; + transition: 0.2s; + position: relative; + flex-wrap: nowrap; + min-width: min-content; + container-type: inline-size; + container-name: ability-row; + + &:hover { + box-shadow: 0px 0px 15px gray inset; + } + + label { + min-width: 40%; + flex: 1.3; + } + + span { + max-width: fit-content; + margin: 0 1rem; + flex: 0.5; + } + + div.specialties { + position: absolute; + bottom: -0.2rem; + font-size: 0.7rem; + } + + .skill-dots { + height: 100%; + align-self: center; + flex: 1.5; + min-width: fit-content; + + > div { + max-width: 0.7rem; + height: 0.7rem; + aspect-ratio: 1/1; + border-radius: 50%; + font-weight: 700; + text-align: center; + padding-bottom: 0.2rem; + font-style: oblique; + align-self: flex-start; + + &.dice-pool-dot { + .background-image(radial-gradient(circle, @dice-pool-color 25%, rgb(0, 0, 0) 100%), @dice-pool-color); + max-width: 0.7rem; + aspect-ratio: 1/1; + border-radius: 50%; + } + + &.dice-reroll-dot { + background: radial-gradient(circle, @dice-reroll-color 25%, rgb(0, 0, 0) 100%); + } + } + } + } + + .skill-category { + padding: 0.3rem; + + &.preferred { + box-shadow: 0px 0px 30px rgba(0, 128, 0, 0.306) inset; + + h4, label { + text-shadow: 0px 0px 5px rgba(0, 128, 0, 0.411); + } + } + } + + #edit { + background-color: #000; + color: #fff; + width: 100%; + } + + .reserve-grid { + line-height: 0.5rem; + transform-origin: 0% 50%; + max-width: fit-content; + align-items: center; + display: flex; + flex-direction: column; + + div.flexrow, + input, + .hexa { + margin: 0; + padding: 0; + min-width: 1rem; + min-height: 1rem; + } + + > .flexrow { + position: relative; + max-width: fit-content; + justify-content: center; + } + } + + .items-list { + .item-name .item-image { + flex: 0 0 30px; + height: 30px; + background-size: 30px; + border: none; + margin-right: 5px; + } + } + + .items-list .item.flexrow > div:not(.item-name):not(.item-controls) { + text-align: center; + } + + .list-item { + list-style: none; + margin: 0; + padding: 0; + + .item { + align-items: center; + padding: 2px; + border-bottom: 1px solid rgba(170, 170, 152, 0.664); + + &:last-child { border-bottom: none; } + } + } + + // ── V2 tab navigation — chitin-plate style ───────────────────────── + nav.sheet-tabs[data-application-part="tabs"] { + display: flex; + align-items: stretch; + gap: 0; + margin: 0; + padding: 0; + background: @color-chitin-dark; + border-bottom: 2px solid @color-amber; + min-height: 36px; + + > a { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-11; + letter-spacing: 0.5px; + display: flex; + align-items: center; + gap: 6px; + padding: 6px 16px; + color: @color-text-light-0; + opacity: 1; + transition: background 0.15s; + position: relative; + border-right: 1px solid fade(@color-membrane, 12%); + + &:hover { + background: fade(@color-honeycomb, 10%); + text-shadow: none; + } + + &.active { + background: linear-gradient(180deg, @color-amber 0%, @color-honeycomb 100%); + color: @color-chitin-dark; + + &::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + right: 0; + height: 2px; + background: @color-acid-green; + } + } + + > i { + font-size: @font-size-14; + width: 16px; + text-align: center; + } + + > span, > i { + color: inherit; + } + } + } +} + +@container ability-row (max-width: 240px) { + .skill-dots, + span.hexa { + display: none; + } +} diff --git a/less/actor/creature.less b/less/actor/creature.less new file mode 100644 index 0000000..7916ae4 --- /dev/null +++ b/less/actor/creature.less @@ -0,0 +1,119 @@ +@import "../variables"; +@import "../utilities"; + +.system-vermine2047 .vermine2047.actor.creature { + .sheet-header { + .background-image(url("@{ui-path}/barre_haut.webp"), no-repeat, 100% 100%, right top); + padding: 10px; + max-height: 110px; + } + + .header-fields { flex: 1; } + .resources { margin-bottom: 10px; } + + .resource { + .card-style(); + } + + .resource-label { + font-weight: bold; + margin-right: 8px; + min-width: 60px; + font-size: @font-size-12; + } + + .resource-content { + display: flex; + align-items: center; + + select { + margin-right: 8px; + min-width: 80px; + } + } + + .charname { + margin: 0; + + input { + width: 100%; + font-size: @font-size-18; + font-weight: bold; + text-align: center; + } + } + + .stats-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; + } + + .stat { + .card-style(); + display: flex; + justify-content: space-between; + + label { font-weight: bold; } + span { + font-weight: bold; + color: @theme-color-light; + } + } + + .mdb { + &:not(.row) { + .card-style(); + } + + h4:first-child { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-14; + margin-top: 0; + border-bottom: 1px solid @color-border-dark-3; + padding-bottom: 5px; + text-align: center; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + } + + ul.unstyled { + margin: 0; + padding: 0; + list-style: none; + } + + li { + padding: 3px 0; + border-bottom: 1px solid @color-border-dark-3; + + &:last-child { border-bottom: none; } + } + } + + input[type="text"] { + width: 100%; + padding: 5px; + margin-bottom: 8px; + } + + .grid-3col { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + + label { + display: flex; + align-items: center; + gap: 5px; + padding: 5px; + } + } + + .row.mdb { + display: flex; + align-items: center; + gap: 8px; + } + +} diff --git a/less/actor/group.less b/less/actor/group.less new file mode 100644 index 0000000..8fed8a5 --- /dev/null +++ b/less/actor/group.less @@ -0,0 +1,99 @@ +@import "../variables"; +@import "../utilities"; + +.system-vermine2047 .vermine2047.actor.group { + .char-header { + .background-image(url("@{ui-path}/barre_haut.webp"), no-repeat, 100% 100%, right top); + max-height: 110px; + } + + .char-details { + h1.char-name input, + .char-vermine2047 a.chooseTotem { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + } + } + + h4 { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-14; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + } + + .reserve-control, + .morale-control { + .card-style(); + + label { + font-weight: bold; + margin-right: 8px; + min-width: 80px; + } + + input, select { + margin-right: 8px; + min-width: 50px; + text-align: center; + } + } + + .actor-list { + list-style: none; + padding: 0; + margin: 0; + + li.actor { + padding: 5px; + border-bottom: 1px solid @color-border-dark-3; + + &:last-child { border-bottom: none; } + + &:hover { + box-shadow: 0 0 10px @theme-color-highlight inset; + } + } + + .actor-name, .item-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .objective-item { + padding: 5px; + border-bottom: 1px solid @color-border-dark-4; + + input { + flex: 1; + width: 100%; + } + } + + .totem-display { + .card-style(); + + span { font-weight: bold; } + } + + .identity-field { + margin-bottom: 8px; + + label { + font-weight: bold; + display: block; + margin-bottom: 3px; + } + + input { width: 100%; } + } + + .level input, + .reputation input { + width: 60px; + text-align: center; + } +} diff --git a/less/actor/npc.less b/less/actor/npc.less new file mode 100644 index 0000000..29d9d87 --- /dev/null +++ b/less/actor/npc.less @@ -0,0 +1,231 @@ +@import "../variables"; +@import "../utilities"; + +.system-vermine2047 .vermine2047.actor.npc { + .sheet-header { + .background-image(url("@{ui-path}/barre_haut.webp"), no-repeat, 100% 100%, right top); + padding: 0.5rem; + max-height: 110px; + } + + .ability-value, + .resource-value { + min-width: 30px; + text-align: center; + font-weight: bold; + margin-left: 8px; + } + + .card, .npc-card { + .card-style(); + + h4 { + .card-title(); + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-14; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + + i { margin-right: 5px; } + } + } + + .skills-container { + display: flex; + flex-direction: column; + gap: 15px; + } + + .skill-category { + .card-style(); + padding: 10px; + + h4 { + font-family: "DistressBlack", sans-serif; + font-size: @font-size-14; + text-transform: uppercase; + margin-top: 0; + margin-bottom: 8px; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + } + } + + .skill-item { + display: flex; + align-items: center; + gap: 8px; + padding: 5px; + border-bottom: 1px solid @color-border-dark-3; + + &:last-child { border-bottom: none; } + + label { + flex: 1; + font-size: @font-size-12; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &:hover { + box-shadow: 0 0 10px @theme-color-highlight inset; + } + } + + .skill-control { + display: flex; + align-items: center; + gap: 5px; + } + + .skill-value { + min-width: 25px; + text-align: center; + font-weight: bold; + } + + .rarity-badge { + display: inline-block; + padding: 1px 4px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; + margin-left: 5px; + + &.rarity-0 { .rarity-badge(@rarity-0-bg, @rarity-0-text); } + &.rarity-1 { .rarity-badge(@rarity-1-bg, @rarity-1-text); } + &.rarity-2 { .rarity-badge(@rarity-2-bg, @rarity-2-text); } + &.rarity-3 { .rarity-badge(@rarity-3-bg, @rarity-3-text); } + } + + .skill-category-selector { + margin-bottom: 15px; + .card-style(); + } + + .wounds-card { + margin-top: 15px; + .card-style(); + } + + .wound-item { + display: flex; + flex-direction: column; + gap: 5px; + + label { + font-size: @font-size-12; + font-weight: bold; + text-align: center; + } + } + + .wound-control { + display: flex; + align-items: center; + gap: 8px; + + input { flex: 1; } + span { + font-size: @font-size-12; + min-width: 30px; + text-align: center; + } + } + + .ability-section { + .card-style(); + } + + .ability-category-header { + font-family: "DistressBlack", sans-serif; + font-size: @font-size-14; + text-transform: uppercase; + margin-top: 0; + margin-bottom: 8px; + border-bottom: 1px solid @color-border-dark-3; + padding-bottom: 5px; + } + + .ability-item { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 0; + + label { + flex: 1; + font-size: @font-size-12; + white-space: nowrap; + } + } + + .ability-control { + display: flex; + align-items: center; + gap: 8px; + } + + .tab { + padding: 10px; + overflow-y: auto; + } + + .form-group { + .form-group-style(); + } + + .header-fields { + flex: 1; + display: flex; + flex-direction: column; + gap: 10px; + } + + .resources { + display: grid; + gap: 8px; + } + + .resource { + .card-style(); + + label { font-weight: bold; } + } + + .resource-label { + font-size: @font-size-12; + font-weight: bold; + white-space: nowrap; + } + + .resource-content { + display: flex; + align-items: center; + + select { + font-size: @font-size-12; + padding: 3px 5px; + min-width: 100px; + } + } + + .editor { + min-height: 100px; + margin-bottom: 10px; + + .editor-content { + min-height: 80px; + font-size: @font-size-12; + } + } + + .hexa { + .hexa-style(); + .transition(); + + &:hover { + .hexa-style(rgba(255, 255, 255, 0.425), rgba(0, 0, 0, 0.288)); + } + } +} diff --git a/less/actor/totem.less b/less/actor/totem.less new file mode 100644 index 0000000..0594a5f --- /dev/null +++ b/less/actor/totem.less @@ -0,0 +1,93 @@ +@import "../variables"; +@import "../utilities"; + +.system-vermine2047 .vermine2047.actor { + .totem-details { + position: relative; + + img.img-totem { + transform-origin: 50% 50%; + filter: grayscale(1); + opacity: 0.15; + position: absolute; + width: 30%; + height: auto; + pointer-events: none; + aspect-ratio: 1/1; + left: 35%; + } + } + + div.minor-totems { + position: relative; + background-color: rgba(146, 156, 111, 0.5215686275); + + h5 { + position: absolute; + top: 0; + + img { + max-width: 2rem; + position: absolute; + bottom: -2rem; + } + + &.human, &.adapted { + .transition(0.3s); + } + + &.human img.img-totem, + &.adapted img.img-totem { + filter: drop-shadow(0px 0px 20px rgb(0, 0, 0)); + } + + &.human.major, + &.adapted.major { + transform: scale(1.1); + } + + &.human.major img, + &.adapted.major img { + filter: drop-shadow(0px 0px 10px red); + } + } + + .totem-dice { + .human-dice, + .adapted-dice { + display: flex; + flex-direction: row; + margin-left: 2rem; + } + + .human-dice i, + .adapted-dice i { + padding-top: 0.5rem; + color: @totem-human-color; + } + + .adapted-dice { + justify-content: flex-end; + margin-left: 0; + margin-right: 2rem; + transform: rotate(180deg); + } + + .adapted-dice i { + transform: rotate(180deg); + padding-top: 0.5rem; + color: @totem-adapted-color; + } + } + + .human { + left: 0; + img { left: 0; } + } + + .adapted { + right: 0; + img { right: 0; } + } + } +} diff --git a/less/base.less b/less/base.less new file mode 100644 index 0000000..12e1f66 --- /dev/null +++ b/less/base.less @@ -0,0 +1,650 @@ +@font-face { + font-family: "DistressBlack"; + src: url("@{fonts-path}/dcc_sharp_distress_black_by_dccanim.otf"); +} + +* { + box-sizing: border-box; +} + +body.system-vermine2047 { + font-family: "Roboto", sans-serif; + color: @theme-color-secondary; +} + +img { + border: none; +} + +ul.unstyled { + list-style-type: none; + padding: 0; + margin: 0; + + li { + padding: 0; + margin: 0; + } +} + +.w-full { + width: 100%; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.window-app { + font-family: "Roboto", sans-serif; + .shadow(0px, 0px, 30px, rgba(106, 176, 76, 0.25)); +} + +.system-vermine2047 { + .window-content, + form.application.sheet.vermine2047 { + .background-image(url("@{ui-path}/box_background.webp"), repeat); + padding: 10px; + overflow-y: hidden; + } + + .dialog .window-content { + .background-image(url("@{ui-path}/fond_chat_box.webp"), repeat); + padding: 0.5rem; + overflow-y: hidden; + } +} + +.window-content .row { + &.smb { margin-bottom: 0.25rem; } + &.mdb { margin-bottom: 0.5rem; } + &.lgb { margin-bottom: 1rem; } +} + +.rollable:hover, +.rollable:focus { + color: #000; + text-shadow: 0 0 10px @color-acid-green; + cursor: pointer; + .transition(); +} + +.rollable { + .transition(); +} + +img.profile-img { + filter: drop-shadow(0px 0px 20px rgb(110, 133, 27)); + height: auto; + width: 100%; + max-width: 10rem; +} + +body.system-vermine2047 img#logo { + content: url("@{assets-path}/images/ui/logo_vermine_foundry.webp"); + height: auto; +} + +#chat-log, +.chat-log { + .message { + .background-image(url("@{ui-path}/box_background.webp"), repeat); + } +} + +.grid { + display: grid; + grid-column: span 2 / span 2; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 5px 0; + padding: 0; +} + +.grid-2col { .grid(); } +.grid-3col { grid-column: span 3; grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-4col { grid-column: span 4; grid-template-columns: repeat(4, minmax(0, 1fr)); } +.grid-5col { grid-column: span 5; grid-template-columns: repeat(5, minmax(0, 1fr)); } +.grid-6col { grid-column: span 6; grid-template-columns: repeat(6, minmax(0, 1fr)); } +.grid-7col { grid-column: span 7; grid-template-columns: repeat(7, minmax(0, 1fr)); } +.grid-8col { grid-column: span 8; grid-template-columns: repeat(8, minmax(0, 1fr)); } +.grid-9col { grid-column: span 9; grid-template-columns: repeat(9, minmax(0, 1fr)); } +.grid-10col { grid-column: span 10; grid-template-columns: repeat(10, minmax(0, 1fr)); } +.grid-11col { grid-column: span 11; grid-template-columns: repeat(11, minmax(0, 1fr)); } +.grid-12col { grid-column: span 12; grid-template-columns: repeat(12, minmax(0, 1fr)); } + +.grid-start-2 { grid-column-start: 2; } +.grid-start-3 { grid-column-start: 3; } +.grid-start-4 { grid-column-start: 4; } +.grid-start-5 { grid-column-start: 5; } +.grid-start-6 { grid-column-start: 6; } +.grid-start-7 { grid-column-start: 7; } +.grid-start-8 { grid-column-start: 8; } +.grid-start-9 { grid-column-start: 9; } +.grid-start-10 { grid-column-start: 10; } +.grid-start-11 { grid-column-start: 11; } +.grid-start-12 { grid-column-start: 12; } + +.grid-span-2 { grid-column-end: span 2; } +.grid-span-3 { grid-column-end: span 3; } +.grid-span-4 { grid-column-end: span 4; } +.grid-span-5 { grid-column-end: span 5; } +.grid-span-6 { grid-column-end: span 6; } +.grid-span-7 { grid-column-end: span 7; } +.grid-span-8 { grid-column-end: span 8; } +.grid-span-9 { grid-column-end: span 9; } +.grid-span-10 { grid-column-end: span 10; } +.grid-span-11 { grid-column-end: span 11; } +.grid-span-12 { grid-column-end: span 12; } + +.flex-group-center, +.flex-group-left, +.flex-group-right { + justify-content: center; + align-items: center; + text-align: center; +} + +.flex-group-left { + justify-content: flex-start; + text-align: left; +} + +.flex-group-right { + justify-content: flex-end; + text-align: right; +} + +.flex-align-left { align-items: flex-start; } +.flex-align-right { align-items: flex-end; } +.gap-xs { gap: 2px; } +.gap-sm { gap: 4px; } +.gap-md { gap: 8px; } +.gap-lg { gap: 16px; } +.flexshrink { flex: 0; } +.flex-between { justify-content: space-between; } +.flexlarge { flex: 2; } + +.align-left { + justify-content: flex-start; + text-align: left; +} + +.align-right { + justify-content: flex-end; + text-align: right; +} + +.align-center { + justify-content: center; + text-align: center; +} + +::-webkit-scrollbar-thumb { + outline: none; + border-radius: 3px; + background: #577822; + border: 1px solid var(--color-border-highlight); +} + +::-webkit-scrollbar { + width: 3px; + height: 3px; +} + +.system-vermine2047 { + .item-form { + font-family: "Roboto", sans-serif; + } + + .sheet-header { + flex: 0 auto; + overflow: hidden; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + margin-bottom: 10px; + + .profile-img { + flex: 0 0 100px; + height: 100px; + margin-right: 10px; + } + + .header-fields { + flex: 1; + } + + h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; + + input { + width: 100%; + height: 100%; + margin: 0; + } + } + } + + .sheet-tabs { + flex: 0; + } + + .sheet-body .tab, + .editor { + height: 100%; + width: 100%; + } + + .editor { + min-height: 75px; + margin-bottom: 1rem; + min-width: 100%; + + .editor-content { + min-width: 100%; + min-height: 3rem; + } + } + + .editor:hover .editor-edit { + display: block; + } + + .tox { + min-height: 25vh; + + .tox-editor-container { + background: #fff; + } + + .tox-edit-area { + padding: 0 8px; + } + } + + .resource-label { + font-weight: bold; + } + + .items-header { + height: 28px; + margin: 2px 0; + padding: 0; + align-items: center; + background: rgba(0, 0, 0, 0.05); + border: 2px groove #eeede0; + font-weight: bold; + + > * { + font-size: 14px; + text-align: center; + } + + .item-name { + font-weight: bold; + padding-left: 5px; + text-align: left; + display: flex; + } + } + + .items-list { + list-style: none; + margin: 0; + padding: 0; + overflow-y: auto; + scrollbar-width: thin; + color: #444; + + .item-list { + list-style: none; + margin: 0; + padding: 0; + } + + .item-name { + flex: 2; + margin: 0; + overflow: hidden; + font-size: 13px; + text-align: left; + align-items: center; + display: flex; + + h3, h4 { + margin: 0; + white-space: nowrap; + overflow-x: hidden; + } + } + + .item-controls { + display: flex; + flex: 0; + justify-content: flex-end; + + a { + font-size: 12px; + text-align: center; + margin: 0 6px; + } + } + + .item { + align-items: center; + padding: 0 2px; + border-bottom: 1px solid #c9c7b8; + + &:last-child { + border-bottom: none; + } + + .item-name { + color: @theme-color-dark; + + .item-image { + flex: 0 0 30px; + height: 30px; + background-size: 30px; + border: none; + margin-right: 5px; + } + } + + .item-prop { + text-align: center; + border-left: 1px solid #c9c7b8; + border-right: 1px solid #c9c7b8; + font-size: 12px; + } + + .items-header { + height: 28px; + margin: 2px 0; + padding: 0; + align-items: center; + background: rgba(0, 0, 0, 0.05); + border: 2px groove #eeede0; + font-weight: bold; + + > * { + font-size: 12px; + text-align: center; + } + + .item-name { + padding-left: 5px; + text-align: left; + } + } + } + + .effects .item { + .effect-source, + .effect-duration, + .effect-controls { + text-align: center; + border-left: 1px solid #c9c7b8; + border-right: 1px solid #c9c7b8; + font-size: 12px; + } + + .effect-controls { + border: none; + } + } + } + + .item-formula { + flex: 0 0 200px; + padding: 0 8px; + } +} + +span.game-mode { + font-family: "DistressBlack", sans-serif; + position: absolute; + margin-left: auto; + color: rgba(0, 0, 0, 0); + top: 1rem; + z-index: 900; + width: 55%; + text-align: center; + text-transform: uppercase; + font-weight: 900; + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.767) 0%, + rgba(0, 0, 0, 0.61) 17%, + rgba(0, 0, 0, 0.548) 19%, + rgba(222, 255, 221, 0.575) 24%, + rgba(255, 255, 255, 0.637) 43%, + rgba(0, 0, 0, 0.486) 47%, + rgba(254, 255, 254, 0.466) 50%, + rgba(0, 0, 0, 0.699) 63%, + rgba(134, 160, 137, 0.479) 64%, + rgba(213, 248, 210, 0.493) 100% + ); + background-clip: text; +} + +span.game-mode#game-mode-1 { color: @game-mode-1-color; } +span.game-mode#game-mode-2 { color: @game-mode-2-color; } +span.game-mode#game-mode-3 { color: @game-mode-3-color; } + +.shadow { + .shadow(); +} + +ol#chat-log, +ol.chat-log { + header.message-header { + background-color: #000; + padding: 0 1rem; + } + + .vermine-roll-message { + overflow: hidden; + padding: 4px 10px 10px; + position: relative; + + > h3 { + font-family: "DistressBlack"; + text-transform: uppercase; + font-size: @font-size-14; + margin: 6px 0 8px; + padding: 4px 10px; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.3) inset; + font-weight: 900; + border-bottom: none; + } + + > .flexrow:not(.roll-total):not(.roll-results) { + align-items: center; + gap: 6px; + margin: 4px 0; + + h4 { + font-family: "DistressBlack"; + text-transform: uppercase; + font-size: @font-size-12; + margin: 0; + border-bottom: none; + font-weight: 900; + } + + span { + font-family: "Roboto", sans-serif; + font-size: @font-size-12; + } + } + + // ── Reroll section ────────────────────────────────────────────── + .reroll-fromroll { + > h4 { + font-family: "DistressBlack"; + text-transform: uppercase; + font-size: @font-size-12; + margin: 6px 0 4px; + border-bottom: none; + font-weight: 900; + + #allowed_reroll { + font-size: @font-size-12; + } + } + } + + div.reroll { + .transition(0.3s); + max-height: 1px; + overflow: hidden; + justify-content: flex-end; + align-items: center; + gap: 8px; + + button { + text-transform: uppercase; + font-family: "DistressBlack"; + font-size: @font-size-12; + padding: 2px 12px; + max-width: fit-content; + box-shadow: 0px 0px 2px #000; + .background-image(url("@{ui-path}/scotch.webp"), no-repeat, cover, 50% 0%); + } + + &.visible { max-height: 15rem; } + } + + // ── Dice results ──────────────────────────────────────────────── + ul.roll-results { + list-style: none; + padding: 6px 0; + justify-content: center; + gap: 6px; + flex-wrap: wrap; + + li.die { + position: relative; + width: 3rem; + height: 3rem; + flex: none; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + .transition(); + border-bottom: 4px solid @color-hemolymph; + border-radius: 50%; + + &:after { + content: ""; + position: absolute; + top: -1rem; + text-wrap: nowrap; + color: #fff; + font-weight: 100; + font-size: smaller; + text-align: center; + opacity: 0; + text-shadow: 0px 0px 5px #000; + } + + &:hover::after { + opacity: 1; + color: #fff; + } + + &.human { border-top: 4px solid @totem-human-color; } + &.adapted { border-top: 4px solid @totem-adapted-color; } + &.rerollable { + cursor: pointer; + &:hover { transform: translateY(0.5rem); } + } + &.success { border-bottom-color: @color-acid-green; } + &.adapted::after { content: "adapté"; } + &.human::after { content: "humain"; } + &.rerolled { transform: translateY(0rem); } + + span { + display: none; + } + + &:not([data-result]) { + background-image: url("@{dice-path}/d10c-1.webp"); + opacity: 0.5; + filter: grayscale(1); + } + } + + // Per-face dice background images + .generate-dice-faces(@i: 1) when (@i =< 10) { + li.die[data-result="@{i}"] { + background-image: url("@{dice-path}/d10c-@{i}.webp"); + } + .generate-dice-faces(@i + 1); + } + .generate-dice-faces(); + } + + // ── Total section ─────────────────────────────────────────────── + div.roll-total { + margin-top: 8px; + padding: 8px 16px; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.3) inset; + border-radius: 2px; + gap: 16px; + justify-content: center; + + > div { + display: flex; + align-items: center; + gap: 6px; + } + + h4 { + font-family: "DistressBlack"; + text-transform: uppercase; + font-size: @font-size-12; + margin: 0; + border-bottom: none; + font-weight: 900; + text-align: center; + } + + span { + font-family: "DistressBlack"; + font-size: @font-size-20; + } + + #total { + color: @color-acid-green; + text-shadow: 0 0 4px fade(@color-acid-green, 40%); + } + + #required { + color: @color-amber; + } + } + } + + div.item-card header img { + max-width: 30%; + } +} + +// Padding supplémentaire pour les fiches groupe, npc et créature +.system-vermine2047 { + .group-content, + .npc-content, + .creature-content { + padding: 15px; + } +} diff --git a/less/dialogs.less b/less/dialogs.less new file mode 100644 index 0000000..2e95594 --- /dev/null +++ b/less/dialogs.less @@ -0,0 +1,475 @@ +@import "variables"; +@import "utilities"; + +.window-app.vermineDialog { + max-width: 50vw; + height: fit-content; + + .window-content { + .background-image(url("@{ui-path}/box_background.webp"), repeat); + color: #000; + } + + details > summary::after { + content: "▶️"; + position: relative; + right: 40%; + } + + details[open] > summary::after { + content: "🔽"; + } + + .grid { + justify-content: space-around; + .shadow(0px, 1px, 10px, rgba(0, 0, 0, 0.555)); + align-items: center; + padding: 0.5rem 0.2rem; + + > * { margin: 0 0.3rem; } + } + + label { + font-family: "DistressBlack", sans-serif; + font-size: larger; + } + + select { + max-width: fit-content; + .custom-select-style(); + option { max-width: fit-content; } + } + + .dialog-buttons { + display: flex; + justify-content: space-around; + flex-direction: row; + + button { + display: block; + flex: 0.3; + color: @theme-color-dark; + } + } +} + +input[type="range"] { .custom-input-style(); } +input[type="checkbox"], +input[type="radio"] { .custom-checkbox-radio(); } + +input[type="radio"] { + width: 1rem; + height: 1rem; + + &:after { + width: 0.8rem; + background-size: 100% 100%; + top: 5%; + left: 5%; + width: 90%; + height: 90%; + background-size: 30% 30%; + background-position: center; + } + + &:not([disabled]):hover::after { background-size: 90% 90%; } + + &:checked::after { + content: ""; + background-size: 70% 70%; + top: 5%; + left: 5%; + position: relative; + background-color: rgba(26, 1, 1, 0); + } +} + +.app.vermine2047.trait-selector { + .form-group { + .shadow(0, 0, 30px, gray); + padding: 0.3rem 0.5rem; + border: 3px solid #8e9010; + + &:has(input[type="checkbox"]:checked) { + border: 3px solid green; + } + + label { + display: inline-flex; + align-items: center; + justify-content: space-around; + width: 100%; + text-align: center; + border-bottom: 2px solid #000; + } + } +} + +.app .actor.choose { + div.actor { + position: relative; + + img { + border-radius: 50%; + .shadow(0px, 0px, 8px, #000); + } + + span.actor-name { + position: absolute; + text-align: center; + background-color: rgba(255, 255, 255, 0.562); + border: 5px; + width: 100%; + padding: 0 1rem; + border-radius: 5px; + .shadow(0px, 0px, 8px, #000); + } + } +} + +iframe { + min-height: 500px; + + .tabs.moods-headings { + max-width: 1px; + } +} + +// ── Roll Dialog V2 ───────────────────────────────────────────────────── +.application.vermine-roll { + .window-content { + overflow-y: auto; + } + + .roll-dialog-content { + display: flex; + flex-direction: column; + gap: 6px; + padding: 10px; + height: 100%; + overflow-y: auto; + color: @color-text-light-0; + + .dice-pool { + flex: 1; + display: flex; + flex-direction: column; + gap: inherit; + } + + label { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-12; + } + + input, select, textarea { + color: @color-chitin-dark; + background: @color-text-light-0; + } + + select { + .custom-select-style(); + width: 100%; + } + + input[type="range"] { .custom-input-style(); } + input[type="checkbox"], + input[type="radio"] { .custom-checkbox-radio(); } + + // ── Main roll section: ability + skill side-by-side ───────────── + .main-roll-section { + gap: 10px; + + > .flexcol { + flex: 1; + gap: 4px; + min-width: 0; + + > label { + font-size: @font-size-13; + color: @color-amber; + white-space: nowrap; + } + + select { + min-height: 28px; + width: 100%; + } + } + + .ability-score { + gap: 4px; + font-size: @font-size-12; + color: @color-text-light-0; + opacity: 0.85; + + #abilityScoreValue { + font-weight: bold; + color: @color-acid-green; + } + } + } + + // ── Collapsible sections (specialties, difficulty, bonuses) ───── + details { + margin-top: 2px; + + summary { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-12; + padding: 6px 10px; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.5) inset; + cursor: pointer; + color: @theme-color-dark; + display: flex; + align-items: center; + gap: 8px; + + &::after { + content: "▶"; + margin-left: auto; + font-size: @font-size-11; + } + + &:hover { + box-shadow: 0 0 10px @theme-color-highlight inset; + } + + span.label { + font-family: "DistressBlack", sans-serif; + font-size: @font-size-12; + } + + .current-values, + .current-specialty, + .bonus-count { + font-family: "Roboto", sans-serif; + font-size: @font-size-11; + text-transform: none; + opacity: 0.8; + } + } + + &[open] > summary::after { + content: "▼"; + } + + > div:not(summary) { + padding: 8px; + background: fade(@color-chitin-dark, 40%); + } + } + + // ── Difficulty section ────────────────────────────────────────── + .difficulty-controls { + gap: 12px; + + > .flexcol { + flex: 1; + gap: 4px; + + label { + font-size: @font-size-11; + color: @color-amber; + } + } + } + + // ── Specialties section ───────────────────────────────────────── + .specialty-options { + gap: 8px; + flex-wrap: wrap; + + label { + display: flex; + align-items: center; + gap: 4px; + font-size: @font-size-12; + font-family: "Roboto", sans-serif; + text-transform: none; + color: @color-text-light-0; + cursor: pointer; + + &:hover { + color: @color-amber; + } + } + + input[type="radio"] { + width: 14px; + height: 14px; + flex: none; + } + } + + // ── Bonuses grid ──────────────────────────────────────────────── + .bonus-grid { + margin: 0; + gap: 8px; + + .bonus-item { + align-items: center; + gap: 6px; + padding: 4px 0; + + > label.label { + font-size: @font-size-11; + color: @color-text-light-0; + white-space: nowrap; + } + + input[type="number"] { + width: 40px; + text-align: center; + padding: 2px; + } + + input[type="checkbox"] + label { + font-size: @font-size-12; + font-family: "Roboto", sans-serif; + text-transform: none; + color: @color-text-light-0; + } + + .item-list { + gap: 4px; + + label { + align-items: center; + gap: 4px; + font-size: @font-size-11; + font-family: "Roboto", sans-serif; + text-transform: none; + color: @color-text-light-0; + cursor: pointer; + + &:hover { + color: @color-amber; + } + } + } + + &.full-width { + grid-column: 1 / -1; + } + } + + // Totems sub-section + .totems-section { + flex-wrap: wrap; + gap: 6px; + + > label.label { + width: 100%; + } + + .totem-options { + gap: 12px; + } + + .totem-option { + display: flex; + align-items: center; + gap: 4px; + color: @color-text-light-0; + font-size: @font-size-12; + font-family: "Roboto", sans-serif; + text-transform: none; + cursor: pointer; + + &:hover { + color: @color-amber; + } + + small { + opacity: 0.7; + } + } + + .totem-hint { + width: 100%; + font-size: @font-size-11; + color: @color-text-light-0; + opacity: 0.6; + font-style: italic; + } + } + } + + // ── Total dice pool section ───────────────────────────────────── + .total-section { + align-items: center; + padding: 10px; + border-top: 1px solid @color-border-dark-4; + margin-top: 4px; + gap: 10px; + background: fade(@color-chitin-dark, 50%); + + .label { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-16; + color: @color-text-light-0; + } + + .total-value { + font-family: "DistressBlack", sans-serif; + font-size: @font-size-28; + color: @color-acid-green; + text-shadow: 0 0 8px fade(@color-acid-green, 40%); + } + + .totem-selector { + font-size: @font-size-11; + color: @color-text-light-0; + opacity: 0.7; + margin-left: auto; + + select { + font-size: @font-size-11; + padding: 1px 4px; + width: auto; + display: inline; + } + } + } + } + + // ── Footer buttons ─────────────────────────────────────────────── + .sheet-footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 8px 10px; + margin-top: auto; + border-top: 1px solid @color-border-dark-4; + background: fade(@color-chitin-dark, 60%); + + button { + font-family: "DistressBlack", sans-serif; + text-transform: uppercase; + font-size: @font-size-13; + padding: 6px 20px; + border: none; + background: 50% 0% / cover no-repeat url("@{ui-path}/scotch.webp"); + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.979) inset; + cursor: pointer; + color: @theme-color-dark; + .transition(); + display: flex; + align-items: center; + gap: 6px; + + &:hover { + box-shadow: 0 0 10px @theme-color-highlight inset; + } + + i { + font-size: @font-size-14; + } + } + } +} diff --git a/less/items.less b/less/items.less new file mode 100644 index 0000000..8c506e6 --- /dev/null +++ b/less/items.less @@ -0,0 +1,84 @@ +@import "variables"; +@import "utilities"; + +.system-vermine2047 .item-formula { + flex: 0 0 200px; + padding: 0 8px; +} + +.system-vermine2047 .vermine2047.item .window-content { + padding: 0 1rem; + + .flexrow { align-items: center; } + + header, h1, h2, h3, h4, h5 { + .background-image(url("@{ui-path}/scotch.webp"), no-repeat, cover, 50% 50%); + text-transform: uppercase; + font-family: "DistressBlack"; + margin-top: 1rem; + border-bottom: none; + } + + h2, h3, h4 { text-align: center; } + h5 { margin-bottom: 0; } + + .resource-label { + font-size: 0.75rem; + color: @color-text-light-2; + text-shadow: 0 0 3px rgba(0, 0, 0, 0.9); + } + + .resource { + border: none; + border-left: 1px solid gray; + padding: 0.2rem 1rem; + text-align: center; + + .flexrow { + min-width: 5rem; + box-shadow: none; + } + } + + .damages-row { + margin: 0; + .radios { + margin: 0; + padding: 0.5rem; + } + } + + .damage-pannes, + .damage-state, + .damage-effect { + text-align: center; + font-family: "DistressBlack"; + } + + select { + background: rgba(0, 0, 0, 0.4); + color: @color-text-light-1; + font-family: "DistressBlack", sans-serif; + font-size: 0.875rem; + text-align: center; + border: none; + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.979) inset; + text-shadow: 0 0 3px rgba(0, 0, 0, 0.9); + } + + option { + background: #1a1a1a; + color: @color-text-light-1; + } + + .traits { + .shadow(); + + h3, h4 { margin: 0; } + } +} + +ol#chat-log div.item-card header img, +ol.chat-log div.item-card header img { + max-width: 30%; +} diff --git a/less/legacy.less b/less/legacy.less new file mode 100644 index 0000000..079aa84 --- /dev/null +++ b/less/legacy.less @@ -0,0 +1,23 @@ +.sans-font { + font-family: "DistressBlack", sans-serif; +} + +.chat-message .message-header { + line-height: 20px; + color: white; + text-shadow: 0px 0px 5px black; + background: #1918135e; +} + +.flex-center { + align-items: center; + justify-content: center; +} + +.flex-around { + justify-content: space-around; +} + +.flexsmall { + flex: 0.5; +} diff --git a/less/utilities.less b/less/utilities.less new file mode 100644 index 0000000..53228f0 --- /dev/null +++ b/less/utilities.less @@ -0,0 +1,257 @@ +// ============================================ +// Utilitaires LESS pour Vermine2047 +// Mixins, fonctions et classes utilitaires +// ============================================ + +// Mixin pour les ombres standard +.shadow() { + box-shadow: 0 2rem @theme-color-shadow; + margin: 2rem 0; +} +.shadow(@color) { + box-shadow: 0 2rem @color; + margin: 2rem 0; +} +.shadow(@color, @blur) { + box-shadow: 0 @blur @color; + margin: 2rem 0; +} +.shadow(@h-offset, @v-offset, @blur, @color) { + box-shadow: @h-offset @v-offset @blur @color; + margin: 2rem 0; +} + +// Mixin pour le style des hexagones +.hexa-style(@bg-color: rgba(255, 255, 255, 0.425), @bg-color-end: rgba(0, 0, 0, 0.288)) { + clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); + background: radial-gradient(circle, @bg-color 0%, @bg-color-end 100%); + max-height: 1.5rem; + max-width: 1.5rem; + min-width: 1.5rem; + aspect-ratio: 1/1; + color: #000; + vertical-align: center; + text-align: center; +} + +// Mixin pour les boutons rollable +.rollable-style() { + &:hover, + &:focus { + color: #000; + text-shadow: 0 0 10px red; + cursor: pointer; + } +} + +// Mixin pour les inputs de type hexa +.input-hexa-style() { + .hexa-style(); + input { + width: 1rem; + } + input[type="radio"] { + opacity: 0; + } + input[type="radio"]::after, + input[type="radio"]::before { + display: none; + } +} + +// Mixin pour les classes flex +.flex-container(@direction: row, @wrap: nowrap, @justify: flex-start, @align: stretch) { + display: flex; + flex-direction: @direction; + flex-wrap: @wrap; + justify-content: @justify; + align-items: @align; +} + +// Mixin pour le style des cartes +.card-style(@bg-color: rgba(0, 0, 0, 0.1), @border-color: #444) { + border: 1px solid @border-color; + border-radius: 4px; + padding: 10px; + margin-bottom: 15px; + background: @bg-color; +} + +// Mixin pour les titres de carte +.card-title(@border-color: #666) { + margin-top: 0; + border-bottom: 1px solid @border-color; + padding-bottom: 5px; + text-align: center; +} + +// Mixin pour les éléments de liste avec bordure +.list-item-with-border(@border-color: #333) { + padding: 5px; + border-bottom: 1px solid @border-color; + &:last-child { + border-bottom: none; + } +} + +// Mixin pour les groupes de formulaires +.form-group-style(@margin-bottom: 10px) { + margin-bottom: @margin-bottom; + label { + display: block; + margin-bottom: 3px; + font-weight: bold; + font-size: 12px; + } + input, + select { + width: 100%; + font-size: 12px; + } +} + +// Mixin pour les ressources +.resource-style(@bg-color: rgba(0, 0, 0, 0.05)) { + padding: 5px; + background: @bg-color; + border-radius: 4px; + margin: 0 5px; + label { + font-weight: bold; + margin-right: 8px; + min-width: 60px; + font-size: 12px; + } + .resource-content { + display: flex; + align-items: center; + } +} + +// Mixin pour les badges de rareté +.rarity-badge(@bg-color, @text-color) { + display: inline-block; + padding: 1px 4px; + border-radius: 3px; + font-size: 10px; + font-weight: bold; + margin-left: 5px; + background: @bg-color; + color: @text-color; +} + +// Mixin pour le fond avec image +.background-image(@url, @repeat: no-repeat, @size: auto, @position: center) { + background: @url @repeat @position / @size; +} + +// Mixin pour les transitions +.transition(@property: all, @duration: 0.2s, @timing: ease-out) { + transition: @property @duration @timing; +} + +// Mixin pour les styles de l'éditeur TinyMCE +.tiny-mce-style() { + .tox { + min-height: 25vh; + .tox-editor-container { + background: #fff; + } + .tox-edit-area { + padding: 0 8px; + } + } +} + +// Mixin pour les styles des inputs personnalisés +.custom-input-style() { + appearance: none; + background: rgba(0, 0, 0, 0); + cursor: pointer; + width: 100%; + + &::-webkit-slider-runnable-track { + .background-image(url("@{ui-path}/scotch.webp"), no-repeat, auto, center); + background-size: 100% auto; + height: 0.4rem; + border: none; + box-shadow: 0px 0px 13px rgba(31, 26, 26, 0.979) inset; + } + + &::-webkit-slider-thumb { + appearance: none; + margin-top: -0.3rem; + height: 1rem; + width: 1rem; + border: none; + border-radius: 50%; + .background-image(url("@{totems-path}/human.webp"), no-repeat, cover); + filter: contrast(2); + box-shadow: 0px 0px 10px #000; + + &:focus { + box-shadow: 0px 0px 10px #ff0; + } + } +} + +// Mixin pour les checkbox et radio boutons personnalisés +.custom-checkbox-radio() { + -webkit-appearance: none; + appearance: none; + background: rgba(0, 0, 0, 0); + box-shadow: 0px 0px 3px #85854e; + cursor: pointer; + width: 1.5rem; + height: 1rem; + border-radius: 0.4rem; + .transition(); + clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); + box-shadow: 0px 0px 6px #000 inset; + background-color: rgba(61, 11, 11, 0.658); + + &[disabled=true] { + filter: grayscale(1); + } + + &:after { + content: " "; + .background-image(url("@{totems-path}/human.webp"), no-repeat, auto, 50% 150%); + position: relative; + top: 10%; + left: 0%; + width: 100%; + height: 80%; + display: block; + border-radius: 0%; + padding: 0; + .transition(); + } + + &:checked { + background-color: rgba(26, 107, 12, 0.658); + &:after { + font-weight: 900; + background-color: rgba(26, 1, 1, 0); + left: 50%; + } + } +} + +// Mixin pour les selects personnalisés +.custom-select-style() { + border: none; + .background-image(url("@{ui-path}/scotch.webp"), no-repeat, auto, 100% 100%); + box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.979) inset; + + &[disabled] { + color: #000; + text-shadow: 0px 0px 15px #000; + } + + option { + appearance: none; + border: none; + .background-image(url("@{ui-path}/scotch.webp"), no-repeat, auto, 100% 100%); + } +} diff --git a/less/variables.less b/less/variables.less new file mode 100644 index 0000000..a8d6109 --- /dev/null +++ b/less/variables.less @@ -0,0 +1,175 @@ +// ============================================ +// Variables LESS pour Vermine2047 +// Converti depuis les variables CSS du fichier vermine2047.css +// ============================================ + +// Couleurs de texte - clair +@color-text-light-highlight: #96d696; +@color-text-light-heading: #9fd8a8; +@color-text-light-primary: #a4b5b3; + +// Couleurs de texte - foncé +@color-text-dark-primary: #131919; +@color-text-dark-secondary: #444b4a; +@color-text-dark-header: #1d2223; +@color-text-dark-inactive: #71797a; + +// Couleurs de texte - autres +@color-text-hyperlink: #5aaf0a; + +// Niveaux de gris pour le texte clair +@color-text-light-0: #fff; +@color-text-light-1: #e0f0f0; +@color-text-light-2: #c9e0c0; +@color-text-light-3: #90c4a4; +@color-text-light-4: #80c08b; +@color-text-light-5: #60b06b; +@color-text-light-6: #40a05d; +@color-text-light-7: #208028; + +// Niveaux de gris pour le texte foncé +@color-text-dark-1: #111; +@color-text-dark-2: #222; +@color-text-dark-3: #444; +@color-text-dark-4: #555; +@color-text-dark-5: #666; +@color-text-dark-6: #777; + +// Couleurs de bordure - clair +@color-border-light-1: #b0d9b0; +@color-border-light-2: #80c0c0; + +// Couleurs de bordure - foncé +@color-border-dark-1: #131919; +@color-border-dark-2: #1d2223; +@color-border-dark-3: #2d3333; +@color-border-dark-4: #3d4444; +@color-border-dark-5: #668888; + +// Couleurs d'ombre +@color-shadow-primary: #7bb60d; +@color-shadow-highlight: #85cc01d0; +@color-shadow-dark: #000; + +// Couleurs de soulignement +@color-underline-inactive: #71797a; +@color-underline-active: #1a1944; +@color-underline-header: #228247; + +// Couleurs de bordure - autres +@color-border-light-highlight: #b0d9b0; +@color-border-light-primary: #a4b5b3; +@color-border-light-secondary: #9fc7d8; +@color-border-light-tertiary: #71797a; +@color-border-dark: #000; +@color-border-dark-primary: #131919; +@color-border-dark-secondary: #1d2223; +@color-border-dark-tertiary: #444b4a; +@color-border-highlight: #85c019; +@color-border-highlight-alt: #70c008; + +// Couleurs de fond +@color-bg-btn-minor-inactive: #9fc7d8; +@color-bg-btn-minor-active: #a4b5b3; +@color-bg-option: #ccdada; + +// Autres couleurs +@color-checkbox-checked: #666; +@color-ownership-none: #00ff55; +@color-ownership-observer: #71797a; +@color-ownership-owner: #a4b5b3; + +// Niveaux de log +@color-level-info: #b95c87; +@color-level-warning: #04b184; +@color-level-error: #03750; +@color-level-success: #3c266c; + +// Z-index +@z-index-canvas: 0; +@z-index-app: 30; +@z-index-ui: 60; +@z-index-window: 100; +@z-index-tooltip: 9999; + +// Dimensions +@sidebar-width: 300px; +@sidebar-header-height: 32px; +@sidebar-item-height: 48px; +@hotbar-height: 52px; +@hotbar-width: 578px; +@macro-size: 50px; +@players-width: 200px; +@form-field-height: 26px; + +// Polices +@font-mono: monospace; + +// Tailles de police +@font-size-11: 0.6875rem; +@font-size-12: 0.75rem; +@font-size-13: 0.8125rem; +@font-size-14: 0.875rem; +@font-size-16: 1rem; +@font-size-18: 1.125rem; +@font-size-20: 1.25rem; +@font-size-24: 1.5rem; +@font-size-28: 1.75rem; +@font-size-32: 2rem; +@font-size-48: 3rem; + +// Hauteurs de ligne +@line-height-12: 0.75rem; +@line-height-16: 1rem; +@line-height-20: 1.25rem; +@line-height-30: 1.875rem; + +// ============================================ +// Variables spécifiques Vermine2047 +// ============================================ + +// Couleurs thématiques +@theme-color-primary: #7e7544; +@theme-color-secondary: #dfdfdf; +@theme-color-accent: #1fa832; +@theme-color-dark: #191813; +@theme-color-light: #4e564c; +@theme-color-shadow: rgba(0, 0, 0, 0.7098039216); +@theme-color-highlight: #005a3c; + +// Couleurs insecte / organique +@color-acid-green: #6ab04c; +@color-amber: #e8b84b; +@color-chitin-dark: #2a2520; +@color-hemolymph: #8b4513; +@color-membrane: rgba(106, 176, 76, 0.1); +@color-honeycomb: #c9a84c; + +// Couleurs pour les dés et totems +@dice-pool-color: rgb(94, 90, 77); +@dice-reroll-color: rgb(187, 182, 165); +@totem-human-color: #064930; +@totem-adapted-color: #553402; + +// Couleurs pour les niveaux de rareté +@rarity-0-bg: #444; +@rarity-0-text: #aaa; +@rarity-1-bg: #5a7a5a; +@rarity-1-text: #fff; +@rarity-2-bg: #7a9a7a; +@rarity-2-text: #000; +@rarity-3-bg: #9a5a9a; +@rarity-3-text: #fff; + +// Couleurs pour les modes de jeu +@game-mode-1-color: rgba(235, 218, 143, 0.8); +@game-mode-2-color: #83f883; +@game-mode-3-color: rgba(245, 124, 124, 0.8); + +// Chemins des assets +@assets-path: "/systems/vermine2047/assets"; +@images-path: "@{assets-path}/images"; +@fonts-path: "@{assets-path}/fonts"; +@ui-path: "@{images-path}/ui"; +@totems-path: "@{images-path}/ui/totems"; +@dice-path: "@{assets-path}/dice"; diff --git a/less/vermine2047.less b/less/vermine2047.less new file mode 100644 index 0000000..217f224 --- /dev/null +++ b/less/vermine2047.less @@ -0,0 +1,37 @@ +// ============================================ +// Fichier principal LESS pour Vermine2047 +// Ce fichier importe tous les modules LESS +// ============================================ + +// 1. Import de police Google (doit être en premier car c'est du CSS pur) +@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap"); + +// 2. Variables et utilitaires (doivent être importés avant tout le reste) +@import "variables"; +@import "utilities"; + +// 3. Styles de base +@import "base"; + +// 4. Legacy SCSS converti en LESS +@import "legacy"; + +// 5. Styles des acteurs +@import "actor/actor"; +@import "actor/totem"; +@import "actor/npc"; +@import "actor/group"; +@import "actor/creature"; + +// 6. Styles des items +@import "items"; + +// 7. Styles des dialogs +@import "dialogs"; + +// ============================================ +// Notes: +// - L'ordre des imports est important +// - Les variables doivent être définies avant d'être utilisées +// - Les mixins doivent être définis avant d'être appelés +// ============================================ diff --git a/module/applications/sheets/_module.mjs b/module/applications/sheets/_module.mjs new file mode 100644 index 0000000..f311583 --- /dev/null +++ b/module/applications/sheets/_module.mjs @@ -0,0 +1,20 @@ +export { VermineBaseActorSheet } from "./base-actor-sheet.mjs" +export { VermineBaseItemSheet } from "./base-item-sheet.mjs" +export { VermineCharacterSheetV2 } from "./character-sheet.mjs" +export { VermineNpcSheetV2 } from "./npc-sheet.mjs" +export { VermineGroupSheetV2 } from "./group-sheet.mjs" +export { VermineCreatureSheetV2 } from "./creature-sheet.mjs" +export { + VermineItemSheetV2, + VermineWeaponSheetV2, + VermineDefenseSheetV2, + VermineVehicleSheetV2, + VermineAbilitySheetV2, + VermineSpecialtySheetV2, + VermineBackgroundSheetV2, + VermineTraumaSheetV2, + VermineEvolutionSheetV2, + VermineRumorSheetV2, + VermineTargetSheetV2, + VermineRiteSheetV2 +} from "./item-sheets.mjs" diff --git a/module/applications/sheets/base-actor-sheet.mjs b/module/applications/sheets/base-actor-sheet.mjs new file mode 100644 index 0000000..28b5b26 --- /dev/null +++ b/module/applications/sheets/base-actor-sheet.mjs @@ -0,0 +1,207 @@ +import { onManageActiveEffect } from "../../system/effects.mjs" + +const { HandlebarsApplicationMixin } = foundry.applications.api + +/** + * Fiche de base pour tous les acteurs Vermine 2047 (ApplicationV2). + * Remplace VermineActorSheet (AppV1). + */ +export class VermineBaseActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) { + + // ── Mode édition / jeu ────────────────────────────────────────────── + + static SHEET_MODES = { EDIT: 0, PLAY: 1 } + + _sheetMode = this.constructor.SHEET_MODES.PLAY + + get isPlayMode() { return this._sheetMode === this.constructor.SHEET_MODES.PLAY } + get isEditMode() { return this._sheetMode === this.constructor.SHEET_MODES.EDIT } + + // ── Options par défaut ────────────────────────────────────────────── + + static DEFAULT_OPTIONS = { + classes: ["vermine2047", "actor"], + position: { width: 800, height: "auto" }, + form: { submitOnChange: true }, + window: { resizable: true }, + dragDrop: [{ dragSelector: ".item", dropSelector: null }], + actions: { + editImage: VermineBaseActorSheet.#onEditImage, + toggleSheet: VermineBaseActorSheet.#onToggleSheet, + edit: VermineBaseActorSheet.#onItemEdit, + delete: VermineBaseActorSheet.#onItemDelete, + create: VermineBaseActorSheet.#onItemCreate, + roll: VermineBaseActorSheet.#onRollItem, + clickRadio: VermineBaseActorSheet.#onClickRadioHexa, + effectControl: VermineBaseActorSheet.#onEffectControl, + chooseTotem: VermineBaseActorSheet.#onChooseTotem + } + } + + // ── Drag & Drop ───────────────────────────────────────────────────── + + #dragDrop + + constructor(options = {}) { + super(options) + this.#dragDrop = this.#createDragDropHandlers() + } + + #createDragDropHandlers() { + return this.options.dragDrop.map(d => { + d.permissions = { + dragstart: this._canDragStart.bind(this), + drop: this._canDragDrop.bind(this) + } + d.callbacks = { + dragover: this._onDragOver.bind(this), + drop: this._onDrop.bind(this) + } + return new foundry.applications.ux.DragDrop.implementation(d) + }) + } + + _canDragStart() { return this.isEditable } + _canDragDrop() { return this.isEditable } + + // ── Contexte commun ───────────────────────────────────────────────── + + async _prepareContext() { + return { + fields: this.document.schema.fields, + systemFields: this.document.system.schema.fields, + actor: this.document, + system: this.document.system, + source: this.document.toObject(), + config: CONFIG.VERMINE, + rollData: this.document.getRollData(), + isGM: game.user.isGM, + isEditMode: this.isEditMode, + isPlayMode: this.isPlayMode, + isEditable: this.isEditable + } + } + + // ── Rendu ─────────────────────────────────────────────────────────── + + async _onRoll(event) { + event.preventDefault() + const el = event.currentTarget + const type = el.dataset.type + const label = el.dataset.label + if (!type || !label) return + const { default: RollDialog } = await import("../../system/dialogs/rollDialog.mjs") + const dialog = await RollDialog.create({ + actorId: this.document.id, + rolltype: type, + label + }) + if (dialog) dialog.render(true) + } + + // ── Actions ───────────────────────────────────────────────────────── + + _onRender(context, options) { + super._onRender(context, options) + this.#dragDrop.forEach(d => d.bind(this.element)) + this.element.querySelectorAll(".rollable").forEach(el => { + el.addEventListener("click", this._onRoll.bind(this)) + }) + // Auto-fill empty number inputs on change to prevent validation errors + this.element.addEventListener("change", e => { + const input = e.target + if (input?.type === "number" && !input.value && input.name && input !== document.activeElement) { + input.value = "0" + } + }, { capture: true }) + } + + /** @override */ + async _onDropItem(item) { + const doc = item instanceof foundry.abstract.Document ? item : await fromUuid(item.uuid) + if (!doc) return + const itemData = doc.toObject() + await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false }) + } + + static #onToggleSheet() { + const modes = this.constructor.SHEET_MODES + this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT + this.render() + } + + static async #onEditImage(event, target) { + const attr = target.dataset.edit ?? "img" + const current = foundry.utils.getProperty(this.document, attr) + const fp = new FilePicker({ + current, + type: "image", + callback: (path) => this.document.update({ [attr]: path }), + top: this.position.top + 40, + left: this.position.left + 10 + }) + return fp.browse() + } + + static async #onItemEdit(event, target) { + const id = target.closest("[data-item-id]")?.dataset?.itemId + const uuid = target.closest("[data-item-uuid]")?.dataset?.itemUuid + let item + if (uuid) item = await fromUuid(uuid) + if (!item) item = this.document.items.get(id) + item?.sheet.render(true) + } + + static async #onItemDelete(event, target) { + const itemUuid = target.closest("[data-item-uuid]")?.dataset?.itemUuid + if (itemUuid) { + const item = await fromUuid(itemUuid) + await item?.deleteDialog() + return + } + const id = target.closest("[data-item-id]")?.dataset?.itemId + const item = this.document.items.get(id) + await item?.deleteDialog() + } + + static async #onItemCreate(event, target) { + const type = target.dataset.type + if (!type) return + const name = game.i18n.localize("ITEMS.new_" + type) + await this.document.createEmbeddedDocuments("Item", [{ name, type }]) + } + + static async #onRollItem(event, target) { + const id = target.closest("[data-item-id]")?.dataset?.itemId + if (!id) return + const item = this.document.items.get(id) + item?.roll() + } + + static #onClickRadioHexa(event, target) { + event.preventDefault() + event.stopPropagation() + const input = target + const update = {} + let current = this.document + const propTree = input.name.split(".") + for (const prop of propTree) { + current = current[prop] + } + if (current != input.value) { + update[input.name] = parseInt(input.value) + } else { + update[input.name] = parseInt(input.value) - 1 + } + this.document.update(update) + } + + static #onEffectControl(event, target) { + onManageActiveEffect(event, this.document) + } + + static async #onChooseTotem(event, target) { + const { TotemPicker } = await import("../../system/applications.mjs") + new TotemPicker(target, this.document).render(true) + } +} diff --git a/module/applications/sheets/base-item-sheet.mjs b/module/applications/sheets/base-item-sheet.mjs new file mode 100644 index 0000000..eaedf12 --- /dev/null +++ b/module/applications/sheets/base-item-sheet.mjs @@ -0,0 +1,114 @@ +const { HandlebarsApplicationMixin } = foundry.applications.api + +/** + * Fiche de base pour tous les items Vermine 2047 (ApplicationV2). + */ +export class VermineBaseItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) { + + // ── Mode édition ──────────────────────────────────────────────────── + + static SHEET_MODES = { EDIT: 0, PLAY: 1 } + + _sheetMode = this.constructor.SHEET_MODES.PLAY + + get isPlayMode() { return this._sheetMode === this.constructor.SHEET_MODES.PLAY } + get isEditMode() { return this._sheetMode === this.constructor.SHEET_MODES.EDIT } + + // ── Options par défaut ────────────────────────────────────────────── + + static DEFAULT_OPTIONS = { + classes: ["vermine2047", "item"], + position: { width: 560, height: "auto" }, + form: { submitOnChange: true }, + window: { resizable: true }, + actions: { + editImage: VermineBaseItemSheet.#onEditImage, + toggleSheet: VermineBaseItemSheet.#onToggleSheet, + clickDamage: VermineBaseItemSheet.#onClickDamage, + openTraits: VermineBaseItemSheet.#onOpenTraits + } + } + + // ── Drag & Drop ───────────────────────────────────────────────────── + + #dragDrop + + constructor(options = {}) { + super(options) + this.#dragDrop = this.#createDragDropHandlers() + } + + #createDragDropHandlers() { + if (!this.options.dragDrop) return [] + return this.options.dragDrop.map(d => { + d.permissions = { + dragstart: this._canDragStart.bind(this), + drop: this._canDragDrop.bind(this) + } + d.callbacks = { + dragover: this._onDragOver.bind(this), + drop: this._onDrop.bind(this) + } + return new foundry.applications.ux.DragDrop.implementation(d) + }) + } + + _canDragStart() { return this.isEditable } + _canDragDrop() { return this.isEditable } + + // ── Contexte commun ───────────────────────────────────────────────── + + async _prepareContext() { + return { + fields: this.document.schema.fields, + systemFields: this.document.system.schema.fields, + item: this.document, + system: this.document.system, + source: this.document.toObject(), + config: CONFIG.VERMINE, + isEditMode: this.isEditMode, + isPlayMode: this.isPlayMode, + isEditable: this.isEditable + } + } + + // ── Rendu ─────────────────────────────────────────────────────────── + + _onRender(context, options) { + this.#dragDrop.forEach(d => d.bind(this.element)) + } + + // ── Actions ───────────────────────────────────────────────────────── + + static #onToggleSheet() { + const modes = this.constructor.SHEET_MODES + this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT + this.render() + } + + static async #onEditImage(event, target) { + const attr = target.dataset.edit ?? "img" + const current = foundry.utils.getProperty(this.document, attr) + const fp = new FilePicker({ + current, + type: "image", + callback: (path) => this.document.update({ [attr]: path }), + top: this.position.top + 40, + left: this.position.left + 10 + }) + return fp.browse() + } + + static #onClickDamage(event, target) { + // Les radios de dégâts sont 1-based dans le template (value="{{@index}}" avec index 1..max) + // mais le stockage est 0-based. On soustrait 1 avant de sauvegarder. + const prop = target.name + const value = parseInt(target.value) - 1 + this.document.update({ [prop]: value }) + } + + static async #onOpenTraits(event, target) { + const { TraitSelector } = await import("../../system/applications.mjs") + new TraitSelector(this.document).render(true) + } +} diff --git a/module/applications/sheets/character-sheet.mjs b/module/applications/sheets/character-sheet.mjs new file mode 100644 index 0000000..b24bee6 --- /dev/null +++ b/module/applications/sheets/character-sheet.mjs @@ -0,0 +1,98 @@ +import { VermineBaseActorSheet } from "./base-actor-sheet.mjs" + +export class VermineCharacterSheetV2 extends VermineBaseActorSheet { + + static DEFAULT_OPTIONS = { + classes: ["character"], + position: { width: 860, height: 720 }, + window: { contentClasses: ["character-content"] }, + actions: { + addSpecialty: VermineCharacterSheetV2.#onAddSpecialty + } + } + + static PARTS = { + main: { template: "systems/vermine2047/templates/actor/appv2/character-main.hbs" }, + tabs: { template: "templates/generic/tab-navigation.hbs" }, + abilities: { template: "systems/vermine2047/templates/actor/appv2/character-abilities.hbs" }, + totem: { template: "systems/vermine2047/templates/actor/appv2/character-totem.hbs" }, + equipment: { template: "systems/vermine2047/templates/actor/appv2/character-equipment.hbs" }, + stories: { template: "systems/vermine2047/templates/actor/appv2/character-stories.hbs" }, + combat: { template: "systems/vermine2047/templates/actor/appv2/character-combat.hbs" } + } + + tabGroups = { sheet: "abilities" } + + #getTabs() { + const tabs = { + abilities: { id: "abilities", group: "sheet", icon: "fas fa-address-card", label: "VERMINE.tabs.abilities" }, + totem: { id: "totem", group: "sheet", icon: "fas fa-star", label: "VERMINE.tabs.totem" }, + equipment: { id: "equipment", group: "sheet", icon: "fas fa-hammer", label: "VERMINE.tabs.equipment" }, + stories: { id: "stories", group: "sheet", icon: "fas fa-book-open-reader", label: "VERMINE.tabs.stories" }, + combat: { id: "combat", group: "sheet", icon: "fas fa-medal", label: "VERMINE.tabs.combat" } + } + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] === v.id + v.cssClass = v.active ? "active" : "" + } + return tabs + } + + async _prepareContext() { + const context = await super._prepareContext() + context.tabs = this.#getTabs() + return context + } + + async _preparePartContext(partId, context) { + const doc = this.document + context.systemFields = doc.system.schema.fields + switch (partId) { + case "main": break + case "abilities": + context.tab = context.tabs.abilities + break + case "totem": + context.tab = context.tabs.totem + context.abilities = doc.itemTypes.ability.filter(i => i.system.type !== "totem") + context.totem_abilities = doc.itemTypes.ability.filter(i => i.system.type === "totem") + context.specialties = doc.itemTypes.specialty + context.backgrounds = doc.itemTypes.background + context.traumas = doc.itemTypes.trauma + context.evolutions = doc.itemTypes.evolution + break + case "equipment": + context.tab = context.tabs.equipment + context.gear = doc.itemTypes.item + context.weapons = doc.itemTypes.weapon + context.defenses = doc.itemTypes.defense + context.vehicles = doc.itemTypes.vehicle + break + case "stories": + context.tab = context.tabs.stories + break + case "combat": + context.tab = context.tabs.combat + const { prepareActiveEffectCategories } = await import("../../system/effects.mjs") + context.effects = prepareActiveEffectCategories(doc.effects) + break + } + return context + } + + changeTab(tab, group, options = {}) { + super.changeTab(tab, group, options) + if (group === "sheet") { + const main = this.element?.querySelector('[data-group="sheet"][data-tab="main"]') + if (main) main.classList.add("active") + } + } + + static async #onAddSpecialty(event, target) { + const skillKey = target.dataset.skill + const name = game.i18n.localize("ITEMS.new_specialty") + const itemData = { name, type: "specialty" } + if (skillKey) itemData.system = { skill: skillKey } + await this.document.createEmbeddedDocuments("Item", [itemData]) + } +} diff --git a/module/applications/sheets/creature-sheet.mjs b/module/applications/sheets/creature-sheet.mjs new file mode 100644 index 0000000..dcea06d --- /dev/null +++ b/module/applications/sheets/creature-sheet.mjs @@ -0,0 +1,67 @@ +import { VermineBaseActorSheet } from "./base-actor-sheet.mjs" + +export class VermineCreatureSheetV2 extends VermineBaseActorSheet { + + static DEFAULT_OPTIONS = { + classes: ["creature"], + position: { width: 700, height: 650 }, + window: { contentClasses: ["creature-content"] } + } + + static PARTS = { + main: { template: "systems/vermine2047/templates/actor/appv2/creature-main.hbs" }, + tabs: { template: "templates/generic/tab-navigation.hbs" }, + info: { template: "systems/vermine2047/templates/actor/appv2/creature-info.hbs" }, + stats: { template: "systems/vermine2047/templates/actor/appv2/creature-stats.hbs" }, + combat: { template: "systems/vermine2047/templates/actor/appv2/creature-combat.hbs" }, + effects: { template: "systems/vermine2047/templates/actor/appv2/creature-effects.hbs" } + } + + tabGroups = { sheet: "info" } + + #getTabs() { + const tabs = { + info: { id: "info", group: "sheet", icon: "fas fa-info-circle", label: "VERMINE.information" }, + stats: { id: "stats", group: "sheet", icon: "fas fa-chart-bar", label: "VERMINE.stats" }, + combat: { id: "combat", group: "sheet", icon: "fas fa-sword", label: "VERMINE.combat" }, + effects: { id: "effects", group: "sheet", icon: "fas fa-magic", label: "UI.effects.name" } + } + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] === v.id + v.cssClass = v.active ? "active" : "" + } + return tabs + } + + async _prepareContext() { + const context = await super._prepareContext() + context.tabs = this.#getTabs() + return context + } + + async _preparePartContext(partId, context) { + const doc = this.document + switch (partId) { + case "main": break + case "info": + context.tab = context.tabs.info + break + case "stats": + context.tab = context.tabs.stats + context.patternLabel = doc.system.pattern?.value ? CONFIG.VERMINE.creaturePatternLevels[doc.system.pattern.value]?.label : "" + context.sizeLabel = doc.system.size?.value || "" + context.roleLabel = doc.system.role?.value ? CONFIG.VERMINE.creatureRoleLevels[doc.system.role.value]?.label : "" + context.packLabel = doc.system.pack?.value || game.i18n.localize("VERMINE.none") + break + case "combat": + context.tab = context.tabs.combat + break + case "effects": + context.tab = context.tabs.effects + const { prepareActiveEffectCategories } = await import("../../system/effects.mjs") + context.effects = prepareActiveEffectCategories(doc.effects) + break + } + return context + } +} diff --git a/module/applications/sheets/group-sheet.mjs b/module/applications/sheets/group-sheet.mjs new file mode 100644 index 0000000..477d267 --- /dev/null +++ b/module/applications/sheets/group-sheet.mjs @@ -0,0 +1,139 @@ +import { VermineBaseActorSheet } from "./base-actor-sheet.mjs" + +export class VermineGroupSheetV2 extends VermineBaseActorSheet { + + static DEFAULT_OPTIONS = { + classes: ["group"], + position: { width: 700, height: 600 }, + window: { contentClasses: ["group-content"] }, + actions: { + chooseTotem: VermineGroupSheetV2.#onChooseTotem, + chooseActor: VermineGroupSheetV2.#onChooseActor, + deleteMember: VermineGroupSheetV2.#onDeleteMember, + deleteEncounter: VermineGroupSheetV2.#onDeleteEncounter, + deleteObjective: VermineGroupSheetV2.#onDeleteObjective, + addObjective: VermineGroupSheetV2.#onAddObjective + } + } + + static PARTS = { + main: { template: "systems/vermine2047/templates/actor/appv2/group-main.hbs" }, + tabs: { template: "templates/generic/tab-navigation.hbs" }, + info: { template: "systems/vermine2047/templates/actor/appv2/group-info.hbs" }, + gear: { template: "systems/vermine2047/templates/actor/appv2/group-gear.hbs" }, + road: { template: "systems/vermine2047/templates/actor/appv2/group-road.hbs" }, + reserve: { template: "systems/vermine2047/templates/actor/appv2/group-reserve.hbs" } + } + + tabGroups = { sheet: "info" } + + #getTabs() { + const tabs = { + info: { id: "info", group: "sheet", icon: "fas fa-star", label: "VERMINE.information" }, + gear: { id: "gear", group: "sheet", icon: "fas fa-gear", label: "VERMINE.gear" }, + road: { id: "road", group: "sheet", icon: "fas fa-route", label: "VERMINE.road" }, + reserve: { id: "reserve", group: "sheet", icon: "fas fa-users", label: "VERMINE.reserve" } + } + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] === v.id + v.cssClass = v.active ? "active" : "" + } + return tabs + } + + async _prepareContext() { + const context = await super._prepareContext() + context.tabs = this.#getTabs() + // Résoudre les IDs des membres/encounters en données acteur + context.resolvedMembers = {} + if (this.document.system.members?.length > 0) { + for (const memberId of this.document.system.members) { + const a = game.actors.get(memberId) + if (a) context.resolvedMembers[memberId] = { name: a.name, id: a.id } + } + } + context.resolvedEncounters = {} + if (this.document.system.encounters?.length > 0) { + for (const encId of this.document.system.encounters) { + const a = game.actors.get(encId) + if (a) context.resolvedEncounters[encId] = { name: a.name, id: a.id } + } + } + return context + } + + async _preparePartContext(partId, context) { + const doc = this.document + switch (partId) { + case "main": break + case "info": + context.tab = context.tabs.info + context.abilities = doc.itemTypes.ability + context.specialties = doc.itemTypes.specialty + context.backgrounds = doc.itemTypes.background + context.traumas = doc.itemTypes.trauma + context.evolutions = doc.itemTypes.evolution + break + case "gear": + context.tab = context.tabs.gear + context.gear = doc.itemTypes.item + context.weapons = doc.itemTypes.weapon + context.defenses = doc.itemTypes.defense + break + case "road": + context.tab = context.tabs.road + context.vehicles = doc.itemTypes.vehicle + break + case "reserve": + context.tab = context.tabs.reserve + break + } + return context + } + + // Actions : délégation aux applications AppV1 existantes pour TotemPicker/ActorPicker + static async #onChooseTotem(event, target) { + const { TotemPicker } = await import("../../system/applications.mjs") + new TotemPicker(target, this.document).render(true) + } + static async #onChooseActor(event, target) { + const { ActorPicker } = await import("../../system/applications.mjs") + new ActorPicker(target, this.document).render(true) + } + static #onDeleteMember(event, target) { + const li = target.closest("li.actor") + if (!li) return + const actorId = li.dataset.actorId + const idx = this.document.system.members.indexOf(actorId) + if (idx !== -1) { + const members = [...this.document.system.members] + members.splice(idx, 1) + this.document.update({ "system.members": members }) + } + } + static #onDeleteEncounter(event, target) { + const li = target.closest("li.actor") + if (!li) return + const actorId = li.dataset.actorId + const idx = this.document.system.encounters.indexOf(actorId) + if (idx !== -1) { + const encounters = [...this.document.system.encounters] + encounters.splice(idx, 1) + this.document.update({ "system.encounters": encounters }) + } + } + static #onDeleteObjective(event, target) { + const type = target.dataset.type + const index = parseInt(target.dataset.index) + if (isNaN(index)) return + const objectives = foundry.utils.duplicate(this.document.system.objectives || { major: [], minor: [] }) + objectives[type].splice(index, 1) + this.document.update({ "system.objectives": objectives }) + } + static #onAddObjective(event, target) { + const type = target.dataset.type === "major_objective" ? "major" : "minor" + const objectives = foundry.utils.duplicate(this.document.system.objectives || { major: [], minor: [] }) + objectives[type].push("") + this.document.update({ "system.objectives": objectives }) + } +} diff --git a/module/applications/sheets/item-sheets.mjs b/module/applications/sheets/item-sheets.mjs new file mode 100644 index 0000000..2fbad61 --- /dev/null +++ b/module/applications/sheets/item-sheets.mjs @@ -0,0 +1,73 @@ +import { VermineBaseItemSheet } from "./base-item-sheet.mjs" + +// ── Item générique ──────────────────────────────────────────────────── +export class VermineItemSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["item-gear"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-item-sheet.hbs" } } +} + +// ── Arme ────────────────────────────────────────────────────────────── +export class VermineWeaponSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["weapon"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-weapon-sheet.hbs" } } +} + +// ── Défense ─────────────────────────────────────────────────────────── +export class VermineDefenseSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["defense"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-defense-sheet.hbs" } } +} + +// ── Véhicule ────────────────────────────────────────────────────────── +export class VermineVehicleSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["vehicle"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-vehicle-sheet.hbs" } } +} + +// ── Capacité ────────────────────────────────────────────────────────── +export class VermineAbilitySheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["ability"], position: { width: 560 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-ability-sheet.hbs" } } +} + +// ── Spécialité ──────────────────────────────────────────────────────── +export class VermineSpecialtySheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["specialty"], position: { width: 400 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-specialty-sheet.hbs" } } +} + +// ── Historique ──────────────────────────────────────────────────────── +export class VermineBackgroundSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["background"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-background-sheet.hbs" } } +} + +// ── Traumatisme ─────────────────────────────────────────────────────── +export class VermineTraumaSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["trauma"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-trauma-sheet.hbs" } } +} + +// ── Évolution ───────────────────────────────────────────────────────── +export class VermineEvolutionSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["evolution"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-evolution-sheet.hbs" } } +} + +// ── Rumeur ──────────────────────────────────────────────────────────── +export class VermineRumorSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["rumor"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-rumor-sheet.hbs" } } +} + +// ── Cible ───────────────────────────────────────────────────────────── +export class VermineTargetSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["target"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-target-sheet.hbs" } } +} + +// ── Rite ────────────────────────────────────────────────────────────── +export class VermineRiteSheetV2 extends VermineBaseItemSheet { + static DEFAULT_OPTIONS = { classes: ["rite"], position: { width: 520 } } + static PARTS = { main: { template: "systems/vermine2047/templates/item/item-rite-sheet.hbs" } } +} diff --git a/module/applications/sheets/npc-sheet.mjs b/module/applications/sheets/npc-sheet.mjs new file mode 100644 index 0000000..b951d5b --- /dev/null +++ b/module/applications/sheets/npc-sheet.mjs @@ -0,0 +1,76 @@ +import { VermineBaseActorSheet } from "./base-actor-sheet.mjs" + +export class VermineNpcSheetV2 extends VermineBaseActorSheet { + + static DEFAULT_OPTIONS = { + classes: ["npc"], + position: { width: 750, height: 680 }, + window: { contentClasses: ["npc-content"] } + } + + static PARTS = { + main: { template: "systems/vermine2047/templates/actor/appv2/npc-main.hbs" }, + tabs: { template: "templates/generic/tab-navigation.hbs" }, + characteristics: { template: "systems/vermine2047/templates/actor/appv2/npc-characteristics.hbs" }, + skills: { template: "systems/vermine2047/templates/actor/appv2/npc-skills.hbs" }, + threat: { template: "systems/vermine2047/templates/actor/appv2/npc-threat.hbs" }, + combat: { template: "systems/vermine2047/templates/actor/appv2/npc-combat.hbs" }, + notes: { template: "systems/vermine2047/templates/actor/appv2/npc-notes.hbs" } + } + + tabGroups = { sheet: "characteristics" } + + #getTabs() { + const tabs = { + characteristics: { id: "characteristics", group: "sheet", icon: "fas fa-dice", label: "VERMINE.abilities" }, + skills: { id: "skills", group: "sheet", icon: "fas fa-brain", label: "VERMINE.skills" }, + threat: { id: "threat", group: "sheet", icon: "fas fa-exclamation-triangle", label: "ADVERSITY.threat" }, + combat: { id: "combat", group: "sheet", icon: "fas fa-sword", label: "VERMINE.combat" }, + notes: { id: "notes", group: "sheet", icon: "fas fa-sticky-note", label: "IDENTITY.notes" } + } + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] === v.id + v.cssClass = v.active ? "active" : "" + } + return tabs + } + + async _prepareContext() { + const context = await super._prepareContext() + context.tabs = this.#getTabs() + return context + } + + changeTab(tab, group, options = {}) { + super.changeTab(tab, group, options) + if (group === "sheet") { + const main = this.element?.querySelector('[data-group="sheet"][data-tab="main"]') + if (main) main.classList.add("active") + } + } + + async _preparePartContext(partId, context) { + const doc = this.document + switch (partId) { + case "main": break + case "characteristics": + context.tab = context.tabs.characteristics + break + case "skills": + context.tab = context.tabs.skills + break + case "threat": + context.tab = context.tabs.threat + break + case "combat": + context.tab = context.tabs.combat + const { prepareActiveEffectCategories } = await import("../../system/effects.mjs") + context.effects = prepareActiveEffectCategories(doc.effects) + break + case "notes": + context.tab = context.tabs.notes + break + } + return context + } +} diff --git a/module/documents/_module.mjs b/module/documents/_module.mjs new file mode 100644 index 0000000..3a97f86 --- /dev/null +++ b/module/documents/_module.mjs @@ -0,0 +1,7 @@ +/** + * Module de ré-export des classes de documents + * Compatible avec Foundry V2 + */ + +export { default as VermineActor } from "./actor.mjs"; +export { default as VermineItem } from "./item.mjs"; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 55fc690..9e3789d 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -3,44 +3,7 @@ * Extend the base Actor document by defining a custom roll data structure which is ideal for the Simple system. * @extends {Actor} */ -export class VermineActor extends Actor { - - /** @override */ - prepareBaseData() { - // Data modifications in this step occur before processing embedded - // documents or derived data. - - // Initialize wound data to prevent undefined errors with active effects - if (!this.system.minorWound) this.system.minorWound = { value: 0, min: 0, max: 5, threshold: 1 }; - if (!this.system.majorWound) this.system.majorWound = { value: 0, min: 0, max: 4, threshold: 4 }; - if (!this.system.deadlyWound) this.system.deadlyWound = { value: 0, min: 0, max: 2, threshold: 8 }; - - // Initialize combatStatus to prevent errors - if (!this.system.combatStatus) { - this.system.combatStatus = { difficulty: "9", label: "Passif" }; - } - - if (this.type == 'character') { - - } - } - - /** @override */ - prepareEmbeddedDocuments() { - // Check if effects are already being applied in this preparation cycle - // In Foundry V11+, the parent prepareEmbeddedDocuments() calls applyActiveEffects() - // If this is called multiple times in the same cycle, we get the "phase already completed" error - const phase = this.effects?.applicationPhase; - - // If we're already in the middle of applying effects (initial or final phase), - // don't call super as it would try to apply effects again - if (phase === "initial" || phase === "final") { - return; - } - - // If effects haven't been applied yet, proceed normally - super.prepareEmbeddedDocuments(); - } +export default class VermineActor extends Actor { /** * @override diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 73db3c5..54a3b1a 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -2,7 +2,7 @@ * Extend the basic Item with some very simple modifications. * @extends {Item} */ -export class VermineItem extends Item { +export default class VermineItem extends Item { /** * Augment the basic Item data model with additional dynamic data. */ @@ -15,8 +15,7 @@ export class VermineItem extends Item { const actorType = (this.actor !== null) ? this.actor.type : 'character'; const itemType = this.type; - - // Vérifie si une méthode spécifique au type existe// preparedData specifique au type + // Vérifie si une méthode spécifique au type existe if (typeof this[`prepare${itemType.charAt(0).toUpperCase() + itemType.slice(1)}Data`] === 'function') { this[`prepare${itemType.charAt(0).toUpperCase() + itemType.slice(1)}Data`](); } @@ -26,15 +25,14 @@ export class VermineItem extends Item { this.damagedLabel = this.system.damages.state[parseInt(this.system.damages?.value) - 1]; switch (this.damagedLabel) { case "endommagé": - this.damagedIcon = ''; + this.damagedIcon = ''; break; case "défectueux": - this.damagedIcon = ''; + this.damagedIcon = ''; break; case "hors d'usage": - this.damagedIcon = ''; + this.damagedIcon = ''; break; - } } } @@ -44,11 +42,9 @@ export class VermineItem extends Item { const actorType = (this.actor !== null) ? this.actor.type : 'character'; if (this.system.type == "") { - // console.log('je suis une capacité, avec pour sous-type', this.system.type, actorType); this.system.type = actorType; } if (this.system.totem == "" && this.actor !== null && this.actor.system.identity.totem != "") { - // console.log('je suis une capacité, avec pour sous-type', this.system.type, actorType); this.system.totem = this.actor.system.identity.totem; } } @@ -79,16 +75,13 @@ export class VermineItem extends Item { const rollMode = game.settings.get('core', 'rollMode'); const label = `[${item.type}] ${item.name}`; - // If there's no roll data, send a chat message. - let mess = { speaker: speaker, rollMode: rollMode, flavor: label, }; - mess.content = await renderTemplate(`systems/vermine2047/templates/item/chatCards/${this.type}.hbs`, { item: this, message: mess }) ?? null; + mess.content = await foundry.applications.handlebars.renderTemplate(`systems/vermine2047/templates/item/chatCards/${this.type}.hbs`, { item: this, message: mess }) ?? null; ChatMessage.create(mess) } } - diff --git a/module/models/_module.mjs b/module/models/_module.mjs new file mode 100644 index 0000000..5d4023a --- /dev/null +++ b/module/models/_module.mjs @@ -0,0 +1,16 @@ +export { default as VermineCharacterData } from "./character.mjs" +export { default as VermineNpcData } from "./npc.mjs" +export { default as VermineGroupData } from "./group.mjs" +export { default as VermineCreatureData } from "./creature.mjs" +export { default as VermineItemData } from "./item.mjs" +export { default as VermineWeaponData } from "./weapon.mjs" +export { default as VermineDefenseData } from "./defense.mjs" +export { default as VermineVehicleData } from "./vehicle.mjs" +export { default as VermineAbilityData } from "./ability.mjs" +export { default as VermineSpecialtyData } from "./specialty.mjs" +export { default as VermineBackgroundData } from "./background.mjs" +export { default as VermineTraumaData } from "./trauma.mjs" +export { default as VermineEvolutionData } from "./evolution.mjs" +export { default as VermineRumorData } from "./rumor.mjs" +export { default as VermineTargetData } from "./target.mjs" +export { default as VermineRiteData } from "./rite.mjs" diff --git a/module/models/_shared.mjs b/module/models/_shared.mjs new file mode 100644 index 0000000..0174010 --- /dev/null +++ b/module/models/_shared.mjs @@ -0,0 +1,285 @@ +/** + * Schémas partagés pour les DataModels Vermine 2047. + * Fonctions factory retournant des objets SchemaField réutilisables. + */ + +/** + * Retourne un schema pour une blessure (minor/major/deadly) + * @param {number} defaultThreshold + * @param {number} defaultMax + * @returns {Object} + */ +export function woundSchema(defaultThreshold = 1, defaultMax = 5) { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return { + threshold: new fields.NumberField({ ...reqInt, initial: defaultThreshold, min: 0 }), + value: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + min: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + max: new fields.NumberField({ ...reqInt, initial: defaultMax, min: 0 }) + } +} + +/** + * Schema des 3 types de blessures présents sur tous les acteurs. + */ +export function woundsSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + minorWound: new fields.SchemaField(woundSchema(1, 5)), + majorWound: new fields.SchemaField(woundSchema(4, 4)), + deadlyWound: new fields.SchemaField(woundSchema(8, 2)) + }) +} + +/** + * Statut de combat (offensif/actif/passif). + */ +export function combatStatusSchema(defaultDifficulty = "7") { + const fields = foundry.data.fields + return new fields.SchemaField({ + label: new fields.StringField({ required: true, nullable: false, initial: "" }), + difficulty: new fields.StringField({ required: true, nullable: false, initial: defaultDifficulty }) + }) +} + +/** + * Description d'équipement. + */ +export function equipmentSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + description: new fields.HTMLField({ required: true, initial: "", textSearch: true }) + }) +} + +/** + * Attribut avec value/min/max. + * @param {number} defaultVal + * @param {number} defaultMin + * @param {number} defaultMax + */ +export function attributeSchema(defaultVal = 0, defaultMin = 0, defaultMax = 10) { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: defaultVal, min: defaultMin }), + min: new fields.NumberField({ ...reqInt, initial: defaultMin, min: 0 }), + max: new fields.NumberField({ ...reqInt, initial: defaultMax, min: 0 }) + }) +} + +/** + * Caractéristique (capacité) avec catégorie. + * @param {string} category - physical, manual, mental, social + */ +export function abilityField(category) { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: 1, min: 0, max: 5 }), + min: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + max: new fields.NumberField({ ...reqInt, initial: 5, min: 0 }), + category: new fields.StringField({ required: true, nullable: false, initial: category }) + }) +} + +/** + * Les 8 caractéristiques communes à character et npc. + */ +export function abilitiesSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + vigor: abilityField("physical"), + health: abilityField("physical"), + precision: abilityField("manual"), + reflexes: abilityField("manual"), + knowledge: abilityField("mental"), + perception: abilityField("mental"), + will: abilityField("social"), + empathy: abilityField("social") + }) +} + +/** + * Une compétence individuelle. + * @param {string} category + * @param {number} rarity - 0, 1, ou 2 + */ +export function skillField(category, rarity = 0) { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 5 }), + min: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + max: new fields.NumberField({ ...reqInt, initial: 5, min: 0 }), + category: new fields.StringField({ required: true, nullable: false, initial: category }), + rarity: new fields.NumberField({ ...reqInt, initial: rarity, min: 0, max: 2 }) + }) +} + +/** + * Les 30 compétences (character et npc). + */ +export function skillsSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + // man + arts: skillField("man", 1), + civilization: skillField("man", 2), + psychology: skillField("man", 1), + rumors: skillField("man", 0), + healing: skillField("man", 1), + // animal + animalism: skillField("animal", 1), + dissection: skillField("animal", 2), + wildlife: skillField("animal", 1), + repulsion: skillField("animal", 0), + tracks: skillField("animal", 0), + // tool + crafting: skillField("tool", 2), + diy: skillField("tool", 0), + mecanical: skillField("tool", 2), + piloting: skillField("tool", 1), + technology: skillField("tool", 2), + // weapon + firearms: skillField("weapon", 2), + archery: skillField("weapon", 0), + armory: skillField("weapon", 2), + throwing: skillField("weapon", 0), + melee: skillField("weapon", 0), + // survival + alertness: skillField("survival", 0), + atletics: skillField("survival", 0), + food: skillField("survival", 0), + stealth: skillField("survival", 0), + close: skillField("survival", 0), + // world + environment: skillField("world", 1), + flora: skillField("world", 1), + road: skillField("world", 0), + toxics: skillField("world", 2), + ruins: skillField("world", 1) + }) +} + +/** + * Catégories de compétences avec domaine de prédilection. + */ +export function skillCategoriesSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + preferred: new fields.StringField({ required: true, nullable: false, initial: "" }), + man: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "VERMINE.skill_category.man" }), + preferred: new fields.BooleanField({ required: true, initial: false }) + }), + animal: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "VERMINE.skill_category.animal" }), + preferred: new fields.BooleanField({ required: true, initial: false }) + }), + tool: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "VERMINE.skill_category.tool" }), + preferred: new fields.BooleanField({ required: true, initial: false }) + }), + weapon: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "VERMINE.skill_category.weapon" }), + preferred: new fields.BooleanField({ required: true, initial: false }) + }), + survival: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "VERMINE.skill_category.survival" }), + preferred: new fields.BooleanField({ required: true, initial: false }) + }), + world: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "VERMINE.skill_category.world" }), + preferred: new fields.BooleanField({ required: true, initial: false }) + }) + }) +} + +// ── Item shared schemas ────────────────────────────────────────────────── + +const reqInt = { required: true, nullable: false, integer: true } + +/** + * Rareté avec handicap. + */ +export function raritySchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: 3, min: 1, max: 5 }), + handicap: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }) + }) +} + +/** + * Dégâts des items (hors arme). + */ +export function itemDamagesSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 5 }), + min: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + max: new fields.NumberField({ ...reqInt, initial: 5, min: 0 }), + pannes: new fields.ArrayField(new fields.StringField({ required: true, initial: "" }), { + initial: ["mineure", "mineure", "grave", "grave", "critique"] + }), + state: new fields.ArrayField(new fields.StringField({ required: true, initial: "" }), { + initial: ["endommagé", "endommagé", "défectueux", "défectueux", "hors d'usage"] + }), + effect: new fields.ArrayField(new fields.StringField({ required: true, initial: "" }), { + initial: ["bonus annulé", "bonus annulé", "malus 1D", "malus 1D", "inutilisable"] + }) + }) +} + +/** + * Base commune à tous les items (template "base" dans l'ancien template.json). + */ +export function baseItemSchema() { + const fields = foundry.data.fields + return { + description: new fields.HTMLField({ required: true, initial: "", textSearch: true }), + rarity: raritySchema(), + reliability: new fields.NumberField({ ...reqInt, initial: 3, min: 1, max: 5 }), + handicap: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + quantity: new fields.NumberField({ ...reqInt, initial: 1, min: 1 }), + weight: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + traits: new fields.ObjectField({ required: true, initial: {} }), + damages: itemDamagesSchema() + } +} + +/** + * Template "list" pour les items abstraits (ability, background, trauma, evolution, rumor, target). + * Version légère avec seulement description. + */ +export function listItemSchema() { + const fields = foundry.data.fields + return { + description: new fields.HTMLField({ required: true, initial: "", textSearch: true }) + } +} + +/** + * Schéma d'apprentissage pour les abilities. + */ +export function learnSchema() { + const fields = foundry.data.fields + return new fields.SchemaField({ + threshold: new fields.NumberField({ ...reqInt, initial: 5, min: 0 }), + hindrance: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }) + }) +} + +/** + * Niveau générique (value/min/max). + */ +export function levelSchema(defaultVal = 1, defaultMin = 1, defaultMax = 5) { + const fields = foundry.data.fields + return new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: defaultVal, min: defaultMin }), + min: new fields.NumberField({ ...reqInt, initial: defaultMin, min: 0 }), + max: new fields.NumberField({ ...reqInt, initial: defaultMax, min: 0 }) + }) +} diff --git a/module/models/ability.mjs b/module/models/ability.mjs new file mode 100644 index 0000000..39454f8 --- /dev/null +++ b/module/models/ability.mjs @@ -0,0 +1,25 @@ +import { listItemSchema, learnSchema, levelSchema } from "./_shared.mjs" + +/** + * DataModel pour les items de type "ability" (capacités, pouvoirs). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineAbilityData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.ability"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...listItemSchema(), + type: new fields.StringField({ required: true, initial: "" }), + totem: new fields.StringField({ required: true, initial: "" }), + learn: learnSchema(), + level: levelSchema(1, 1, 3), + effects: new fields.ArrayField(new fields.StringField({ required: true, initial: "" }), { + initial: [] + }) + } + } +} diff --git a/module/models/background.mjs b/module/models/background.mjs new file mode 100644 index 0000000..3505e92 --- /dev/null +++ b/module/models/background.mjs @@ -0,0 +1,20 @@ +import { listItemSchema } from "./_shared.mjs" + +/** + * DataModel pour les items de type "background" (historiques, origines). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineBackgroundData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.background"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return { + ...listItemSchema(), + cost: new fields.NumberField({ ...reqInt, initial: 1, min: 0 }) + } + } +} diff --git a/module/models/character.mjs b/module/models/character.mjs new file mode 100644 index 0000000..ddfa705 --- /dev/null +++ b/module/models/character.mjs @@ -0,0 +1,215 @@ +/** + * DataModel pour les acteurs de type "character" (personnage). + * Étend foundry.abstract.TypeDataModel. + */ +import { + woundSchema, + combatStatusSchema, + equipmentSchema, + attributeSchema, + abilitiesSchema, + skillCategoriesSchema, + skillsSchema +} from "./_shared.mjs" + +export default class VermineCharacterData extends foundry.abstract.TypeDataModel { + + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.character"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + + return { + // Blessures (base) + minorWound: new fields.SchemaField(woundSchema(1, 5)), + majorWound: new fields.SchemaField(woundSchema(4, 4)), + deadlyWound: new fields.SchemaField(woundSchema(8, 2)), + + // Statut de combat (base) + combatStatus: combatStatusSchema("7"), + + // Adaptation (totems humain/adapté) + adaptation: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1, min: 0, max: 5 }), + min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), + max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 5, min: 0 }), + totems: new fields.SchemaField({ + human: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1, min: 0, max: 3 }), + min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), + max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 3, min: 0 }) + }), + adapted: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1, min: 0, max: 3 }), + min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), + max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 3, min: 0 }) + }) + }) + }), + + // Identité + identity: new fields.SchemaField({ + height: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + weight: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + totem: new fields.StringField({ required: true, nullable: false, initial: "" }), + age: new fields.StringField({ required: true, nullable: false, initial: "15" }), + ageType: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 2 }), + profile: new fields.StringField({ required: true, nullable: false, initial: "" }), + theme: new fields.StringField({ required: true, nullable: false, initial: "" }), + instincts: new fields.StringField({ required: true, nullable: false, initial: "" }), + prohibits: new fields.StringField({ required: true, nullable: false, initial: "" }), + objectives: new fields.StringField({ required: true, nullable: false, initial: "" }), + relations: new fields.StringField({ required: true, nullable: false, initial: "" }), + biography: new fields.StringField({ required: true, nullable: false, initial: "" }) + }), + + // Équipement + equipment: equipmentSchema(), + + // Attributs (XP, réputation, sang-froid, effort) + attributes: new fields.SchemaField({ + xp: attributeSchema(0, 0, 10), + reputation: attributeSchema(0, 0, 10), + self_control: attributeSchema(0, 0, 5), + effort: attributeSchema(0, 0, 5) + }), + + // Rencontres + encounters: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })), + + // Caractéristiques (8) + abilities: abilitiesSchema(), + + // Catégories de compétences + skill_categories: skillCategoriesSchema(), + + // Compétences (30) + skills: skillsSchema() + } + } + + /** @override */ + prepareDerivedData() { + super.prepareDerivedData() + + // 1. Déterminer la tranche d'âge + this._setAgeType() + + // 2. Calculer les modificateurs de caractéristiques + this._setAbilityModifiers() + + // 3. Calculer les réserves (sang-froid et effort) + this._setSelfControlMax() + this._setEffortMax() + + // 4. Calculer les seuils de blessures + this._setWoundThresholds() + + // 5. Mettre à jour le statut de combat + this._updateCombatStatus() + } + + /** + * Détermine la tranche d'âge (1=jeune, 2=adulte, 3=vieux) + * à partir de l'âge et de la config VERMINE.AgeTypes. + */ + _setAgeType() { + const age = this.identity.age + const ageTypes = CONFIG.VERMINE.AgeTypes + for (const [type, cfg] of Object.entries(ageTypes)) { + if (age >= parseInt(cfg.beginning, 10)) { + this.identity.ageType = parseInt(type, 10) + } + } + } + + /** + * Calcule les modificateurs de caractéristiques (règle d20). + */ + _setAbilityModifiers() { + for (const ability of Object.values(this.abilities)) { + ability.mod = Math.floor((ability.value - 10) / 2) + } + } + + /** + * Calcule le max de sang-froid : + * somme des caractéristiques mentales + sociales + modificateur d'âge. + */ + _setSelfControlMax() { + const abilities = Object.values(this.abilities) + const modFromAge = this._getModFromAgeSelfControl() + const sum = abilities + .filter(a => a.category === "mental" || a.category === "social") + .reduce((acc, a) => acc + a.value, 0) + this.attributes.self_control.max = sum + modFromAge + } + + /** + * Calcule le max d'effort : + * somme des caractéristiques physiques + manuelles + modificateur d'âge. + */ + _setEffortMax() { + const abilities = Object.values(this.abilities) + const modFromAge = this._getModFromAgeEffort() + const sum = abilities + .filter(a => a.category === "physical" || a.category === "manual") + .reduce((acc, a) => acc + a.value, 0) + this.attributes.effort.max = sum + modFromAge + } + + /** + * Calcule les seuils de blessures à partir de la Santé. + */ + _setWoundThresholds() { + const health = this.abilities.health.value + const ageMods = this._getModFromAgeWounds() + + this.minorWound.threshold = health + this.majorWound.threshold = health + 3 + this.deadlyWound.threshold = (health + 7 < 11) ? health + 7 : 10 + + this.minorWound.max = 4 + ageMods.l + this.majorWound.max = 3 + ageMods.h + this.deadlyWound.max = 2 + ageMods.d + } + + /** + * Met à jour le label du statut de combat en fonction de la difficulté. + */ + _updateCombatStatus() { + const difficulty = parseInt(this.combatStatus.difficulty) || 9 + let newLabel = "Passif" + switch (difficulty) { + case 5: newLabel = "Offensif"; break + case 7: newLabel = "Actif"; break + case 9: newLabel = "Passif"; break + } + if (this.combatStatus.label !== newLabel) { + this.combatStatus.label = newLabel + } + } + + // ── Modificateurs liés à l'âge ────────────────────────────────────── + + /** @returns {number} Modificateur de sang-froid selon l'âge. */ + _getModFromAgeSelfControl() { + return this.identity.ageType === 1 ? -1 : 0 + } + + /** @returns {number} Modificateur d'effort selon l'âge. */ + _getModFromAgeEffort() { + if (this.identity.ageType === 1) return -1 + if (this.identity.ageType === 3) return -2 + return 0 + } + + /** @returns {{l: number, h: number, d: number}} Modificateurs de blessures selon l'âge. */ + _getModFromAgeWounds() { + if (this.identity.ageType === 1) return { l: 0, h: 0, d: -1 } + if (this.identity.ageType === 3) return { l: -1, h: -1, d: -1 } + return { l: 0, h: 0, d: 0 } + } +} diff --git a/module/models/creature.mjs b/module/models/creature.mjs new file mode 100644 index 0000000..b05e085 --- /dev/null +++ b/module/models/creature.mjs @@ -0,0 +1,182 @@ +/** + * DataModel pour les acteurs de type "creature" (créature). + * Étend foundry.abstract.TypeDataModel. + */ +import { + woundSchema, + combatStatusSchema, + equipmentSchema, + attributeSchema +} from "./_shared.mjs" + +export default class VermineCreatureData extends foundry.abstract.TypeDataModel { + + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.creature"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + + return { + // Blessures (base) + minorWound: new fields.SchemaField(woundSchema(1, 5)), + majorWound: new fields.SchemaField(woundSchema(4, 4)), + deadlyWound: new fields.SchemaField(woundSchema(8, 2)), + + // Statut de combat (base) + combatStatus: combatStatusSchema(), + + // Identité + identity: new fields.SchemaField({ + profile: new fields.StringField({ required: true, nullable: false, initial: "" }), + origin: new fields.StringField({ required: true, nullable: false, initial: "" }), + theme: new fields.StringField({ required: true, nullable: false, initial: "" }), + notes: new fields.StringField({ required: true, nullable: false, initial: "" }), + biography: new fields.StringField({ required: true, nullable: false, initial: "" }) + }), + + // Compétences (description libre) + skills: new fields.StringField({ required: true, nullable: false, initial: "" }), + + // Modes de jeu actifs + modes: new fields.SchemaField({ + survival: new fields.BooleanField({ required: true, initial: true }), + nightmare: new fields.BooleanField({ required: true, initial: true }), + apocalypse: new fields.BooleanField({ required: true, initial: false }) + }), + + // Niveaux de créature (patron, taille, rôle, meute) + pattern: attributeSchema(1, 1, 4), + size: attributeSchema(1, 1, 3), + role: attributeSchema(1, 1, 4), + pack: attributeSchema(0, 0, 3), + + // Valeurs calculées (dérivées de pattern/size/role/pack) + computed: new fields.SchemaField({ + attack: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + damage: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + vigor: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + reaction: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + reactionBonus: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + pools: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + gear: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 9 }), + gearHindrance: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), + protection: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1 }) + }), + + // Équipement + equipment: equipmentSchema() + } + } + + /** @override */ + prepareDerivedData() { + super.prepareDerivedData() + + // 1. Calculer les valeurs dérivées (attaque, dégâts, vigueur, etc.) + this._calculateCreatureComputedValues() + + // 2. Calculer les seuils de blessures + this._calculateCreatureWoundThresholds() + + // 3. Mettre à jour le statut de combat + this._updateCombatStatus() + } + + /** + * Calcule les valeurs dérivées à partir des niveaux de patron, taille, rôle et meute. + * Utilise les configs CONFIG.VERMINE.creaturePatternLevels, .creatureSizeLevels, + * .creatureRoleLevels, .creaturePackLevels. + * + * Règles : + * - Attaque = pattern.attack + size.attack + pack.attack + role.reaction + * - Dégâts = pattern.damage + size.vigor + pack.damage + * - Vigueur = size.vigor + pack.damage + * - Réaction = role.reaction + role.reaction_bonus + */ + _calculateCreatureComputedValues() { + const patternLevel = this.pattern?.value || 1 + const sizeLevel = this.size?.value || 1 + const roleLevel = this.role?.value || 1 + const packLevel = this.pack?.value || 0 + + const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {} + const sizeConfig = CONFIG.VERMINE.creatureSizeLevels[sizeLevel] || {} + const roleConfig = CONFIG.VERMINE.creatureRoleLevels[roleLevel] || {} + const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {} + + // Attaque : patron + taille + meute + réaction du rôle + this.computed.attack = (patternConfig.attack || 0) + + (sizeConfig.attack || 0) + + (packConfig.attack || 0) + + (roleConfig.reaction || 0) + + // Dégâts : patron + vigueur de taille + meute + this.computed.damage = (patternConfig.damage || 0) + + (sizeConfig.vigor || 0) + + (packConfig.damage || 0) + + // Vigueur : taille + meute + this.computed.vigor = (sizeConfig.vigor || 0) + (packConfig.damage || 0) + + // Réaction : rôle + this.computed.reaction = (roleConfig.reaction || 0) + (roleConfig.reaction_bonus || 0) + this.computed.reactionBonus = roleConfig.reaction_bonus || 0 + + // Réserves + this.computed.pools = roleConfig.pools || 0 + + // Équipement et handicap + this.computed.gear = roleConfig.gear || 9 + this.computed.gearHindrance = roleConfig.gear_hindrance || 0 + + // Protection + this.computed.protection = roleConfig.protection || 1 + } + + /** + * Calcule les seuils de blessures à partir du patron, de la taille et de la meute. + * Les seuils sont la somme des valeurs correspondantes des trois sources. + */ + _calculateCreatureWoundThresholds() { + const patternLevel = this.pattern?.value || 1 + const sizeLevel = this.size?.value || 1 + const packLevel = this.pack?.value || 0 + + const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {} + const sizeConfig = CONFIG.VERMINE.creatureSizeLevels[sizeLevel] || {} + const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {} + + this.minorWound.threshold = (patternConfig.minorWound || 0) + + (sizeConfig.minorWound || 0) + + (packConfig.minorWound || 0) + this.majorWound.threshold = (patternConfig.majorWound || 0) + + (sizeConfig.majorWound || 0) + + (packConfig.majorWound || 0) + this.deadlyWound.threshold = (patternConfig.deadlyWound || 0) + + (sizeConfig.deadlyWound || 0) + + (packConfig.deadlyWound || 0) + + // Max de blessures + this.minorWound.max = Math.min(5, this.minorWound.threshold + 2) + this.majorWound.max = Math.min(4, this.majorWound.threshold + 1) + this.deadlyWound.max = Math.min(2, this.deadlyWound.threshold) + } + + /** + * Met à jour le label du statut de combat en fonction de la difficulté. + */ + _updateCombatStatus() { + const difficulty = parseInt(this.combatStatus.difficulty) || 9 + let newLabel = "Passif" + switch (difficulty) { + case 5: newLabel = "Offensif"; break + case 7: newLabel = "Actif"; break + case 9: newLabel = "Passif"; break + } + if (this.combatStatus.label !== newLabel) { + this.combatStatus.label = newLabel + } + } +} diff --git a/module/models/defense.mjs b/module/models/defense.mjs new file mode 100644 index 0000000..ff42f11 --- /dev/null +++ b/module/models/defense.mjs @@ -0,0 +1,26 @@ +import { baseItemSchema } from "./_shared.mjs" + +/** + * DataModel pour les items de type "defense" (protections, armures, boucliers). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineDefenseData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.defense"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return { + ...baseItemSchema(), + level: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + specificLevel: new fields.SchemaField({ + label: new fields.StringField({ required: true, initial: "" }), + level: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }) + }), + mobility: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + isShield: new fields.BooleanField({ required: true, initial: false }) + } + } +} diff --git a/module/models/evolution.mjs b/module/models/evolution.mjs new file mode 100644 index 0000000..da03058 --- /dev/null +++ b/module/models/evolution.mjs @@ -0,0 +1,19 @@ +import { listItemSchema, levelSchema } from './_shared.mjs' + +/** + * DataModel pour les items de type "evolution" (évolutions du personnage). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineEvolutionData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.evolution"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...listItemSchema(), + level: levelSchema(1, 1, 4) + } + } +} diff --git a/module/models/group.mjs b/module/models/group.mjs new file mode 100644 index 0000000..774b60f --- /dev/null +++ b/module/models/group.mjs @@ -0,0 +1,164 @@ +/** + * DataModel pour les acteurs de type "group" (groupe). + * Étend foundry.abstract.TypeDataModel. + */ +import { + woundSchema, + combatStatusSchema, + equipmentSchema, + attributeSchema, + levelSchema +} from "./_shared.mjs" + +export default class VermineGroupData extends foundry.abstract.TypeDataModel { + + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.group"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + + return { + // Blessures (base) + minorWound: new fields.SchemaField(woundSchema(1, 5)), + majorWound: new fields.SchemaField(woundSchema(4, 4)), + deadlyWound: new fields.SchemaField(woundSchema(8, 2)), + + // Statut de combat (base) + combatStatus: combatStatusSchema(), + + // Identité du groupe + identity: new fields.SchemaField({ + totem: new fields.StringField({ required: true, nullable: false, initial: "" }), + profile: new fields.StringField({ required: true, nullable: false, initial: "" }), + origin: new fields.StringField({ required: true, nullable: false, initial: "" }), + theme: new fields.StringField({ required: true, nullable: false, initial: "" }), + instincts: new fields.StringField({ required: true, nullable: false, initial: "" }), + prohibits: new fields.StringField({ required: true, nullable: false, initial: "" }), + notes: new fields.StringField({ required: true, nullable: false, initial: "" }) + }), + + // Équipement + equipment: equipmentSchema(), + + // Niveau du groupe (1-10) + level: levelSchema(1, 1, 10), + + // Réputation + reputation: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: true, integer: true, initial: 10, min: 2 }), + min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 2, min: 0 }), + max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 10, min: 0 }) + }), + + // Moral + morale: new fields.SchemaField({ + level: new fields.StringField({ required: true, nullable: false, initial: "high" }), + value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 7, min: 0, max: 7 }), + min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), + max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 7, min: 0 }) + }), + + // Réserve + reserve: attributeSchema(0, 0, 10), + + // Objectifs (majeurs et mineurs) + objectives: new fields.SchemaField({ + major: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })), + minor: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })) + }), + + // Capacités de groupe + groupAbilities: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })), + + // Membres (IDs d'acteurs) + members: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })), + + // Rencontres + encounters: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })) + } + } + + /** @override */ + prepareDerivedData() { + super.prepareDerivedData() + + // 1. Initialiser les données de groupe si absentes + this._initGroupData() + + // 2. Calculer la réserve max selon le niveau + this._calculateGroupReserve() + + // 3. Mettre à jour le moral selon la valeur de dés + this._updateGroupMorale() + + // 4. Mettre à jour le statut de combat + this._updateCombatStatus() + } + + /** + * Initialise les champs optionnels du groupe s'ils ne sont pas présents. + */ + _initGroupData() { + if (!this.objectives) { + this.objectives = { major: [], minor: [] } + } + if (!this.groupAbilities) { + this.groupAbilities = [] + } + if (!this.reserve) { + this.reserve = { value: 0, min: 0, max: 10 } + } + } + + /** + * Calcule la réserve max en fonction du niveau du groupe. + * Règle simplifiée : niveau × 2, plafonné à 10. + */ + _calculateGroupReserve() { + const level = this.level?.value || 1 + this.reserve.max = Math.min(10, level * 2) + + if (this.reserve.value > this.reserve.max) { + this.reserve.value = this.reserve.max + } + } + + /** + * Met à jour le niveau de moral en fonction de la valeur de dés. + * Règles : 7D+ = Haut, 6-3D = Normal, 2-1D = Bas, 0D = Crise. + */ + _updateGroupMorale() { + const moraleValue = this.morale?.value || 0 + + // Ne pas écraser un niveau explicitement défini (sauf "high" qui est la valeur par défaut) + if (this.morale.level && this.morale.level !== "high") return + + if (moraleValue >= 7) { + this.morale.level = "high" + } else if (moraleValue >= 3) { + this.morale.level = "normal" + } else if (moraleValue >= 1) { + this.morale.level = "low" + } else { + this.morale.level = "crisis" + } + } + + /** + * Met à jour le label du statut de combat en fonction de la difficulté. + */ + _updateCombatStatus() { + const difficulty = parseInt(this.combatStatus.difficulty) || 9 + let newLabel = "Passif" + switch (difficulty) { + case 5: newLabel = "Offensif"; break + case 7: newLabel = "Actif"; break + case 9: newLabel = "Passif"; break + } + if (this.combatStatus.label !== newLabel) { + this.combatStatus.label = newLabel + } + } +} diff --git a/module/models/item.mjs b/module/models/item.mjs new file mode 100644 index 0000000..3b8ced0 --- /dev/null +++ b/module/models/item.mjs @@ -0,0 +1,23 @@ +/** + * DataModel pour les items de type "item" (équipement générique). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineItemData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.item"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...baseItemSchema(), + needSkill: new fields.SchemaField({ + value: new fields.BooleanField({ required: true, initial: false }), + skill: new fields.StringField({ required: true, initial: "" }) + }) + } + } +} + +// Import partagé — après la déclaration de classe car defineSchema est statique +import { baseItemSchema } from './_shared.mjs' diff --git a/module/models/npc.mjs b/module/models/npc.mjs new file mode 100644 index 0000000..eff05ae --- /dev/null +++ b/module/models/npc.mjs @@ -0,0 +1,162 @@ +/** + * DataModel pour les acteurs de type "npc" (PNJ). + * Étend foundry.abstract.TypeDataModel. + * + * Note : le champ libre de compétences (texte descriptif) est nommé "freeSkills" + * pour éviter le conflit avec le SchemaField "skills" qui contient les 30 compétences. + */ +import { + woundSchema, + combatStatusSchema, + equipmentSchema, + attributeSchema, + abilitiesSchema, + skillCategoriesSchema, + skillsSchema +} from "./_shared.mjs" + +export default class VermineNpcData extends foundry.abstract.TypeDataModel { + + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.npc"] + + /** + * Migration des données avant traitement par le schéma. + * Avant DataModel, template.json définissait "skills" comme un champ texte libre + * pour les PNJ. Le DataModel réserve "skills" pour les 30 compétences individuelles + * (SchemaField) et utilise "freeSkills" pour le texte libre. + * @param {Object} source Données brutes avant validation du schéma + * @returns {Object} Données migrées + */ + static migrateData(source) { + if (typeof source.skills === "string") { + source.freeSkills = source.skills + } + return super.migrateData(source) + } + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + + return { + // Blessures (base) + minorWound: new fields.SchemaField(woundSchema(1, 5)), + majorWound: new fields.SchemaField(woundSchema(4, 4)), + deadlyWound: new fields.SchemaField(woundSchema(8, 2)), + + // Statut de combat (base, difficulté par défaut 9 pour PNJ) + combatStatus: combatStatusSchema("9"), + + // Identité + identity: new fields.SchemaField({ + name: new fields.StringField({ required: true, nullable: false, initial: "" }), + profile: new fields.StringField({ required: true, nullable: false, initial: "" }), + origin: new fields.StringField({ required: true, nullable: false, initial: "" }), + totem: new fields.StringField({ required: true, nullable: false, initial: "" }), + theme: new fields.StringField({ required: true, nullable: false, initial: "" }), + notes: new fields.StringField({ required: true, nullable: false, initial: "" }) + }), + + // Attributs (XP, réputation, sang-froid, effort) + attributes: new fields.SchemaField({ + xp: attributeSchema(0, 0, 10), + reputation: attributeSchema(0, 0, 10), + self_control: attributeSchema(0, 0, 5), + effort: attributeSchema(0, 0, 5) + }), + + // Niveaux PNJ (menace, expérience, rôle) + threat: attributeSchema(1, 1, 4), + experience: attributeSchema(1, 1, 4), + role: attributeSchema(1, 1, 4), + + // Compétences (les 30 compétences individuelles) + skills: skillsSchema(), + + // Description libre des compétences (champ texte PNJ) + freeSkills: new fields.StringField({ required: true, nullable: false, initial: "" }), + + // Catégories de compétences + skill_categories: skillCategoriesSchema(), + + // Caractéristiques (8) + abilities: abilitiesSchema(), + + // Équipement + equipment: equipmentSchema() + } + } + + /** @override */ + prepareDerivedData() { + super.prepareDerivedData() + + // 1. Calculer les seuils de blessures selon le niveau de menace + this._setNpcWoundThresholds() + + // 2. Calculer les réserves selon le niveau de rôle + this._setNpcAttributes() + + // 3. Définir les libellés des caractéristiques + this._setAbilityLabels() + + // 4. Mettre à jour le statut de combat + this._updateCombatStatus() + } + + /** + * Calcule les seuils de blessures à partir du niveau de menace. + * Utilise CONFIG.VERMINE.npcThreatLevels. + */ + _setNpcWoundThresholds() { + const health = this.abilities?.health?.value || 1 + const threatLevel = this.threat?.value || 1 + const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {} + + this.minorWound.threshold = threatConfig.minorWound || health + this.majorWound.threshold = threatConfig.majorWound || (health + 3) + this.deadlyWound.threshold = threatConfig.deadlyWound || (health + 7 < 11 ? health + 7 : 10) + + this.minorWound.max = threatConfig.minorWound || 4 + this.majorWound.max = threatConfig.majorWound || 3 + this.deadlyWound.max = threatConfig.deadlyWound || 2 + } + + /** + * Définit les attributs dérivés (effort, sang-froid) selon le niveau de rôle. + * Utilise CONFIG.VERMINE.npcRoleLevels. + */ + _setNpcAttributes() { + const roleLevel = this.role?.value || 1 + const roleConfig = CONFIG.VERMINE.npcRoleLevels[roleLevel] || {} + + this.attributes.effort.max = roleConfig.pools || 0 + this.attributes.self_control.max = roleConfig.reaction_bonus || 0 + } + + /** + * Définit les libellés localisés des caractéristiques. + */ + _setAbilityLabels() { + for (const [k, v] of Object.entries(this.abilities)) { + v.label = game.i18n.localize(CONFIG.VERMINE.abilities[k]) ?? k + } + } + + /** + * Met à jour le label du statut de combat en fonction de la difficulté. + */ + _updateCombatStatus() { + const difficulty = parseInt(this.combatStatus.difficulty) || 9 + let newLabel = "Passif" + switch (difficulty) { + case 5: newLabel = "Offensif"; break + case 7: newLabel = "Actif"; break + case 9: newLabel = "Passif"; break + } + if (this.combatStatus.label !== newLabel) { + this.combatStatus.label = newLabel + } + } +} diff --git a/module/models/rite.mjs b/module/models/rite.mjs new file mode 100644 index 0000000..1d39b48 --- /dev/null +++ b/module/models/rite.mjs @@ -0,0 +1,22 @@ +import { baseItemSchema } from './_shared.mjs' + +/** + * DataModel pour les items de type "rite" (rites, rituels). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineRiteData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.rite"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...baseItemSchema(), + rituel: new fields.StringField({ required: true, initial: "" }), + transe: new fields.StringField({ required: true, initial: "" }), + ability: new fields.StringField({ required: true, initial: "" }), + effect: new fields.StringField({ required: true, initial: "" }) + } + } +} diff --git a/module/models/rumor.mjs b/module/models/rumor.mjs new file mode 100644 index 0000000..08554dc --- /dev/null +++ b/module/models/rumor.mjs @@ -0,0 +1,18 @@ +import { listItemSchema } from "./_shared.mjs" + +/** + * DataModel pour les items de type "rumor" (rumeurs). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineRumorData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.rumor"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...listItemSchema() + } + } +} diff --git a/module/models/specialty.mjs b/module/models/specialty.mjs new file mode 100644 index 0000000..1b0e205 --- /dev/null +++ b/module/models/specialty.mjs @@ -0,0 +1,18 @@ +/** + * DataModel pour les items de type "specialty" (spécialités de compétence). + * Modèle minimal sans base partagée. + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineSpecialtyData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.specialty"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + name: new fields.StringField({ required: true, nullable: false, initial: "" }), + skill: new fields.StringField({ required: true, nullable: false, initial: "" }) + } + } +} diff --git a/module/models/target.mjs b/module/models/target.mjs new file mode 100644 index 0000000..fb83815 --- /dev/null +++ b/module/models/target.mjs @@ -0,0 +1,19 @@ +import { listItemSchema } from './_shared.mjs' + +/** + * DataModel pour les items de type "target" (cibles, objectifs). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineTargetData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.target"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...listItemSchema(), + level: new fields.StringField({ required: true, initial: "minor" }) + } + } +} diff --git a/module/models/trauma.mjs b/module/models/trauma.mjs new file mode 100644 index 0000000..85df923 --- /dev/null +++ b/module/models/trauma.mjs @@ -0,0 +1,19 @@ +import { listItemSchema } from './_shared.mjs' + +/** + * DataModel pour les items de type "trauma" (traumatismes, séquelles). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineTraumaData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.trauma"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + return { + ...listItemSchema(), + type: new fields.StringField({ required: true, initial: "" }) + } + } +} diff --git a/module/models/vehicle.mjs b/module/models/vehicle.mjs new file mode 100644 index 0000000..58bc219 --- /dev/null +++ b/module/models/vehicle.mjs @@ -0,0 +1,20 @@ +import { baseItemSchema } from "./_shared.mjs" + +/** + * DataModel pour les items de type "vehicle" (véhicules). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineVehicleData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.vehicle"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return { + ...baseItemSchema(), + mobility: new fields.NumberField({ ...reqInt, initial: 3, min: 0 }) + } + } +} diff --git a/module/models/weapon.mjs b/module/models/weapon.mjs new file mode 100644 index 0000000..73922f1 --- /dev/null +++ b/module/models/weapon.mjs @@ -0,0 +1,27 @@ +import { baseItemSchema } from "./_shared.mjs" + +/** + * DataModel pour les items de type "weapon" (armes). + * @augments {foundry.abstract.TypeDataModel} + */ +export default class VermineWeaponData extends foundry.abstract.TypeDataModel { + /** @override */ + static LOCALIZATION_PREFIXES = ["VERMINE.item.weapon"] + + /** @override */ + static defineSchema() { + const fields = foundry.data.fields + const reqInt = { required: true, nullable: false, integer: true } + return { + ...baseItemSchema(), + min_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + max_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + damage: new fields.SchemaField({ + value: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }), + type: new fields.StringField({ required: true, initial: "" }), + addVigor: new fields.BooleanField({ required: true, initial: false }) + }), + ammo: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }) + } + } +} diff --git a/module/sheets/actor-sheet.mjs b/module/sheets/actor-sheet.mjs deleted file mode 100644 index 0673be3..0000000 --- a/module/sheets/actor-sheet.mjs +++ /dev/null @@ -1,157 +0,0 @@ -import { onManageActiveEffect, prepareActiveEffectCategories } from "../system/effects.mjs"; -import { preloadHandlebarsTemplates } from "../system/handlebars-manager.mjs"; - -/** - * Extend the basic ActorSheet with some very simple modifications - * @extends {ActorSheet} - */ -export class VermineActorSheet extends ActorSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - /*classes: ["vermine2047", "sheet", "actor"], - template: "systems/vermine2047/templates/actor/actor-sheet.hbs", - height: 800, - width: 690, - resizable: false, - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "features" }]*/ - }); - } - - /** @override */ - get template() { - return `systems/vermine2047/templates/actor/actor-${this.actor.type}-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve the data structure from the base sheet. You can inspect or log - // the context variable to see the structure, but some key properties for - // sheets are the actor object, the data object, whether or not it's - // editable, the items array, and the effects array. - const context = super.getData(); - - // Use a safe clone of the actor data for further operations. - const actorData = this.actor.toObject(false); - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - - //add system config for convenience use - context.config = CONFIG.VERMINE; - - // Add roll data for TinyMCE editors. - context.rollData = context.actor.getRollData(); - - // Prepare active effects - context.effects = prepareActiveEffectCategories(this.actor.effects); - - - return context; - } - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - // Render the item sheet for viewing/editing prior to the editable check. - html.find('.item-edit').click(ev => { - const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.items.get(li.data("itemId")); - item.sheet.render(true); - }); - - // ------------------------------------------------------------- - // Everything below here is only needed if the sheet is editable - if (!this.isEditable) return; - - // Add Inventory Item - html.find('.item-create').click(this._onItemCreate.bind(this)); - - // Delete Inventory Item - html.find('.item-delete').click(ev => { - const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.items.get(li.data("itemId")); - item.delete(); - li.slideUp(200, () => this.render(false)); - }); - html.find(".item-roll").click(ev => { - this._onRollItem(ev) - }) - // Active Effect management - html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.actor)); - - - // Drag events for macros. - if (this.actor.isOwner) { - let handler = ev => this._onDragStart(ev); - html.find('li.item').each((i, li) => { - if (li.classList.contains("inventory-header")) return; - li.setAttribute("draggable", true); - li.addEventListener("dragstart", handler, false); - }); - } - - //click on wound radio - html.find('.hexa [type="radio"]').click(ev => { - ev.preventDefault(); - ev.stopPropagation(); - - return this._onClickRadioHexa(ev) - }) - - } - - async _onRollItem(ev) { - const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.items.get(li.data("itemId")); - item.roll(); - } - _onClickRadioHexa(ev) { - let input = ev.currentTarget; - console.log(input.value, input.name); - let update = {}; - update[input.name] = 0 - let propTree = input.name.split('.') - let current = this.actor; - for (let prop of propTree) { - current = current[prop] - } - if (current != input.value) { - update[input.name] = parseInt(input.value) - - } else { - update[input.name] = parseInt(input.value) - 1; - } - this.actor.update(update) - - - - } - async _onItemCreate(event) { - event.preventDefault(); - const header = event.currentTarget; - // Get the type of item to create. - const type = header.dataset.type; - // Grab any data associated with this control. - const data = foundry.utils.duplicate(header.dataset); - // Initialize a default name. - // const name = `New ${type.capitalize()}`; - const name = game.i18n.localize('ITEMS.new_' + type); - - // Prepare the item object. - const itemData = { - name: name, - type: type, - system: data - }; - // Remove the type from the dataset since it's in the itemData.type prop. - delete itemData.system["type"]; - - // Finally, create the item! - return await Item.create(itemData, { parent: this.actor }); - } -} diff --git a/module/sheets/character-sheet.mjs b/module/sheets/character-sheet.mjs deleted file mode 100644 index 766cd36..0000000 --- a/module/sheets/character-sheet.mjs +++ /dev/null @@ -1,228 +0,0 @@ -import { onManageActiveEffect, prepareActiveEffectCategories } from "../system/effects.mjs"; -import { VermineActorSheet } from "./actor-sheet.mjs"; -import RollDialog from "../system/dialogs/rollDialog.mjs"; -import { TotemPicker } from "../system/applications.mjs"; - -/** - * Extend the basic ActorSheet with some very simple modifications - * @extends {VermineActorSheet} - */ -export class VermineCharacterSheet extends VermineActorSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["vermine2047", "sheet", "character", "actor"], - template: "systems/vermine2047/templates/actor/actor-sheet.hbs", - width: "fit-content", - height: "fit-content", - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "features" }] - }); - } - - /** @override */ - get template() { - return `systems/vermine2047/templates/actor/actor-character-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve the data structure from the base sheet. You can inspect or log - // the context variable to see the structure, but some key properties for - // sheets are the actor object, the data object, whether or not it's - // editable, the items array, and the effects array. - const context = super.getData(); - - // Use a safe clone of the actor data for further operations. - const actorData = this.actor.toObject(false); - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - context.config = CONFIG.VERMINE; - - // Prepare character data and items. - if (actorData.type == 'character') { - this._prepareItems(context); - this._prepareCharacterData(context); - } - - // Prepare NPC data and items. - if (actorData.type == 'npc') { - this._prepareItems(context); - } - // Add roll data for TinyMCE editors. - context.rollData = context.actor.getRollData(); - - - - // Prepare active effects - context.effects = prepareActiveEffectCategories(this.actor.effects); - - return context; - } - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareCharacterData(context) { - // Handle ability scores. - for (let [k, v] of Object.entries(context.system.abilities)) { - v.label = game.i18n.localize(context.system.abilities[k].label) ?? k; - } - for (let [k, v] of Object.entries(context.system.skills)) { - if (v.value >= 2) { - let spe = this.actor.items.filter(it => it.type == "specialty").filter(spec => spec.system.skill == k); - v.specialties = spe - } - - } - - } - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareItems(context) { - context.gear = this.actor.itemTypes['item']; - context.weapons = this.actor.itemTypes['weapon']; - context.defenses = this.actor.itemTypes['defense']; - context.traits = this.actor.itemTypes['trait']; - context.specialties = this.actor.itemTypes['specialty']; - context.abilities = this.actor.itemTypes['ability']; - context.evolutions = this.actor.itemTypes['evolution']; - context.traumas = this.actor.itemTypes['trauma']; - context.backgrounds = this.actor.itemTypes['background']; - context.rumors = this.actor.itemTypes['rumor']; - } - - /* -------------------------------------------- */ - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - //desactiver les inputs si mode jeu - if (!this.actor.flags.world?.editMode) { - this.disableInputs(html) - } - // Choose Totem - html.find('.chooseTotem').click(this._onTotemButton.bind(this)); - //activer lest jets - html.find('.ability .rollable').click(this._onRoll.bind(this)); - //gérer les dés totems - html.find('[data-totem-name]').click(this._onClickTotemDice.bind(this)); - //creation de specialités - html.find('i.add-specialty').click(this.addSpecialty.bind(this)) - - - } - - //mode jeu/edit en mode jeu on bloque les selects et input - disableInputs(html) { - for (let input of html.find('input')) { - //préserver le toggle mode jeu/ mode edit - if (input.name != "flags.world.editMode") { - input.setAttribute('disabled', true) - } - } - for (let select of html.find('select')) { - select.setAttribute('disabled', true) - } - } - async addSpecialty(ev) { - let skillName = ev.target.closest('.ability').querySelector('label').dataset.label; - let itemData = { - name: `spécialité, ${skillName}`, - type: 'specialty', - system: { - skill: skillName - } - } - let spec = await this.actor.createEmbeddedDocuments("Item", [itemData]); - spec[0].sheet.render(true) - } - async _onClickTotemDice(ev) { - let el = ev.currentTarget; - let totem = el.dataset.totemName; - let value = parseInt(el.dataset.totemValue) || 0; - - let oldValue = this.actor.system.adaptation.totems[totem].value; - if (value === oldValue) { value-- }; - let updates = {}; - updates[`system.adaptation.totems.${totem}.value`] = value; - //verifier le max des dés totems - let sum = value; - switch (totem) { - case "human": - sum += this.actor.system.adaptation.totems.adapted.value; - break; - case "adapted": - sum += this.actor.system.adaptation.totems.human.value; - break; - } - if (sum > 5) { return ui.notifications.warn("pas plus de 5 dés totems") } - await this.actor.update(updates); - } - /** - * Handle clickable rolls. - * @param {Event} event The originating click event - * @private - */ - async _onRoll(event) { - event.preventDefault(); - const element = event.currentTarget; - const dataset = element.dataset; - console.log("Ceci est un jet d'un personnage joueur", this.actor); - // Handle item rolls. - if (dataset.rollType) { - if (dataset.rollType == 'item') { - const itemId = element.closest('.item').dataset.itemId; - const item = this.actor.items.get(itemId); - if (item) return item.roll(); - } - } - - // Handle rolls that supply the formula directly. - if (dataset.label) { - dataset.rollType = dataset.type; - - let data = { - actorId: this.object.id, - rollType: dataset.rollType, - labelKey: dataset.label, - - label: game.i18n.localize(dataset.label) - }; - - let dial = await RollDialog.create(data); - console.log("from sheet", data, this) - return dial.render(true) - - } - } - - /** - * Handle totem pick - * @param {Event} event The originating click event - * @private - */ - _onTotemButton(event) { - event.preventDefault(); - const el = event.currentTarget; - // const dataset = el.dataset; - - const totemPicker = new TotemPicker(el, this.actor); - totemPicker.render(true); - } - -} diff --git a/module/sheets/creature-sheet.mjs b/module/sheets/creature-sheet.mjs deleted file mode 100644 index 430f194..0000000 --- a/module/sheets/creature-sheet.mjs +++ /dev/null @@ -1,167 +0,0 @@ -import { onManageActiveEffect, prepareActiveEffectCategories } from "../system/effects.mjs"; -import { VermineActorSheet } from "./actor-sheet.mjs"; - -/** - * Extend the basic ActorSheet with some very simple modifications - * @extends {ActorSheet} - */ -export class VermineCreatureSheet extends VermineActorSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["vermine2047", "sheet", "actor", "creature"], - template: "systems/vermine2047/templates/actor/actor-sheet.hbs", - width: 650, - height: 600, - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] - }); - } - - /** @override */ - get template() { - return `systems/vermine2047/templates/actor/actor-${this.actor.type}-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve the data structure from the base sheet. You can inspect or log - // the context variable to see the structure, but some key properties for - // sheets are the actor object, the data object, whether or not it's - // editable, the items array, and the effects array. - const context = super.getData(); - - // Use a safe clone of the actor data for further operations. - const actorData = this.actor.toObject(false); - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - context.config = CONFIG.VERMINE; - - // Prepare character data and items. - if (actorData.type == 'character') { - this._prepareItems(context); - this._prepareCharacterData(context); - } - - // Prepare NPC data and items. - if (actorData.type == 'npc') { - this._prepareItems(context); - } - - // Prepare Creature data and items. - if (actorData.type == 'creature') { - this._prepareItems(context); - this._prepareCreatureData(context); - } - - // Add roll data for TinyMCE editors. - context.rollData = context.actor.getRollData(); - - // Prepare active effects - context.effects = prepareActiveEffectCategories(this.actor.effects); - - return context; - } - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareItems(context) { - context.gear = this.actor.itemTypes['item']; - context.traits = this.actor.itemTypes['trait']; - } - - /** - * Prepare Character type specific data. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareCharacterData(context) { - // Handle ability scores. - for (let [k, v] of Object.entries(context.system.abilities)) { - v.label = game.i18n.localize(context.system.abilities[k].label) ?? k; - } - } - - /** - * Prepare Creature type specific data for the sheet. - * - * @param {Object} context The context data to prepare. - * @return {undefined} - */ - _prepareCreatureData(context) { - if (this.actor.type !== 'creature') return; - - // Add computed values to context - context.computed = context.system.computed || {}; - - // Get labels for pattern, size, role - const patternLevel = context.system.pattern?.value || 1; - const sizeLevel = context.system.size?.value || 1; - const roleLevel = context.system.role?.value || 1; - const packLevel = context.system.pack?.value || 0; - - // Add pattern label - const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel]; - if (patternConfig) { - context.patternLabel = game.i18n.localize(patternConfig.label); - } - - // Add size label (using numeric for now) - context.sizeLabel = sizeLevel; - - // Add role label - const roleConfig = CONFIG.VERMINE.creatureRoleLevels[roleLevel]; - if (roleConfig) { - context.roleLabel = game.i18n.localize(roleConfig.label); - } - - // Add pack label - context.packLabel = packLevel > 0 ? packLevel : game.i18n.localize('VERMINE.none'); - } - - /* -------------------------------------------- */ - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - - html.find('.item-create').click(this._onItemCreate.bind(this)); - } - - async _onItemCreate(event) { - event.preventDefault(); - const header = event.currentTarget; - // Get the type of item to create. - const type = header.dataset.type; - // Grab any data associated with this control. - const data = foundry.utils.duplicate(header.dataset); - // Initialize a default name. - // const name = `New ${type.capitalize()}`; - const name = game.i18n.localize('ITEMS.new_' + type); - - console.log('onItemCreate child', data.type, this.actor.type); - // Prepare the item object. - const itemData = { - name: name, - type: type, - system: data - }; - // Remove the type from the dataset since it's in the itemData.type prop. - delete itemData.system["type"]; - - // Finally, create the item! - return await Item.create(itemData, { parent: this.actor }); - } - -} diff --git a/module/sheets/creature-sheet.mjs.backup b/module/sheets/creature-sheet.mjs.backup deleted file mode 100644 index 204f41d..0000000 --- a/module/sheets/creature-sheet.mjs.backup +++ /dev/null @@ -1,167 +0,0 @@ -import { onManageActiveEffect, prepareActiveEffectCategories } from "../system/effects.mjs"; -import { VermineActorSheet } from "./actor-sheet.mjs"; - -/** - * Extend the basic ActorSheet with some very simple modifications - * @extends {ActorSheet} - */ -export class VermineCreatureSheet extends VermineActorSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["vermine2047", "sheet", "actor", "creature"], - template: "systems/vermine2047/templates/actor/actor-sheet.hbs", - width: 650, - height: 600, - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] - }); - } - - /** @override */ - get template() { - return `systems/vermine2047/templates/actor/actor-${this.actor.type}-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve the data structure from the base sheet. You can inspect or log - // the context variable to see the structure, but some key properties for - // sheets are the actor object, the data object, whether or not it's - // editable, the items array, and the effects array. - const context = super.getData(); - - // Use a safe clone of the actor data for further operations. - const actorData = this.actor.toObject(false); - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - context.config = CONFIG.VERMINE; - - // Prepare character data and items. - if (actorData.type == 'character') { - this._prepareItems(context); - this._prepareCharacterData(context); - } - - // Prepare NPC data and items. - if (actorData.type == 'npc') { - this._prepareItems(context); - } - - // Prepare Creature data and items. - if (actorData.type == 'creature') { - this._prepareItems(context); - this._prepareCreatureData(context); - } - - // Add roll data for TinyMCE editors. - context.rollData = context.actor.getRollData(); - - // Prepare active effects - context.effects = prepareActiveEffectCategories(this.actor.effects); - - return context; - - - /** - * Prepare Creature type specific data for the sheet. - * - * @param {Object} context The context data to prepare. - * @return {undefined} - */ - _prepareCreatureData(context) { - if (this.actor.type !== 'creature') return; - - // Add computed values to context - context.computed = context.system.computed || {}; - - // Get labels for pattern, size, role - const patternLevel = context.system.pattern?.value || 1; - const sizeLevel = context.system.size?.value || 1; - const roleLevel = context.system.role?.value || 1; - const packLevel = context.system.pack?.value || 0; - - // Add pattern label - const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel]; - if (patternConfig) { - context.patternLabel = game.i18n.localize(patternConfig.label); - } - - // Add size label (using numeric for now) - context.sizeLabel = sizeLevel; - - // Add role label - const roleConfig = CONFIG.VERMINE.creatureRoleLevels[roleLevel]; - if (roleConfig) { - context.roleLabel = game.i18n.localize(roleConfig.label); - } - - // Add pack label - context.packLabel = packLevel > 0 ? packLevel : game.i18n.localize('VERMINE.none'); - }} - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareCharacterData(context) { - // Handle ability scores. - for (let [k, v] of Object.entries(context.system.abilities)) { - v.label = game.i18n.localize(context.system.abilities[k].label) ?? k; - } - } - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareItems(context) { - context.gear = this.actor.itemTypes['item']; - context.traits = this.actor.itemTypes['trait']; - } - - /* -------------------------------------------- */ - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - - html.find('.item-create').click(this._onItemCreate.bind(this)); - } - - async _onItemCreate(event) { - event.preventDefault(); - const header = event.currentTarget; - // Get the type of item to create. - const type = header.dataset.type; - // Grab any data associated with this control. - const data = duplicate(header.dataset); - // Initialize a default name. - // const name = `New ${type.capitalize()}`; - const name = game.i18n.localize('ITEMS.new_' + type); - - console.log('onItemCreate child', data.type, this.actor.type); - // Prepare the item object. - const itemData = { - name: name, - type: type, - system: data - }; - // Remove the type from the dataset since it's in the itemData.type prop. - delete itemData.system["type"]; - - // Finally, create the item! - return await Item.create(itemData, { parent: this.actor }); - } - -} diff --git a/module/sheets/item-sheet.mjs b/module/sheets/item-sheet.mjs deleted file mode 100644 index 09af423..0000000 --- a/module/sheets/item-sheet.mjs +++ /dev/null @@ -1,80 +0,0 @@ -import { TraitSelector } from "../system/applications.mjs"; - -/** - * Extend the basic ItemSheet with some very simple modifications - * @extends {ItemSheet} - */ -export class VermineItemSheet extends ItemSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["vermine2047", "sheet", "item"], - width: 450, - height: "max-content", - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] - }); - } - - /** @override */ - get template() { - const path = "systems/vermine2047/templates/item"; - return `${path}/item-${this.item.type}-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve base data structure. - const context = super.getData(); - - // Use a safe clone of the item data for further operations. - const itemData = context.item; - - // Retrieve the roll data for TinyMCE editors. - context.rollData = {}; - let actor = this.object?.parent ?? null; - if (actor) { - context.rollData = actor.getRollData(); - } - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = itemData.system; - context.flags = itemData.flags; - context.config = CONFIG.VERMINE; - - return context; - } - - /* -------------------------------------------- */ - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - // Everything below here is only needed if the sheet is editable - if (!this.isEditable) return; - //click on wound radio - html.find('.damages-row [type="radio"]').click(ev => { - this._onClickDamage(ev) - }) - - html.find('.traits-selector').click(ev => { - this.openTraitSelector(ev) - - }) - } - async _onClickDamage(ev) { - if (!ev.currentTarget.checked) { return } - let prop = ev.currentTarget.name; - let update = {}; - update[prop] = ev.currentTarget.value - 1 - - this.item.update(update) - } - - async openTraitSelector(ev) { - let selector = new TraitSelector(this.item); - selector.render(true) - } -} diff --git a/module/sheets/npc-group.mjs b/module/sheets/npc-group.mjs deleted file mode 100644 index c93ecd3..0000000 --- a/module/sheets/npc-group.mjs +++ /dev/null @@ -1,275 +0,0 @@ -import { onManageActiveEffect, prepareActiveEffectCategories } from "../system/effects.mjs"; -import { VermineActorSheet } from "./actor-sheet.mjs"; -import { TotemPicker, ActorPicker } from "../system/applications.mjs"; - -/** - * Extend the basic ActorSheet with some very simple modifications - * @extends {VermineActorSheet} - */ -export class VermineGroupSheet extends VermineActorSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["vermine2047", "sheet", "actor", "group"], - template: "systems/vermine2047/templates/actor/actor-sheet.hbs", - width: 700, - height: 600, - tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }] - }); - } - - /** @override */ - get template() { - return `systems/vermine2047/templates/actor/actor-${this.actor.type}-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve the data structure from the base sheet. You can inspect or log - // the context variable to see the structure, but some key properties for - // sheets are the actor object, the data object, whether or not it's - // editable, the items array, and the effects array. - const context = super.getData(); - - // Use a safe clone of the actor data for further operations. - const actorData = this.actor.toObject(false); - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - context.config = CONFIG.VERMINE; - - // Prepare character data and items. - if (actorData.type == 'character') { - this._prepareItems(context); - this._prepareCharacterData(context); - } - - // Prepare NPC data and items. - if (actorData.type == 'npc') { - this._prepareItems(context); - } - - if (actorData.type == 'group') { - this._prepareItems(context); - this._prepareGroupData(context); - } - - // Add roll data for TinyMCE editors. - context.rollData = this.actor.getRollData(); - - // Prepare active effects - context.effects = prepareActiveEffectCategories(this.actor.effects); - - return context; - } - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareCharacterData(context) { - // Handle ability scores. - for (let [k, v] of Object.entries(context.system.abilities)) { - v.label = game.i18n.localize(context.system.abilities[k].label) ?? k; - } - } - - /** - * Prepare Group type specific data. - * Resolves member and encounter actor IDs to actual actor data. - * - * @param {Object} context The context data to prepare. - * @return {undefined} - */ - _prepareGroupData(context) { - if (this.actor.type !== 'group') return; - - // Resolve member IDs to actor data - context.resolvedMembers = {}; - if (context.system.members && context.system.members.length > 0) { - context.system.members.forEach(memberId => { - const actor = game.actors.get(memberId); - if (actor) { - context.resolvedMembers[memberId] = { - name: actor.name, - id: actor.id - }; - } - }); - } - - // Resolve encounter IDs to actor data - context.resolvedEncounters = {}; - if (context.system.encounters && context.system.encounters.length > 0) { - context.system.encounters.forEach(encounterId => { - const actor = game.actors.get(encounterId); - if (actor) { - context.resolvedEncounters[encounterId] = { - name: actor.name, - id: actor.id - }; - } - }); - } - - // Set morale level based on dice value (rules: p. 68-69) - this._updateMoraleLevel(context); - } - - /** - * Update morale level based on dice value. - * Rules: 7D+ = Haut, 6-3D = Normal, 2D- = Bas, 0D = Crise - * - * @param {Object} context The context data. - * @return {undefined} - */ - _updateMoraleLevel(context) { - const moraleValue = context.system.morale.value || 0; - - // If level is already set, keep it - if (context.system.morale.level) return; - - // Determine morale level based on dice value - if (moraleValue >= 7) { - context.system.morale.level = "high"; - } else if (moraleValue >= 3) { - context.system.morale.level = "normal"; - } else if (moraleValue >= 1) { - context.system.morale.level = "low"; - } else { - context.system.morale.level = "crisis"; - } - } - - /** - * Organize and classify Items for Character sheets. - * - * @param {Object} actorData The actor to prepare. - * - * @return {undefined} - */ - _prepareItems(context) { - context.specialties = this.actor.itemTypes['specialty']; - context.backgrounds = this.actor.itemTypes['background']; - context.evolutions = this.actor.itemTypes['evolution']; - context.traumas = this.actor.itemTypes['trauma']; - - context.gear = this.actor.itemTypes['item']; - context.weapons = this.actor.itemTypes['weapon']; - context.defenses = this.actor.itemTypes['defense']; - context.vehicles = this.actor.itemTypes['vehicle']; - - context.totem_abilities = this.actor.itemTypes['ability'].filter(i => i.system.type === 'totem'); - context.abilities = this.actor.itemTypes['ability'].filter(i => i.system.type !== 'totem'); - - context.members = this.actor.system.members; - context.encounters = this.actor.system.encounters; - - - - } - - /* -------------------------------------------- */ - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - - // Choose Totem - html.find('.chooseTotem').click(this._onTotemButton.bind(this)); - - // Choose Members / Encounters - html.find('.chooseActor').click(this._onRoadButton.bind(this)); - html.find('.member-delete').click(ev => { - const li = $(ev.currentTarget).parents("li.actor"); - const actorId = li.data("actor-id"); - const actorIdIndex = this.actor.system.members.indexOf(actorId); - if (actorIdIndex !== -1) { - this.actor.system.members.splice(actorIdIndex, 1); - } - this.actor.update({ "system.members": this.actor.system.members }); - this.render(true); - }); - - html.find('.encounter-delete').click(ev => { - const li = $(ev.currentTarget).parents("li.actor"); - const actorId = li.data("actor-id"); - const actorIdIndex = this.actor.system.encounters.indexOf(actorId); - if (actorIdIndex !== -1) { - this.actor.system.encounters.splice(actorIdIndex, 1); - } - this.actor.update({ "system.encounters": this.actor.system.encounters }); - this.render(true); - }); - - // Handle objective deletion - html.find('.objective-delete').click(ev => { - ev.preventDefault(); - const btn = $(ev.currentTarget); - const type = btn.data("type"); // 'major' or 'minor' - const index = parseInt(btn.data("index")); - - if (!isNaN(index)) { - const objectives = foundry.utils.duplicate(this.actor.system.objectives || { major: [], minor: [] }); - objectives[type].splice(index, 1); - this.actor.update({ "system.objectives": objectives }); - } - }); - - // Handle adding new objectives - html.find('.item-create[data-type="major_objective"], .item-create[data-type="minor_objective"]').click(ev => { - ev.preventDefault(); - const btn = $(ev.currentTarget); - const type = btn.data("type") === "major_objective" ? "major" : "minor"; - - const objectives = foundry.utils.duplicate(this.actor.system.objectives || { major: [], minor: [] }); - objectives[type].push(""); - this.actor.update({ "system.objectives": objectives }); - }); - - // Handle morale level change - html.find('select[name="system.morale.level"]').change(ev => { - const select = $(ev.currentTarget); - const level = select.val(); - this.actor.update({ "system.morale.level": level }); - }); - - } - - - /** - * Handle totem pick - * @param {Event} event The originating click event - * @private - */ - _onTotemButton(event) { - event.preventDefault(); - const el = event.currentTarget; - // const dataset = el.dataset; - - const totemPicker = new TotemPicker(el, this.actor); - totemPicker.render(true); - } - - /** -* Handle actor pick -* @param {Event} event The originating click event -* @private -*/ - _onRoadButton(event) { - event.preventDefault(); - const el = event.currentTarget; - // const dataset = el.dataset; - - const actorPicker = new ActorPicker(el, this.actor); - actorPicker.render(true); - } - -} diff --git a/module/sheets/npc-sheet.mjs b/module/sheets/npc-sheet.mjs deleted file mode 100644 index 76297fa..0000000 --- a/module/sheets/npc-sheet.mjs +++ /dev/null @@ -1,154 +0,0 @@ -import { onManageActiveEffect, prepareActiveEffectCategories } from "../system/effects.mjs"; -import { VermineActorSheet } from "./actor-sheet.mjs"; -import { TotemPicker } from "../system/applications.mjs"; - -/** - * Extend the basic ActorSheet for NPC type - * @extends {VermineActorSheet} - */ -export class VermineNpcSheet extends VermineActorSheet { - - /** @override */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["vermine2047", "sheet", "actor", "npc"], - template: "systems/vermine2047/templates/actor/actor-sheet.hbs", - width: 600, - height: 700, - tabs: [ - { navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "characteristics" } - ] - }); - } - - /** @override */ - get template() { - return `systems/vermine2047/templates/actor/actor-${this.actor.type}-sheet.hbs`; - } - - /* -------------------------------------------- */ - - /** @override */ - getData() { - // Retrieve the data structure from the base sheet. - const context = super.getData(); - - // Use a safe clone of the actor data for further operations. - const actorData = this.actor.toObject(false); - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - context.config = CONFIG.VERMINE; - - // Prepare items for all actor types - this._prepareItems(context); - - // Prepare NPC-specific data - if (actorData.type === 'npc') { - this._prepareNpcData(context); - } - - // Add roll data for TinyMCE editors - context.rollData = this.actor.getRollData(); - - // Prepare active effects - context.effects = prepareActiveEffectCategories(this.actor.effects); - - return context; - } - - /** - * Prepare NPC specific data - */ - _prepareNpcData(context) { - // Calculate derived values from threat, experience, and role - const threat = CONFIG.VERMINE.npcThreatLevels[context.system.threat.value]; - const experience = CONFIG.VERMINE.npcExperienceLevels[context.system.experience.value]; - const role = CONFIG.VERMINE.npcRoleLevels[context.system.role.value]; - - // Add calculated values to context for easier access - context.threatData = threat; - context.experienceData = experience; - context.roleData = role; - - // Set wound thresholds based on threat level - if (threat) { - context.system.minorWound.threshold = threat.minorWound || context.system.minorWound.threshold; - context.system.majorWound.threshold = threat.majorWound || context.system.majorWound.threshold; - context.system.deadlyWound.threshold = threat.deadlyWound || context.system.deadlyWound.threshold; - - // Set max wounds - context.system.minorWound.max = threat.minorWound || context.system.minorWound.max; - context.system.majorWound.max = threat.majorWound || context.system.majorWound.max; - context.system.deadlyWound.max = threat.deadlyWound || context.system.deadlyWound.max; - } - - // Set reserve max values based on role - if (role) { - context.system.attributes.effort.max = role.pools || context.system.attributes.effort.max; - context.system.attributes.self_control.max = role.reaction_bonus || context.system.attributes.self_control.max; - } - - // Prepare abilities with labels - for (let [k, v] of Object.entries(context.system.abilities)) { - v.label = game.i18n.localize(CONFIG.VERMINE.abilities[k]) ?? k; - } - - // Prepare skills with localized names - for (let [k, v] of Object.entries(context.system.skills)) { - const skillKey = `VERMINE.skill.${k}`; - v.name = game.i18n.localize(skillKey); - if (v.name === skillKey) { - // Fallback to key if no translation - v.name = k.charAt(0).toUpperCase() + k.slice(1); - } - } - - // Prepare skill categories - for (let [k, v] of Object.entries(context.system.skill_categories)) { - if (k !== 'preferred') { - v.label = game.i18n.localize(v.label) ?? k; - } - } - } - - /** - * Organize and classify Items for NPC sheets. - * - * @param {Object} context - The context to prepare. - */ - _prepareItems(context) { - context.gear = this.actor.itemTypes['item']; - context.weapons = this.actor.itemTypes['weapon']; - context.defenses = this.actor.itemTypes['defense']; - context.vehicles = this.actor.itemTypes['vehicle']; - context.abilities = this.actor.itemTypes['ability']; - context.specialties = this.actor.itemTypes['specialty']; - context.backgrounds = this.actor.itemTypes['background']; - context.traumas = this.actor.itemTypes['trauma']; - context.evolutions = this.actor.itemTypes['evolution']; - } - - /* -------------------------------------------- */ - - /** @override */ - activateListeners(html) { - super.activateListeners(html); - - // Choose Totem - html.find('.chooseTotem').click(this._onTotemButton.bind(this)); - } - - /** - * Handle totem pick - * @param {Event} event - The originating click event - * @private - */ - _onTotemButton(event) { - event.preventDefault(); - const el = event.currentTarget; - const totemPicker = new TotemPicker(el, this.actor); - totemPicker.render(true); - } -} diff --git a/module/system/config.mjs b/module/system/config.mjs index 941d7a3..5c0af30 100644 --- a/module/system/config.mjs +++ b/module/system/config.mjs @@ -251,7 +251,7 @@ VERMINE.skillCategories = { } } -VERMINE.sexes = { "male": "VERMINE.sexes.male", "female": "VERMINE.sexes.female" }; +VERMINE.sexes = { "male": "SEXES.male", "female": "SEXES.female" }; VERMINE.totems = { "human": "TOTEMS.human.name", diff --git a/module/system/dialogs/rollDialog.mjs b/module/system/dialogs/rollDialog.mjs index 6f89654..b16103e 100644 --- a/module/system/dialogs/rollDialog.mjs +++ b/module/system/dialogs/rollDialog.mjs @@ -1,596 +1,327 @@ -import { VermineUtils } from "../roll.mjs"; - -/** - * Dialog for rolling dice in Vermine2047. - * Handles dice pool calculation, modifiers, and roll execution. - */ -export default class RollDialog extends Dialog { - - /** - * Creates a new RollDialog instance. - * @param {Object} data - The data for the dialog - * @param {HTMLElement} html - The HTML content of the dialog - * @param {Object} options - The options for the dialog - * @param {Function} [close] - The callback function for closing the dialog - */ - constructor(data, html, options, close = undefined) { - const conf = { - title: "jet de dés", - content: html, - buttons: { - roll: { - icon: '', - label: "Lancer !", - callback: () => this._onRoll() - }, - cancel: { - icon: '', - label: "Annuler", - callback: () => this.close() - } - }, - close: close - }; - super({ ...conf, ...data }, options); - // Store reference to close callback - this._closeCallback = close; - } - - /** - * Creates a new RollDialog instance. - * @param {Object} [data] - The data for the dialog - * @param {string} [data.label] - Roll label - * @param {string} [data.rolltype] - Roll type - * @param {number} [data.NoD=1] - Number of dice - * @param {boolean} [data.Reroll=false] - Allow rerolls - * @param {string} [data.actorId] - Actor ID for the roll - * @returns {Promise} The RollDialog instance or null if creation failed - */ - static async create(data = { - label: null, - rolltype: null, - NoD: 1, - Reroll: false, - actorId: game.user.character?.id ?? canvas.tokens.controlled[0]?.actor?.id - }) { - // Validate actorId - const actorId = data.actorId; - if (!actorId || typeof actorId !== 'string') { - ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected')); - return null; - } - - // Retrieve the actor data based on the actorId - data.actor = await game.actors.get(actorId); - if (!data.actor) { - ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected')); - return null; - } - - data.availableSpecialties = data.actor.items.filter(item => item.type === "specialty"); - data.availableItems = data.actor.items.filter(item => item.type === "item"); - data.config = CONFIG.VERMINE; - - // Define options for the dialog - const options = { - classes: ["vermineDialog"], - width: "fit-content", - height: 'fit-content', - zIndex: 99999 - }; - - // Render the HTML template for the dialog - const html = await renderTemplate('systems/vermine2047/templates/dialogs/roll-dialog.hbs', data); - - // Return a new RollDialog instance with the provided data, HTML, and options - return new RollDialog(data, html, options); - } - - /** - * Retrieves the default options for the RollDialog. - */ - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - focus: true, - classes: ["dialog vermine-roll"], - - }); - } - /** - * Retrieves the data for the dialog. - * @returns {Object} The context data for the dialog - */ - getData() { - // Get the context data from the superclass - const context = super.getData(); - context.data = this.data; - context.config = CONFIG.VERMINE; - - return context; - } - - /** - * Prepares items for display. - * @returns {Array} Filtered list of items - */ - prepareItems() { - return this.data.actor.items.filter(it => it.type === "item"); - } - - /** - * Prepares specialties for display. - * @returns {Array} Filtered list of specialties - */ - prepareSpecialties() { - return this.data.actor.items.filter(it => it.type === "specialty"); - } - - /** - * Activates event listeners for the dialog. - * @param {HTMLElement} html - The HTML element of the dialog. - */ - async activateListeners(html) { - // Activate event listeners from the superclass - super.activateListeners(html); - - // Initialize UI elements - this._html = html; - - // Retrieve roll data and set up event listeners - await this.getRollData(); - - // Set up event listeners for all roll-related inputs - const rollInputs = html.find('[data-roll]'); - for (const inp of rollInputs) { - inp.addEventListener('change', this._onRollInputChange.bind(this)); - } - - this.displaySpecialties(); - - const selectAbil = html.find('#ability')[0]; - // Set the maximum value for self control based on ability value - html.find("#self_control")[0].max = selectAbil.value; - selectAbil.addEventListener('change', this._onChangeAbility.bind(this)); - const selfControl = html.find('#self_control')[0]; - // Add event listener for self control changes - selfControl.addEventListener('change', this._onChangeSelfControl.bind(this)); - - // Set up difficulty change listener - html.find('#difficulty')[0].addEventListener('change', this._onDifficultyChange.bind(this)); - - // Set up handicap change listener - html.find('#handicap')[0].addEventListener('change', this._onHandicapChange.bind(this)); - - // Set up totem checkbox listeners - html.find('#human-totem')[0]?.addEventListener('change', this._onTotemChange.bind(this)); - html.find('#adapted-totem')[0]?.addEventListener('change', this._onTotemChange.bind(this)); - - // Initial update of all UI elements - this._updateUI(); - - }; - - /** - * Retrieves the roll data for the dialog. - * @param {Event} _ev - The event triggering the roll data retrieval (unused). - */ - getRollData(_ev) { - // Calculate and store the roll data - this.rollData = { - actor: this.data.actor, - NoD: this.getDicePool(), - Reroll: this.getReroll(), - difficulty: this.getDifficulty(), - handicap: this.getHandicap(), - rollType: this.getRollType(), - rollLabel: this.getLabel(), - totems: this.getTotems(), - self_control: this.getSelfControl(), - max_effort: this.getMaxEffort(), - keepTotem: this.getKeepTotem(), - skillCategory: this.getSkillCategory() - }; - this.displaySpecialties(); - this._updateUI(); - }; - - /** - * Gets the selected skill category - * @returns {string|null} - The skill category - */ - getSkillCategory() { - const html = this.element[0]; - const skillSelect = html.querySelector('#skill'); - if (skillSelect && skillSelect.selectedIndex > 0) { - const selectedOption = skillSelect.options[skillSelect.selectedIndex]; - return selectedOption.dataset.category || null; - } - return null; - } - - /** - * Gets the selected skill level - * @returns {number|null} - The skill level - */ - getSkillLevel() { - const html = this.element[0]; - const skillSelect = html.querySelector('#skill'); - if (skillSelect && skillSelect.selectedIndex > 0) { - const selectedOption = skillSelect.options[skillSelect.selectedIndex]; - return parseInt(selectedOption.value) || null; - } - return null; - } - - /** - * Checks if a specialty is selected - * @returns {boolean} - True if a specialty is selected - */ - hasSpecialtySelected() { - const html = this.element[0]; - const specialtyRadio = html.querySelector('input[name="usingSpecialization"]:checked'); - return specialtyRadio && specialtyRadio.value !== 'aucune'; - } - - /** - * Handles changes to roll inputs and updates UI. - * @param {Event} ev - The change event. - */ - _onRollInputChange(ev) { - this.getRollData(ev); - } - - /** - * Updates all UI elements based on current roll data - */ - _updateUI() { - if (!this._html) return; - - const html = this._html[0]; - - // Update total dice pool display - const totalDice = this.getDicePool(); - const totalEl = html.querySelector('#dice-pool-total'); - if (totalEl) { - totalEl.textContent = `${totalDice}D`; - } - - // Update bonus count - const bonusCount = this._calculateBonusCount(); - const bonusEl = html.querySelector('#total-bonus'); - if (bonusEl) { - bonusEl.textContent = bonusCount; - } - - // Update difficulty display - const difficultyEl = html.querySelector('#current-difficulty'); - const difficultySelect = html.querySelector('#difficulty'); - if (difficultyEl && difficultySelect) { - const selectedIndex = difficultySelect.selectedIndex; - const diffValue = parseInt(difficultySelect.options[selectedIndex].value); - const diffLabel = difficultySelect.options[selectedIndex].text.split(' ')[0]; - difficultyEl.textContent = `${diffLabel} (${diffValue})`; - } - - // Update handicap display - const handicapEl = html.querySelector('#current-handicap'); - const handicapSelect = html.querySelector('#handicap'); - if (handicapEl && handicapSelect) { - const selectedIndex = handicapSelect.selectedIndex; - handicapEl.textContent = handicapSelect.options[selectedIndex].text; - } - - // Update ability score display - const abilSelect = html.querySelector('#ability'); - const abilScoreEl = html.querySelector('#abilityScoreValue'); - if (abilSelect && abilScoreEl) { - const selectedIndex = abilSelect.selectedIndex; - if (selectedIndex > 0) { - abilScoreEl.textContent = abilSelect.options[selectedIndex].value; - } else { - abilScoreEl.textContent = '0'; - } - } - - // Update specialty display - const specialtyRadios = html.querySelectorAll('input[name="usingSpecialization"]:checked'); - const currentSpecEl = html.querySelector('.current-specialty'); - if (currentSpecEl && specialtyRadios.length > 0) { - const checkedRadio = specialtyRadios[0]; - currentSpecEl.textContent = checkedRadio.value === 'aucune' ? game.i18n.localize('VERMINE.none') : checkedRadio.value; - } - } - - /** - * Calculates the bonus count for display. - * @returns {number} Total bonus dice. - */ - _calculateBonusCount() { - let bonus = 0; - - // Help bonus - if (this._html?.find('#helped')[0]?.checked) { - bonus += 1; - } - - // Group bonus - const groupValue = parseInt(this._html?.find('#group')[0]?.value, 10) || 0; - bonus += groupValue; - - // Self control bonus - const selfControlValue = parseInt(this._html?.find('#self_control')[0]?.value, 10) || 0; - bonus += selfControlValue; - - // Tools bonus - const toolsChecked = this._html?.find('input[name="usingTools"]:checked')[0]?.value !== '0'; - if (toolsChecked) { - bonus += 1; - } - - // Totems bonus - if (this._html?.find('#human-totem')[0]?.checked) { - bonus += parseInt(this.data.actor?.system?.adaptation?.totems?.human?.value, 10) || 0; - } - if (this._html?.find('#adapted-totem')[0]?.checked) { - bonus += parseInt(this.data.actor?.system?.adaptation?.totems?.adapted?.value, 10) || 0; - } - - // Specialty bonus - const specialtyChecked = this._html?.find('input[name="usingSpecialization"]:checked')[0]?.value !== 'aucune'; - if (specialtyChecked) { - bonus += 1; - } - - return bonus; - } - - /** - * Handles difficulty change - * @param {Event} ev - The change event - */ - _onDifficultyChange(ev) { - this._updateUI(); - } - - /** - * Handles handicap change - * @param {Event} ev - The change event - */ - _onHandicapChange(ev) { - this._updateUI(); - } - - /** - * Handles totem checkbox change - * @param {Event} ev - The change event - */ - _onTotemChange(ev) { - this._updateUI(); - } - - /** - * Gets the selected totem to keep (for dual totem rolls) - * @returns {string|null} - The totem to keep ('human', 'adapted', or null) - */ - getKeepTotem() { - const keepTotemSelect = this._html?.find('#keep-totem-select')[0]; - if (keepTotemSelect) { - return keepTotemSelect.value; - } - // Default to null (both totems used) - return null; - } - - - /** - * Handles the change in self control value. - * @param {Event} ev - The event triggering the change in self control value. - */ - _onChangeSelfControl(ev) { - const html = this.element[0]; - const selfControlValueElement = html.querySelector('#self_control_value'); - if (selfControlValueElement) { - selfControlValueElement.innerText = ev.currentTarget.value; - } - } - /** - * Retrieves the handicap value from the HTML element. - * @returns {number} The handicap value. - */ - getHandicap() { - const html = this.element[0]; - const handicapValue = html.querySelector('#handicap')?.value ?? '1'; - return parseInt(handicapValue, 10); - } - /** - * Gets the roll type (ability or skill). - * @returns {string} The roll type: 'skill' or 'ability'. - */ - getRollType() { - const html = this.element[0]; - return html.querySelector('select#skill')?.value ? "skill" : "ability"; - } - - /** - * Gets the label for the roll. - * @returns {string} The roll label. - */ - getLabel() { - const html = this.element[0]; - const rollType = this.getRollType(); - - if (rollType === "skill") { - const skillSelect = html.querySelector('select#skill'); - const selectedIndex = skillSelect?.selectedIndex ?? 0; - return skillSelect?.options[selectedIndex]?.dataset?.label ?? ""; - } - - const abilitySelect = html.querySelector('select#ability'); - const selectedIndex = abilitySelect?.selectedIndex ?? 0; - return abilitySelect?.options[selectedIndex]?.dataset?.label ?? ""; - } - - /** - * Displays specialties related to the selected skill. - */ - displaySpecialties() { - const specialties = this.element[0]?.querySelectorAll('[data-spec-skill]'); - if (specialties) { - specialties.forEach(specEl => { - specEl.style.display = "inline"; - }); - } - } - - /** - * Retrieves the self control value from the HTML element. - * @returns {number} The self control value. - */ - getSelfControl() { - const html = this.element[0]; - const selfControlValue = html.querySelector('#self_control')?.value ?? '0'; - return parseInt(selfControlValue, 10); - } - - /** - * Retrieves the maximum effort value from the HTML element. - * @returns {number} The maximum effort value. - */ - getMaxEffort() { - const html = this.element[0]; - const abilityValue = html.querySelector('#ability')?.value ?? '0'; - return parseInt(abilityValue, 10); - } - - /** - * Retrieves the selected totems from the HTML element. - * @returns {Object} An object containing the selected totems {human: boolean, adapted: boolean}. - */ - getTotems() { - const html = this.element[0]; - return { - human: html.querySelector('#human-totem')?.checked ?? false, - adapted: html.querySelector('#adapted-totem')?.checked ?? false - }; - } - - /** - * Handles the change in ability value. - * @param {Event} ev - The event triggering the change in ability value. - */ - _onChangeAbility(ev) { - const html = this.element[0]; - const abilitySelect = html.querySelector('#ability'); - const selectedIndex = abilitySelect?.selectedIndex ?? 0; - const score = abilitySelect?.options[selectedIndex]?.value ?? '0'; - - const scoreElement = html.querySelector('#abilityScore'); - if (scoreElement) { - scoreElement.value = score; - } - - const selfControlElement = html.querySelector('#self_control'); - if (selfControlElement) { - selfControlElement.max = score; - } - } - - /** - * Retrieves the total dice pool based on various factors. - * @returns {number} The total dice pool value. - */ - getDicePool() { - // Retrieve the HTML element - const html = this.element[0]; - - // Safely get ability value - const abilitySelect = html.querySelector('#ability'); - const abilValue = abilitySelect?.options[abilitySelect?.selectedIndex]?.value ?? 0; - - // Safely get skill value and pool - const skillSelect = html.querySelector('#skill'); - const skillOption = skillSelect?.options[skillSelect?.selectedIndex]; - const skillValue = skillOption?.dataset?.pool ?? 0; - - // Get the self control value - const selfControl = html.querySelector('#self_control')?.value ?? 0; - - // Calculate bonuses based on certain conditions - const bonuses = - (html.querySelector('#usingSpecialization')?.checked ? 1 : 0) + - (html.querySelector('#helped')?.checked ? 1 : 0) + - (html.querySelector('#usingTools')?.checked ? 1 : 0); - - // Calculate the total dice pool - const total = parseInt(abilValue, 10) + parseInt(selfControl, 10) + parseInt(skillValue, 10) + bonuses; - return total || 0; - } - - /** - * Retrieves the reroll value based on selected skill. - * @returns {number} The reroll value. - */ - getReroll() { - const html = this.element[0]; - const skillSelect = html.querySelector('#skill'); - const selectedIndex = skillSelect?.selectedIndex ?? 0; - const rerollValue = skillSelect?.options[selectedIndex]?.dataset?.reroll ?? '0'; - return parseInt(rerollValue, 10) || 0; - } - - /** - * Retrieves the difficulty value based on selected option. - * @returns {number} The difficulty value. - */ - getDifficulty() { - const html = this.element[0]; - const difficultySelect = html.querySelector('#difficulty'); - const selectedIndex = difficultySelect?.selectedIndex ?? 0; - const diffValue = difficultySelect?.options[selectedIndex]?.value ?? '0'; - return parseInt(diffValue, 10) || 0; - } - - /** - * Performs a dice roll based on the roll data and handles self control checks. - * @returns {Promise} A promise that resolves with the Roll result or false if cancelled. - */ - async _onRoll() { - // Check if self control is required for the roll - if (this.rollData.self_control > 0) { - // Check if the actor has enough self control - const currentSelfControl = this.rollData.actor?.system?.attributes?.self_control?.value ?? 0; - if (currentSelfControl < this.rollData.self_control) { - // Display a warning message if self control is insufficient - ui.notifications.warn(game.i18n.localize('VERMINE.error_not_enough_self_control')); - // Re-render the dialog - this.render(true); - return false; // Exit the function if self control is insufficient - } - } - - const caracName = this.element[0]?.querySelector('[name="ability"]')?.value; - if (caracName === "0" || caracName === undefined) { - // Display a warning message if no ability selected - ui.notifications.warn(game.i18n.localize('VERMINE.error_select_ability')); - // Re-render the dialog - this.render(true); - return false; // Exit the function if no ability - } - - // Deduct self control points if necessary - if (this.rollData.self_control > 0) { - const newSelfControl = this.rollData.actor.system.attributes.self_control.value - this.rollData.self_control; - // Update the actor's self control value - await this.rollData.actor.update({ - "system.attributes.self_control.value": newSelfControl - }); - } - - // Perform the dice roll using VermineUtils - return VermineUtils.roll({ - ...this.rollData, - skillLevel: this.getSkillLevel(), - hasSpecialty: this.hasSpecialtySelected() - }); - } -} \ No newline at end of file +import { VermineUtils } from "../roll.mjs"; + +const { HandlebarsApplicationMixin } = foundry.applications.api; + +export default class RollDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) { + + #actor; + + get title() { + return game.i18n.localize("VERMINE.roll"); + } + + static DEFAULT_OPTIONS = { + classes: ["vermine-roll"], + tag: "form", + window: { + icon: "fas fa-dice-d10", + resizable: false + }, + position: { + width: 520, + height: 600 + }, + actions: { + roll: RollDialog.#onRoll, + cancel: RollDialog.#onCancel + } + }; + + static PARTS = { + main: { template: "systems/vermine2047/templates/dialogs/roll-dialog.hbs" } + }; + + static async create(data = {}) { + const actorId = data.actorId ?? game.user.character?.id ?? canvas.tokens.controlled[0]?.actor?.id; + if (!actorId || typeof actorId !== "string") { + ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected")); + return null; + } + const actor = await game.actors.get(actorId); + if (!actor) { + ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected")); + return null; + } + return new RollDialog({ actor, label: data.label, rolltype: data.rolltype }); + } + + constructor(options = {}) { + super(options); + this.#actor = options.actor; + this.label = options.label ?? null; + this.rolltype = options.rolltype ?? null; + } + + async _prepareContext() { + const actor = this.#actor; + return { + actor, + system: actor.system, + config: CONFIG.VERMINE, + label: this.label, + rollType: this.rolltype, + labelKey: this.label, + speakerId: actor.id, + ability: null, + help: false, + specialty: false, + availableSpecialties: actor.items.filter(i => i.type === "specialty"), + availableItems: actor.items.filter(i => i.type === "item") + }; + } + + async _onRender(context, options) { + this.element.dataset.actorId = this.#actor.id; + + for (const inp of this.element.querySelectorAll("[data-roll]")) { + inp.addEventListener("change", this.#onInputChange.bind(this)); + } + + const ability = this.element.querySelector("#ability"); + if (ability) { + ability.addEventListener("change", this.#onChangeAbility.bind(this)); + const selfControl = this.element.querySelector("#self_control"); + if (selfControl) selfControl.max = ability.value; + } + + const selfControl = this.element.querySelector("#self_control"); + if (selfControl) { + selfControl.addEventListener("change", this.#onChangeSelfControl.bind(this)); + } + + this.element.querySelector("#difficulty")?.addEventListener("change", () => this.#updateUI()); + this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI()); + this.element.querySelector("#human-totem")?.addEventListener("change", () => this.#updateUI()); + this.element.querySelector("#adapted-totem")?.addEventListener("change", () => this.#updateUI()); + + this.#displaySpecialties(); + this.#updateUI(); + + if (ability?.value !== "0") { + this.element.querySelector("#self_control")?.dispatchEvent(new Event("change")); + } + } + + // ── Getters ────────────────────────────────────────────────────────── + + get #el() { return this.element; } + + #getAbility() { return this.#el.querySelector("#ability"); } + #getSkill() { return this.#el.querySelector("#skill"); } + #getDifficulty() { return this.#el.querySelector("#difficulty"); } + #getHandicap() { return this.#el.querySelector("#handicap"); } + #getSelfCtrl() { return this.#el.querySelector("#self_control"); } + + getDicePool() { + const abil = this.#getAbility(); + const abilVal = parseInt(abil?.options[abil?.selectedIndex]?.value, 10) || 0; + const skill = this.#getSkill(); + const skillPool = parseInt(skill?.options[skill?.selectedIndex]?.dataset?.pool, 10) || 0; + const sc = parseInt(this.#getSelfCtrl()?.value, 10) || 0; + const specChecked = this.#el.querySelector("#usingSpecialization")?.checked; + const helped = this.#el.querySelector("#helped")?.checked; + const tools = this.#el.querySelector("input[name='usingTools']:checked")?.value !== "0"; + const bonuses = (specChecked ? 1 : 0) + (helped ? 1 : 0) + (tools ? 1 : 0); + return (abilVal + sc + skillPool + bonuses) || 0; + } + + getDifficultySelect() { + const sel = this.#getDifficulty(); + const idx = sel?.selectedIndex ?? 0; + return parseInt(sel?.options[idx]?.value, 10) || 7; + } + + getReroll() { + const sel = this.#getSkill(); + const idx = sel?.selectedIndex ?? 0; + return parseInt(sel?.options[idx]?.dataset?.reroll, 10) || 0; + } + + getHandicapSelect() { + const sel = this.#getHandicap(); + return parseInt(sel?.value, 10) || 1; + } + + getSkillCategory() { + const sel = this.#getSkill(); + const idx = sel?.selectedIndex ?? 0; + return sel?.options[idx]?.dataset?.category ?? null; + } + + getSkillLevel() { + const sel = this.#getSkill(); + const idx = sel?.selectedIndex ?? 0; + const val = sel?.options[idx]?.value; + return val ? parseInt(val, 10) : null; + } + + hasSpecialtySelected() { + const checked = this.#el.querySelector("input[name='usingSpecialization']:checked"); + return checked && checked.value !== "aucune"; + } + + getRollType() { + const sel = this.#getSkill(); + return sel?.value ? "skill" : "ability"; + } + + getLabel() { + const type = this.getRollType(); + if (type === "skill") { + const sel = this.#getSkill(); + const idx = sel?.selectedIndex ?? 0; + return sel?.options[idx]?.dataset?.label ?? ""; + } + const sel = this.#getAbility(); + const idx = sel?.selectedIndex ?? 0; + return sel?.options[idx]?.dataset?.label ?? ""; + } + + getSelfControl() { + return parseInt(this.#getSelfCtrl()?.value, 10) || 0; + } + + getMaxEffort() { + const sel = this.#getAbility(); + return parseInt(sel?.value, 10) || 0; + } + + getTotems() { + return { + human: this.#el.querySelector("#human-totem")?.checked ?? false, + adapted: this.#el.querySelector("#adapted-totem")?.checked ?? false + }; + } + + getKeepTotem() { + return this.#el.querySelector("#keep-totem-select")?.value ?? null; + } + + // ── UI ─────────────────────────────────────────────────────────────── + + #displaySpecialties() { + for (const el of this.#el.querySelectorAll("[data-spec-skill]")) { + el.style.display = "inline"; + } + } + + #calculateBonusCount() { + let b = 0; + if (this.#el.querySelector("#helped")?.checked) b += 1; + b += parseInt(this.#el.querySelector("#group")?.value, 10) || 0; + b += parseInt(this.#getSelfCtrl()?.value, 10) || 0; + const tools = this.#el.querySelector("input[name='usingTools']:checked"); + if (tools && tools.value !== "0") b += 1; + const human = this.#el.querySelector("#human-totem"); + if (human?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.human?.value, 10) || 0; + const adapted = this.#el.querySelector("#adapted-totem"); + if (adapted?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.adapted?.value, 10) || 0; + if (this.hasSpecialtySelected()) b += 1; + return b; + } + + #updateUI() { + const total = this.getDicePool(); + const totalEl = this.#el.querySelector("#dice-pool-total"); + if (totalEl) totalEl.textContent = `${total}D`; + + const bonusEl = this.#el.querySelector("#total-bonus"); + if (bonusEl) bonusEl.textContent = this.#calculateBonusCount(); + + const diffSel = this.#getDifficulty(); + const diffEl = this.#el.querySelector("#current-difficulty"); + if (diffEl && diffSel) { + const idx = diffSel.selectedIndex; + const val = diffSel.options[idx].value; + const lbl = diffSel.options[idx].text.split(" ")[0]; + diffEl.textContent = `${lbl} (${val})`; + } + + const handSel = this.#getHandicap(); + const handEl = this.#el.querySelector("#current-handicap"); + if (handEl && handSel) { + handEl.textContent = handSel.options[handSel.selectedIndex].text; + } + + const abilSel = this.#getAbility(); + const abilValEl = this.#el.querySelector("#abilityScoreValue"); + if (abilSel && abilValEl) { + const idx = abilSel.selectedIndex; + abilValEl.textContent = idx > 0 ? abilSel.options[idx].value : "0"; + } + + const specChecked = this.#el.querySelector("input[name='usingSpecialization']:checked"); + const specEl = this.#el.querySelector(".current-specialty"); + if (specEl && specChecked) { + specEl.textContent = specChecked.value === "aucune" + ? game.i18n.localize("VERMINE.none") + : specChecked.value; + } + } + + // ── Event handlers ─────────────────────────────────────────────────── + + #onInputChange() { + this.#updateUI(); + } + + #onChangeAbility(ev) { + const sel = ev.currentTarget; + const score = sel.options[sel.selectedIndex]?.value ?? "0"; + const scoreEl = this.#el.querySelector("#abilityScore"); + if (scoreEl) scoreEl.value = score; + const sc = this.#getSelfCtrl(); + if (sc) sc.max = score; + this.#updateUI(); + } + + #onChangeSelfControl(ev) { + const valEl = this.#el.querySelector("#self_control_value"); + if (valEl) valEl.textContent = ev.currentTarget.value; + } + + static async #onCancel(event, target) { + this.close(); + } + + static async #onRoll(event, target) { + const selfCtrl = this.getSelfControl(); + if (selfCtrl > 0) { + const current = this.#actor?.system?.attributes?.self_control?.value ?? 0; + if (current < selfCtrl) { + ui.notifications.warn(game.i18n.localize("VERMINE.error_not_enough_self_control")); + return; + } + } + + const abilityVal = this.#el.querySelector('[name="ability"]')?.value; + if (!abilityVal || abilityVal === "0") { + ui.notifications.warn(game.i18n.localize("VERMINE.error_select_ability")); + return; + } + + if (selfCtrl > 0) { + const newVal = this.#actor.system.attributes.self_control.value - selfCtrl; + await this.#actor.update({ "system.attributes.self_control.value": newVal }); + } + + await VermineUtils.roll({ + actor: this.#actor, + NoD: this.getDicePool(), + Reroll: this.getReroll(), + difficulty: this.getDifficultySelect(), + handicap: this.getHandicapSelect(), + rollLabel: this.getLabel(), + totems: this.getTotems(), + self_control: selfCtrl, + max_effort: this.getMaxEffort(), + keepTotem: this.getKeepTotem(), + skillCategory: this.getSkillCategory(), + skillLevel: this.getSkillLevel(), + hasSpecialty: this.hasSpecialtySelected() + }); + + this.close(); + } + +} diff --git a/module/system/fight.mjs b/module/system/fight.mjs index 0971969..f0f27a8 100644 --- a/module/system/fight.mjs +++ b/module/system/fight.mjs @@ -291,7 +291,7 @@ export class VermineFight { console.log(data); // render template - let html = await renderTemplate(data._template, data); + let html = await foundry.applications.handlebars.renderTemplate(data._template, data); let ui = new Dialog({ title: game.i18n.localize("VERMINE.FightTool"), @@ -415,7 +415,7 @@ export class VermineCombat extends Combat { } } -export class VermineCombatTracker extends CombatTracker { +export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker { get template() { return "systems/vermine2047/templates/combat-tracker.hbs"; diff --git a/module/system/handlebars-manager.mjs b/module/system/handlebars-manager.mjs index c5267e1..4c4b7fd 100644 --- a/module/system/handlebars-manager.mjs +++ b/module/system/handlebars-manager.mjs @@ -4,7 +4,7 @@ * @return {Promise} */ export const preloadHandlebarsTemplates = async function () { - return loadTemplates([ + return foundry.applications.handlebars.loadTemplates([ // Actor partials. @@ -31,6 +31,7 @@ export const preloadHandlebarsTemplates = async function () { // npc partials "systems/vermine2047/templates/actor/npc/npc-combat.hbs", + "systems/vermine2047/templates/actor/parts/npc-skill-category.hbs", // creature partials "systems/vermine2047/templates/actor/creature/creature-combat.hbs", diff --git a/module/system/hooks.mjs b/module/system/hooks.mjs index 7f32555..53d8a46 100644 --- a/module/system/hooks.mjs +++ b/module/system/hooks.mjs @@ -38,16 +38,16 @@ export const registerHooks = function () { }); - Hooks.on('renderChatMessage', async (message, html, data) => { - let rerollTitle = html[0].querySelector(".reroll-fromroll h4"); + Hooks.on('renderChatMessageHTML', async (message, html, data) => { + let rerollTitle = html.querySelector(".reroll-fromroll h4"); if (rerollTitle) { - rerollTitle.addEventListener("click", () => { html[0].querySelector(".reroll").classList.toggle('visible') }) + rerollTitle.addEventListener("click", () => { html.querySelector(".reroll").classList.toggle('visible') }) } if (message.author?._id != game.user._id || !game.user.isGM) { // désactiver les inputs pour les joueurs non-auteurs du message - html[0].querySelectorAll("input").forEach(inp => inp.disabled = true); + html.querySelectorAll("input").forEach(inp => inp.disabled = true); //cacher le boutton reroll - html[0].querySelectorAll("div.reroll-from-effort").forEach(el => el.style.display = "none") + html.querySelectorAll("div.reroll-from-effort").forEach(el => el.style.display = "none") return } await VermineUtils.chatListenners(html) diff --git a/module/system/roll.mjs b/module/system/roll.mjs index b84766b..f35dbb2 100644 --- a/module/system/roll.mjs +++ b/module/system/roll.mjs @@ -468,7 +468,7 @@ export class VermineUtils { * @returns {Promise} The created chat message */ static async diplayChatRoll(roll, param) { - const content = await renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param }); + const content = await foundry.applications.handlebars.renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param }); const chatData = { user: game.user?._id, speaker: ChatMessage.getSpeaker(), diff --git a/module/system/tour.mjs b/module/system/tour.mjs index a4b2909..b4d1acc 100644 --- a/module/system/tour.mjs +++ b/module/system/tour.mjs @@ -48,7 +48,7 @@ class CreateActorDialog extends FormApplication { }); } } -class VermineTour extends Tour { +class VermineTour extends foundry.nue.Tour { /** @override */ async _preStep() { var _a2, _b, _c, _d, _e; diff --git a/module/vermine2047.mjs b/module/vermine2047.mjs index b08a6bd..1f0cb87 100644 --- a/module/vermine2047.mjs +++ b/module/vermine2047.mjs @@ -3,19 +3,17 @@ import { registerSettings } from "./system/settings.mjs"; import { GroupLink } from "./system/group-link.mjs"; // Import document classes. -import { VermineActor } from "./documents/actor.mjs"; - -import { VermineCharacterSheet } from "./sheets/character-sheet.mjs"; -import { VermineNpcSheet } from "./sheets/npc-sheet.mjs"; -import { VermineGroupSheet } from "./sheets/npc-group.mjs"; -import { VermineCreatureSheet } from "./sheets/creature-sheet.mjs"; - -import { VermineItem } from "./documents/item.mjs"; -import { VermineItemSheet } from "./sheets/item-sheet.mjs"; +import * as documents from "./documents/_module.mjs"; import { VermineUtils } from "./system/roll.mjs"; import { VermineCombat, VermineCombatant, VermineCombatTracker } from "./system/fight.mjs"; +// Import DataModels +import * as models from "./models/_module.mjs" + +// Import ApplicationV2 sheets +import * as sheets from "./applications/sheets/_module.mjs" + // Import helper/utility classes and constants. import { preloadHandlebarsTemplates, registerHandlebarsHelpers } from "./system/handlebars-manager.mjs"; import { VERMINE } from "./system/config.mjs"; @@ -26,51 +24,109 @@ import { VERMINE } from "./system/config.mjs"; Hooks.once('init', async function () { + // System stylesheet is automatically loaded by Foundry from system.json + // No need to manually inject it - this was causing MIME type issues + // If you need to ensure fresh CSS, use cache-busting in the filename or system.json version + + // Register GroupLink hooks for automatic synchronization + GroupLink.registerHooks(); + + // Register ALL DataModels FIRST - this is crucial for Foundry V2 + // Use individual assignments like Celestopol for compatibility + CONFIG.Actor.dataModels.character = models.VermineCharacterData; + CONFIG.Actor.dataModels.npc = models.VermineNpcData; + CONFIG.Actor.dataModels.group = models.VermineGroupData; + CONFIG.Actor.dataModels.creature = models.VermineCreatureData; + + CONFIG.Item.dataModels.item = models.VermineItemData; + CONFIG.Item.dataModels.weapon = models.VermineWeaponData; + CONFIG.Item.dataModels.defense = models.VermineDefenseData; + CONFIG.Item.dataModels.vehicle = models.VermineVehicleData; + CONFIG.Item.dataModels.ability = models.VermineAbilityData; + CONFIG.Item.dataModels.specialty = models.VermineSpecialtyData; + CONFIG.Item.dataModels.background = models.VermineBackgroundData; + CONFIG.Item.dataModels.trauma = models.VermineTraumaData; + CONFIG.Item.dataModels.evolution = models.VermineEvolutionData; + CONFIG.Item.dataModels.rumor = models.VermineRumorData; + CONFIG.Item.dataModels.target = models.VermineTargetData; + CONFIG.Item.dataModels.rite = models.VermineRiteData; + + // Define custom Document classes AFTER ALL DataModels are registered + CONFIG.Actor.documentClass = documents.VermineActor; + CONFIG.Item.documentClass = documents.VermineItem; + // Add utility classes to the global game object so that they're more easily // accessible in global contexts. + // Note: Do NOT expose Document classes here as it can cause issues with DataModel initialization game.vermine2047 = { - VermineActor, - VermineItem, VermineUtils, VermineCombat, GroupLink }; - - // Register GroupLink hooks for automatic synchronization - GroupLink.registerHooks(); - - // Define custom Document classes - CONFIG.Actor.documentClass = VermineActor; - CONFIG.Item.documentClass = VermineItem; CONFIG.ui.combat = VermineCombatTracker; CONFIG.Combatant.documentClass = VermineCombatant; CONFIG.Combat.documentClass = VermineCombat; - // Register sheet application classes - Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet('vermine2047', VermineCharacterSheet, { - types: ['character'], - makeDefault: true, - }); + // Register sheet application classes (ApplicationV2) + // Unregister core sheets + foundry.applications.sheets.ActorSheetV2?.unregisterSheet?.("core", "Actor", { + types: ["character", "npc", "group", "creature"] + }) + foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1?.sheets?.ItemSheet) - Actors.registerSheet('vermine2047', VermineNpcSheet, { - types: ['npc'], - makeDefault: true, - }); + // Actor sheets + foundry.documents.collections.Actors.registerSheet("vermine2047", sheets.VermineCharacterSheetV2, { + types: ["character"], makeDefault: true, label: "VERMINE.Sheet.character" + }) + foundry.documents.collections.Actors.registerSheet("vermine2047", sheets.VermineNpcSheetV2, { + types: ["npc"], makeDefault: true, label: "VERMINE.Sheet.npc" + }) + foundry.documents.collections.Actors.registerSheet("vermine2047", sheets.VermineCreatureSheetV2, { + types: ["creature"], makeDefault: true, label: "VERMINE.Sheet.creature" + }) + foundry.documents.collections.Actors.registerSheet("vermine2047", sheets.VermineGroupSheetV2, { + types: ["group"], makeDefault: true, label: "VERMINE.Sheet.group" + }) - Actors.registerSheet('vermine2047', VermineCreatureSheet, { - types: ['creature'], - makeDefault: true, - }); - - Actors.registerSheet('vermine2047', VermineGroupSheet, { - types: ['group'], - makeDefault: true, - }); - Items.unregisterSheet("core", ItemSheet); - Items.registerSheet("vermine2047", VermineItemSheet, { makeDefault: true }); + // Item sheets — un par type + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineItemSheetV2, { + types: ["item"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineWeaponSheetV2, { + types: ["weapon"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineDefenseSheetV2, { + types: ["defense"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineVehicleSheetV2, { + types: ["vehicle"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineAbilitySheetV2, { + types: ["ability"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineSpecialtySheetV2, { + types: ["specialty"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineBackgroundSheetV2, { + types: ["background"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineTraumaSheetV2, { + types: ["trauma"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineEvolutionSheetV2, { + types: ["evolution"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineRumorSheetV2, { + types: ["rumor"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineTargetSheetV2, { + types: ["target"], makeDefault: true + }) + foundry.documents.collections.Items.registerSheet("vermine2047", sheets.VermineRiteSheetV2, { + types: ["rite"], makeDefault: true + }) registerHandlebarsHelpers(); // Register Handlebars helpers registerHooks(); // register Hooks @@ -79,13 +135,8 @@ Hooks.once('init', async function () { // Add custom constants for configuration. CONFIG.VERMINE = VERMINE; - // Set up model templates - must be done after system templates are loaded - if (game.system?.template?.Actor && game.system?.template?.Item) { - CONFIG.VERMINE.model = { - Actor: game.system.template.Actor, - Item: game.system.template.Item - }; - } + // Les DataModels sont déjà enregistrés dans CONFIG.Actor.dataModels et + // CONFIG.Item.dataModels. On expose leurs définitions pour compatibilité. /** * Set an initiative formula for the system @@ -116,8 +167,33 @@ Hooks.once('init', async function () { document.querySelector('#ui-left').prepend(el); - // Preload Handlebars templates. - return preloadHandlebarsTemplates(); + // Preload templates (ApplicationV2 + legacy) + await preloadHandlebarsTemplates(); + await foundry.applications.handlebars.loadTemplates([ + "systems/vermine2047/templates/actor/appv2/character-header.hbs", + "systems/vermine2047/templates/actor/appv2/character-main.hbs", + "systems/vermine2047/templates/actor/appv2/character-abilities.hbs", + "systems/vermine2047/templates/actor/appv2/character-totem.hbs", + "systems/vermine2047/templates/actor/appv2/character-equipment.hbs", + "systems/vermine2047/templates/actor/appv2/character-stories.hbs", + "systems/vermine2047/templates/actor/appv2/character-combat.hbs", + "systems/vermine2047/templates/actor/appv2/npc-main.hbs", + "systems/vermine2047/templates/actor/appv2/npc-characteristics.hbs", + "systems/vermine2047/templates/actor/appv2/npc-skills.hbs", + "systems/vermine2047/templates/actor/appv2/npc-threat.hbs", + "systems/vermine2047/templates/actor/appv2/npc-combat.hbs", + "systems/vermine2047/templates/actor/appv2/npc-notes.hbs", + "systems/vermine2047/templates/actor/appv2/group-main.hbs", + "systems/vermine2047/templates/actor/appv2/group-info.hbs", + "systems/vermine2047/templates/actor/appv2/group-gear.hbs", + "systems/vermine2047/templates/actor/appv2/group-road.hbs", + "systems/vermine2047/templates/actor/appv2/group-reserve.hbs", + "systems/vermine2047/templates/actor/appv2/creature-main.hbs", + "systems/vermine2047/templates/actor/appv2/creature-info.hbs", + "systems/vermine2047/templates/actor/appv2/creature-stats.hbs", + "systems/vermine2047/templates/actor/appv2/creature-combat.hbs", + "systems/vermine2047/templates/actor/appv2/creature-effects.hbs" + ]); }); diff --git a/package-lock.json b/package-lock.json index af96d0a..9b7e7c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,107 @@ "version": "1.0.0", "devDependencies": { "@foundryvtt/foundryvtt-cli": "^1.0.2", + "@typescript-eslint/eslint-plugin": "^8.60.1", + "@typescript-eslint/parser": "^8.60.1", "@typhonjs-fvtt/eslint-config-foundry.js": "^0.8.0", "browser-sync": "^3.0.3", + "eslint": "^8.57.1", + "eslint-plugin-html": "^8.1.4", + "eslint-plugin-import": "^2.32.0", "gulp": "^5.0.0", - "gulp-sass": "^5.1.0", - "sass": "^1.55.0" + "gulp-autoprefixer": "^8.0.0", + "gulp-clean-css": "^4.3.0", + "gulp-less": "^5.0.0", + "gulp-rename": "^2.0.0", + "less": "^4.2.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@foundryvtt/foundryvtt-cli": { @@ -70,6 +166,114 @@ "node": ">=10.13.0" } }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@seald-io/binary-search-tree": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz", @@ -108,6 +312,13 @@ "@types/node": "*" } }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", @@ -117,12 +328,414 @@ "undici-types": "~6.19.8" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", + "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/type-utils": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.60.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", + "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", + "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.1", + "@typescript-eslint/types": "^8.60.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", + "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", + "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", + "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/types": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", + "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", + "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.60.1", + "@typescript-eslint/tsconfig-utils": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", + "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", + "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typhonjs-fvtt/eslint-config-foundry.js": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@typhonjs-fvtt/eslint-config-foundry.js/-/eslint-config-foundry.js-0.8.0.tgz", "integrity": "sha512-Fu1XDS747exX5zVwty4VSwlOwkuzdnnN15C5w66uG4hOpJnPCZU/jcyEOCf9bazRPp06Smimn+mKd9OjrCvuuw==", "dev": true }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, "node_modules/abstract-level": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz", @@ -177,6 +790,46 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -189,6 +842,19 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -259,6 +925,23 @@ "node": ">=0.10.0" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -268,6 +951,29 @@ "node": ">=0.10.0" } }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", @@ -277,6 +983,88 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -318,6 +1106,16 @@ "node": ">=0.8.0" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/async-settle": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-2.0.0.tgz", @@ -330,6 +1128,43 @@ "node": ">= 10.13.0" } }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -401,6 +1236,19 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -523,6 +1371,40 @@ "stream-throttle": "^0.1.3" } }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/bs-recipes": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", @@ -563,16 +1445,16 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -581,6 +1463,68 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/catering": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", @@ -647,6 +1591,29 @@ "node": ">=12" } }, + "node_modules/clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -694,6 +1661,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -745,6 +1722,22 @@ "node": ">= 0.6" } }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/copy-props": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-4.0.0.tgz", @@ -771,6 +1764,91 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -780,6 +1858,13 @@ "ms": "2.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -797,6 +1882,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -837,6 +1940,106 @@ "node": ">= 0.8.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/each-props": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/each-props/-/each-props-3.0.0.tgz", @@ -880,6 +2083,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, + "node_modules/electron-to-chromium": { + "version": "1.5.367", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.367.tgz", + "integrity": "sha512-4Mk/mrynCNQ+atY40D3UpmhLWB6AHMbYMlIrPhHcMF6x0L7O0b052FCAsxw1LlaR++UFuNg3D/A6XCuGDa0guQ==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -993,14 +2203,108 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -1014,11 +2318,72 @@ "node": ">= 0.4" } }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1029,6 +2394,309 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-import-resolver-node/node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.13.0.tgz", + "integrity": "sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-html": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-8.1.4.tgz", + "integrity": "sha512-Eno3oPEj3s6AhvDJ5zHhnHPDvXp6LNFXuy3w51fNebOKYuTrfjOHUGwP+mOrGFpR6eOJkO1xkB8ivtbfMjbMjg==", + "dev": true, + "license": "ISC", + "dependencies": { + "htmlparser2": "^10.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -1038,6 +2706,70 @@ "node": ">=6" } }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1084,12 +2816,42 @@ "node": ">=0.10.0" } }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-levenshtein": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", @@ -1117,6 +2879,19 @@ "reusify": "^1.0.4" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1147,6 +2922,23 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/findup-sync": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", @@ -1187,6 +2979,28 @@ "node": ">= 10.13.0" } }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/follow-redirects": { "version": "1.15.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", @@ -1208,12 +3022,19 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/for-in": { @@ -1237,6 +3058,20 @@ "node": ">=0.10.0" } }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -1270,6 +3105,13 @@ "node": ">=10.13.0" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1293,6 +3135,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1303,16 +3176,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -1321,6 +3200,60 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1407,6 +3340,39 @@ "node": ">=0.10.0" } }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glogg": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/glogg/-/glogg-2.2.0.tgz", @@ -1420,12 +3386,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1437,6 +3404,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/gulp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.0.tgz", @@ -1455,6 +3429,55 @@ "node": ">=10.13.0" } }, + "node_modules/gulp-autoprefixer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-8.0.0.tgz", + "integrity": "sha512-sVR++PIaXpa81p52dmmA/jt50bw0egmylK5mjagfgOJ8uLDGaF9tHyzvetkY9Uo0gBZUS5sVqN3kX/GlUKOyog==", + "dev": true, + "license": "MIT", + "dependencies": { + "autoprefixer": "^10.2.6", + "fancy-log": "^1.3.3", + "plugin-error": "^1.0.1", + "postcss": "^8.3.0", + "through2": "^4.0.2", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "gulp": ">=4" + }, + "peerDependenciesMeta": { + "gulp": { + "optional": true + } + } + }, + "node_modules/gulp-clean-css": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.3.0.tgz", + "integrity": "sha512-mGyeT3qqFXTy61j0zOIciS4MkYziF2U594t2Vs9rUnpkEHqfu6aDITMp8xOvZcvdX61Uz3y1mVERRYmjzQF5fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-css": "4.2.3", + "plugin-error": "1.0.1", + "through2": "3.0.1", + "vinyl-sourcemaps-apply": "0.2.1" + } + }, + "node_modules/gulp-clean-css/node_modules/through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "2 || 3" + } + }, "node_modules/gulp-cli": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.0.0.tgz", @@ -1519,21 +3542,32 @@ "node": ">=10" } }, - "node_modules/gulp-sass": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.1.0.tgz", - "integrity": "sha512-7VT0uaF+VZCmkNBglfe1b34bxn/AfcssquLKVDYnCDJ3xNBaW7cUuI3p3BQmoKcoKFrs9jdzUxyb+u+NGfL4OQ==", + "node_modules/gulp-less": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-5.0.0.tgz", + "integrity": "sha512-W2I3TewO/By6UZsM/wJG3pyK5M6J0NYmJAAhwYXQHR+38S0iDtZasmUgFCH3CQj+pQYw/PAIzxvFvwtEXz1HhQ==", "dev": true, + "license": "MIT", "dependencies": { - "lodash.clonedeep": "^4.5.0", - "picocolors": "^1.0.0", - "plugin-error": "^1.0.1", + "less": "^3.7.1 || ^4.0.0", + "object-assign": "^4.0.1", + "plugin-error": "^1.0.0", "replace-ext": "^2.0.0", - "strip-ansi": "^6.0.1", - "vinyl-sourcemaps-apply": "^0.2.1" + "through2": "^4.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" }, "engines": { - "node": ">=12" + "node": ">=6" + } + }, + "node_modules/gulp-rename": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.1.0.tgz", + "integrity": "sha512-dGuzuH8jQGqCMqC544IEPhs5+O2l+IkdoSZsgd4kY97M1CxQeI3qrmweQBIrxLBbjbe/8uEWK8HHcNBc3OCy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/gulplog": { @@ -1548,6 +3582,19 @@ "node": ">= 10.13.0" } }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1570,10 +3617,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -1582,10 +3633,11 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1609,10 +3661,11 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1632,6 +3685,26 @@ "node": ">=0.10.0" } }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -1703,6 +3776,30 @@ } ] }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -1718,6 +3815,45 @@ "node": ">=0.10.0" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1730,6 +3866,21 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -1768,6 +3919,60 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1780,6 +3985,23 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -1793,12 +4015,48 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1840,6 +4098,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1876,6 +4150,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -1885,6 +4172,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1903,6 +4203,33 @@ "lodash.isfinite": "^3.3.2" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -1912,6 +4239,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", @@ -1924,13 +4270,78 @@ "node": ">=0.10.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -1960,6 +4371,65 @@ "node": ">=0.10.0" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -1978,6 +4448,13 @@ "node": ">=4" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2005,6 +4482,40 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -2014,6 +4525,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/last-run": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/last-run/-/last-run-2.0.0.tgz", @@ -2032,6 +4553,43 @@ "node": ">=10.13.0" } }, + "node_modules/less": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/less/-/less-4.6.4.tgz", + "integrity": "sha512-OJmO5+HxZLLw0RLzkqaNHzcgEAQG7C0y3aMbwtCzIUFZsLMNNq/1IdAdHEycQ58CwUO3jPTHmoN+tE5I7FQxNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^3.0.5", + "parse-node-version": "^1.0.1" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/level-supports": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", @@ -2054,6 +4612,20 @@ "node": ">=12" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lie": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", @@ -2096,24 +4668,67 @@ "lie": "3.1.1" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "dev": true - }, "node_modules/lodash.isfinite": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -2123,6 +4738,16 @@ "node": ">=0.10.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -2181,6 +4806,16 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mitt": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", @@ -2226,12 +4861,38 @@ "node": ">= 10.13.0" } }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/napi-macros": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", "dev": true }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/nedb-promises": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/nedb-promises/-/nedb-promises-6.2.3.tgz", @@ -2241,6 +4902,38 @@ "@seald-io/nedb": "^4.0.2" } }, + "node_modules/needle": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.5.0.tgz", + "integrity": "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -2250,6 +4943,25 @@ "node": ">= 0.6" } }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/node-gyp-build": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.3.tgz", @@ -2261,6 +4973,16 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2291,6 +5013,50 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -2306,6 +5072,56 @@ "node": ">=0.10.0" } }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -2318,6 +5134,25 @@ "node": ">=0.10.0" } }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2351,6 +5186,94 @@ "node": ">=4" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/optionator/node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -2365,6 +5288,16 @@ "node": ">=0.8" } }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -2383,6 +5316,36 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2411,10 +5374,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -2428,6 +5392,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", @@ -2458,14 +5433,79 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2554,6 +5594,50 @@ "node": ">= 10.13.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -2623,6 +5707,16 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/resolve-options": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", @@ -2658,12 +5752,73 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rx": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", "dev": true }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2684,41 +5839,63 @@ } ] }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/sass": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz", - "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==", + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "dev": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, + "license": "BlueOak-1.0.0", + "optional": true, "engines": { - "node": ">=14.0.0" + "node": ">=11.0.0" } }, - "node_modules/sass/node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", - "dev": true - }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "optional": true, "bin": { "semver": "bin/semver.js" } @@ -2965,12 +6142,142 @@ "node": ">= 0.4" } }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -3129,10 +6436,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -3155,6 +6463,20 @@ "node": ">= 0.6" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-composer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", @@ -3223,6 +6545,65 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3235,6 +6616,29 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3283,6 +6687,81 @@ "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", "dev": true }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3316,6 +6795,151 @@ "node": ">=0.6" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/ua-parser-js": { "version": "1.0.37", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", @@ -3339,6 +6963,25 @@ "node": "*" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -3396,6 +7039,47 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -3555,16 +7239,86 @@ "which": "bin/which" } }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.21.tgz", + "integrity": "sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==", + "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -3574,6 +7328,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -3662,6 +7426,19 @@ "engines": { "node": ">=12" } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index e622c14..0764af1 100644 --- a/package.json +++ b/package.json @@ -7,18 +7,34 @@ "test": "echo \"Error: no test specified\" && exit 1", "launch_Foundry12": "sudo node /home/rwan/foundry/foundry_software/FoundryVTT-12.331/resources/app/main.js", "watch": "gulp watch", - "buildStyle": "gulp buildStyles", + "build:less": "gulp buildLess", + "build:less:dev": "gulp buildLessDev", + "build:css": "gulp buildCSS", + "build:all-css": "gulp buildAllCSS", + "build": "npm run build:less", "dev": "npm run launch_Foundry12 & xdg-open http://localhost:30000/", + "clean:css": "rm -f css/vermine2047.*.css", + "rebuild:less": "npm run clean:css && npm run build:less", + "rebuild:css": "npm run clean:css && npm run build:all-css", "pushLDBtoYAML": "node ./tools/pushLDBtoYAML.mjs", - "pullYAMLtoLDB": "node ./tools/pullYAMLtoLDB.mjs" + "pullYAMLtoLDB": "node ./tools/pullYAMLtoLDB.mjs", + "lint:less": "lesshint less/**/*.less" }, "author": "Rwan", "devDependencies": { "@foundryvtt/foundryvtt-cli": "^1.0.2", + "@typescript-eslint/eslint-plugin": "^8.60.1", + "@typescript-eslint/parser": "^8.60.1", "@typhonjs-fvtt/eslint-config-foundry.js": "^0.8.0", "browser-sync": "^3.0.3", + "eslint": "^8.57.1", + "eslint-plugin-html": "^8.1.4", + "eslint-plugin-import": "^2.32.0", "gulp": "^5.0.0", - "gulp-sass": "^5.1.0", - "sass": "^1.55.0" + "gulp-autoprefixer": "^8.0.0", + "gulp-clean-css": "^4.3.0", + "gulp-less": "^5.0.0", + "gulp-rename": "^2.0.0", + "less": "^4.2.0" } -} \ No newline at end of file +} diff --git a/screenshot-initial.png b/screenshot-initial.png new file mode 100644 index 0000000..61b1b70 Binary files /dev/null and b/screenshot-initial.png differ diff --git a/screenshot-weapon.png b/screenshot-weapon.png new file mode 100644 index 0000000..b48d100 Binary files /dev/null and b/screenshot-weapon.png differ diff --git a/screenshots/item-sheet-weapon.png b/screenshots/item-sheet-weapon.png new file mode 100644 index 0000000..c7aac6c Binary files /dev/null and b/screenshots/item-sheet-weapon.png differ diff --git a/system.json b/system.json index f306c93..5249a1b 100644 --- a/system.json +++ b/system.json @@ -4,11 +4,13 @@ "description": "The Vermine 2047 system for FoundryVTT!", "version": "0.1.13", "compatibility": { - "minimum": "11", - "verified": "14.0", - "maximum": "14" + "minimum": "14", + "verified": "14" }, "authors": [ + { + "name": "LeRatierBretonnien/Uberwald" + }, { "name": "François-Xavier Guillois" }, @@ -25,8 +27,30 @@ "module/vermine2047.mjs" ], "styles": [ - "css/vermine2047.css" + "css/vermine2047.min.css" ], + "documentTypes": { + "Actor": { + "character": { "htmlFields": ["equipment.description", "identity.biography", "identity.relations"] }, + "npc": { "htmlFields": ["equipment.description", "identity.notes"] }, + "group": { "htmlFields": ["equipment.description", "identity.notes"] }, + "creature": { "htmlFields": ["equipment.description", "identity.notes", "identity.biography"] } + }, + "Item": { + "item": { "htmlFields": ["description"] }, + "weapon": { "htmlFields": ["description"] }, + "defense": { "htmlFields": ["description"] }, + "vehicle": { "htmlFields": ["description"] }, + "ability": { "htmlFields": ["description"] }, + "specialty": {}, + "background": { "htmlFields": ["description"] }, + "trauma": { "htmlFields": ["description"] }, + "evolution": { "htmlFields": ["description"] }, + "rumor": { "htmlFields": ["description"] }, + "target": { "htmlFields": ["description"] }, + "rite": { "htmlFields": ["description"] } + } + }, "languages": [ { "lang": "en", @@ -144,9 +168,9 @@ }, "primaryTokenAttribute": "health", "secondaryTokenAttribute": "power", - "url": "https://github.com/rwanoux/vermine2047/tree/main", - "manifest": "https://raw.githubusercontent.com/rwanoux/vermine2047/refs/heads/main/system.json", - "download": "https://github.com/rwanoux/vermine2047/archive/refs/heads/main.zip", + "url": "https://www.uberwald.me/gitea/uberwald/vermine2047", + "manifest": "https://www.uberwald.me/gitea/uberwald/vermine2047/raw/refs/heads/main/system.json", + "download": "https://www.uberwald.me/gitea/uberwald/vermine2047/archive/refs/heads/main.zip", "license": "LICENSE.txt", "changelog": "CHANGELOG.md", "flags": { diff --git a/templates/actor/actor-npc-sheet.hbs b/templates/actor/actor-npc-sheet.hbs index f8d38d9..5860907 100644 --- a/templates/actor/actor-npc-sheet.hbs +++ b/templates/actor/actor-npc-sheet.hbs @@ -186,18 +186,10 @@

{{ localize 'VERMINE.reserves' }}

- -
- - {{system.attributes.effort.value}}/{{system.attributes.effort.max}}D -
+ {{ localize 'VERMINE.effort' }} : {{system.attributes.effort.value}}/{{system.attributes.effort.max}}D
- -
- - {{system.attributes.self_control.value}}/{{system.attributes.self_control.max}}D -
+ {{ localize 'VERMINE.self_control' }} : {{system.attributes.self_control.value}}/{{system.attributes.self_control.max}}D
@@ -265,8 +257,8 @@

{{ localize 'ADVERSITY.experience' }}

  • - - + +
  • {{ localize 'ADVERSITY.action' }}: @@ -315,33 +307,6 @@ - {{!-- Blessures --}} -
    -

    {{ localize 'VERMINE.wounds' }}

    -
    -
    - -
    - - {{system.minorWound.value}}/{{system.minorWound.max}} -
    -
    -
    - -
    - - {{system.majorWound.value}}/{{system.majorWound.max}} -
    -
    -
    - -
    - - {{system.deadlyWound.value}}/{{system.deadlyWound.max}} -
    -
    -
    -
    {{!-- Equipment Tab --}} diff --git a/templates/actor/appv2/character-abilities.hbs b/templates/actor/appv2/character-abilities.hbs new file mode 100644 index 0000000..c272ed7 --- /dev/null +++ b/templates/actor/appv2/character-abilities.hbs @@ -0,0 +1,125 @@ +
    + {{> "systems/vermine2047/templates/actor/appv2/character-header.hbs"}} + {{!-- Character --}} +

    {{ localize 'VERMINE.abilities' }}

    +
    + {{#each config.abilityCategories as |abilityCategory ackey|}} +
    +

    {{ smarttl "ABILITY_CATEGORIES" ackey }}

    + {{#each @root.system.abilities as |ability key|}} + {{#if (eq ability.category ackey) }} +
    + + {{#if @root.isEditMode}} + + {{else}} + {{ability.value}} + {{/if}} +
    + {{/if}} + {{/each}} + +
    + {{/each}} +
    +

    {{ localize 'VERMINE.skills' }}

    +
    + {{#each system.skill_categories as |skillCategory sckey|}} + {{#if skillCategory.label}} +
    +

    {{ smarttl "SKILLS_CATEGORIES" sckey }} + {{#if @root.isEditMode}} + + {{/if}} +

    + {{#each @root.system.skills as |skill skey|}} + + {{#if (eq skill.category sckey) }} +
    + + {{#if @root.isEditMode}} +
    + {{#ifgt skill.value 1}} + + {{/ifgt}} + {{#each skill.specialties as |spe ind|}} + {{#ife spe.system.skill skey}} + {{spe.name}} + {{/ife}} + {{/each}} +
    + + {{/if}} + {{skill.value}} +
    + {{#repeat (skillLevel "dicePool" skill.value) 0 "dicepool"}} +
    + {{/repeat}} + {{#repeat (skillLevel "reroll" skill.value) 0 "dicereroll"}} +
    X
    + {{/repeat}} +
    + +
    + + {{/if}} + {{/each}} +
    + {{/if}} + {{/each}} +
    +
    diff --git a/templates/actor/appv2/character-combat.hbs b/templates/actor/appv2/character-combat.hbs new file mode 100644 index 0000000..6f18e10 --- /dev/null +++ b/templates/actor/appv2/character-combat.hbs @@ -0,0 +1,295 @@ +
    + {{> "systems/vermine2047/templates/actor/appv2/character-header.hbs"}} +
    +
    +

    situation de combat par défaut

    +
    + {{#each config.combatStatus as |dif label|}} +
    + + +
    + {{/each}} +
    +
    +
    +
    +
    +

    + + {{ localize "VERMINE.self_control"}} : + {{@root.system.attributes.self_control.value}}/ + {{@root.system.attributes.self_control.max}} +

    + {{setVar "SCIndex" 1}} +
    + {{#repeat 4 1 "row"}} + +
    + {{#repeat @row @root.SCIndex "case"}} +
    + {{#iflteq @row @root.system.attributes.self_control.max }} + {{#if @root.isEditMode}} + + {{/if}} + {{/iflteq}} + +
    + {{setVar "SCIndex" (math_add @root.SCIndex 1)}} + {{/repeat}} +
    + {{/repeat}} +
    +
    +
    +

    + + {{ localize "VERMINE.effort"}} : + {{@root.system.attributes.effort.value}}/ + {{@root.system.attributes.effort.max}} +

    + {{setVar "EffIndex" 1}} + +
    + {{#repeat 4 1 "row"}} + +
    + {{#repeat @row @root.EffIndex "case"}} +
    + {{#iflteq @row @root.system.attributes.effort.max }} + {{#if @root.isEditMode}} + + {{/if}} + {{/iflteq}} + +
    + {{setVar "EffIndex" (math_add @root.EffIndex 1)}} + {{/repeat}} +
    + {{/repeat}} +
    +
    +
    +

    {{ localize + "VERMINE.wounds.name"}}

    +
      +
    • {{ localize + 'VERMINE.wounds.light'}} ({{ + system.minorWound.threshold }}) +
      + {{#repeat system.minorWound.max 1 "minorwoundmax"}} +
      + {{#if @root.isEditMode}} + + {{/if}} +
      + {{/repeat}} +
      +
    • +
    • {{ localize + 'VERMINE.wounds.heavy'}} + ({{ + system.majorWound.threshold }}) +
      + {{#repeat system.majorWound.max 1 "majourwoundmax"}} +
      + {{#if @root.isEditMode}} + + {{/if}} + +
      + {{/repeat}} + +
      +
    • +
    • {{ localize + 'VERMINE.wounds.deadly'}} + ({{ + system.deadlyWound.threshold }}) +
      + {{#repeat system.deadlyWound.max 1 "deadlywoundmax"}} +
      + {{#if @root.isEditMode}} + + {{/if}} +
      + {{/repeat}} + +
      +
    • +
    +
    +
    + +

    {{ localize "UI.effects.name"}}

    +
      + {{#each effects as |section sid|}} +
    1. +

      {{#if (eq section.type + 'temporary')}} + {{localize "UI.effects.temporary" }} + {{else if (eq section.type 'passive')}} + {{localize "UI.effects.passive" }} + {{else if (eq section.type 'inactive')}} + {{localize "UI.effects.inactive" }} + {{/if}} +

      +
      {{localize 'UI.source'}}
      +
      {{localize 'UI.duration'}}
      +
      + {{#if @root.isEditMode}} + + {{localize "UI.add"}} + + {{/if}} +
      +
    2. + +
        + {{#each section.effects as |effect|}} +
      1. + +
        {{effect.sourceName}}
        +
        {{effect.duration.label}}
        +
        + {{#if @root.isEditMode}} + + + + + + + {{/if}} +
        +
      2. + {{/each}} +
      + {{/each}} +
    +
    diff --git a/templates/actor/appv2/character-equipment.hbs b/templates/actor/appv2/character-equipment.hbs new file mode 100644 index 0000000..9b5c8fc --- /dev/null +++ b/templates/actor/appv2/character-equipment.hbs @@ -0,0 +1,100 @@ +
    + {{> "systems/vermine2047/templates/actor/appv2/character-header.hbs"}} +
    +
    +
      +
    1. +
      {{ localize + 'IDENTITY.name'}}
      +
      {{ localize 'VERMINE.qty'}}
      +
      {{ localize 'VERMINE.weight'}}
      +
      + {{#if isEditMode}} + + {{/if}} +
      +
    2. + {{#each gear as |item id|}} +
    3. +
      +
      + +
      + {{item.name}} + + {{#if item.system.damages.value}} + / + {{{item.damagedIcon}}} + {{/if}} + +
      + + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    4. + {{/each}} +
    +
    +
    +

    {{ localize 'IDENTITY.notes'}}

    + {{editor system.equipment.description target="system.equipment.description" + button=true owner=owner editable=editable}} +
    +
    +
    +
    +

    {{localize 'ITEMS.weapons'}}

    + {{> "systems/vermine2047/templates/actor/parts/actor-weapons.hbs"}} +
    +
    +

    {{localize 'ITEMS.defenses'}}

    + {{> "systems/vermine2047/templates/actor/parts/actor-defenses.hbs"}} +
    +
    +
    diff --git a/templates/actor/appv2/character-header.hbs b/templates/actor/appv2/character-header.hbs new file mode 100644 index 0000000..a52cd48 --- /dev/null +++ b/templates/actor/appv2/character-header.hbs @@ -0,0 +1,62 @@ +{{!-- HEADER --}} +
    +
    +

    + + {{#if isEditMode}} + + {{else}} + {{actor.name}} + {{/if}} +

    +
    + + {{#if isEditMode}} + + {{else}} + {{system.identity.profile}} + {{/if}} +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{ system.identity.age }} + {{/if}} + ({{ ageType "name" system.identity.ageType }}) +
    +
    +
    +
    +

    + + {{#if (eq system.identity.totem "")}} + Choisissez votre totem + {{ else }} + {{ smarttl "TOTEMS" system.identity.totem }} + {{/if}} +

    +
    + + {{#if isEditMode}} + + {{else}} + {{system.attributes.reputation.value}} + {{/if}} + + + {{#if isEditMode}} + + {{else}} + {{system.attributes.xp.value}} + {{/if}} +
    +
    +
    diff --git a/templates/actor/appv2/character-main.hbs b/templates/actor/appv2/character-main.hbs new file mode 100644 index 0000000..b9e07cc --- /dev/null +++ b/templates/actor/appv2/character-main.hbs @@ -0,0 +1,140 @@ +
    + {{!-- Toggle Edit/Play --}} +
    + {{#if isEditMode}} + + {{else}} + + {{/if}} +
    + + {{!-- Logo --}} + + + {{!-- Sidebar: Profile Image + Totem Dice --}} +
    + +
    +
    + {{!-- Totem Evolution --}} +

    {{localize "ITEMS.evolution"}}

    +
    {{localize "TOTEMS.human.name"}} + +
    + +
    {{localize "TOTEMS.adapted.name"}} + +
    + +
    +
    + {{#repeat system.adaptation.totems.human.max 1 "humantotem"}} +
    + {{#if @root.isEditMode}} + + {{/if}} + {{#ifgteq system.adaptation.totems.human.value @humantotem}} +
    + {{/ifgteq}} +
    + {{/repeat}} +
    + +
    + {{#repeat system.adaptation.totems.adapted.max 1 "adaptedtotem"}} +
    + {{#if @root.isEditMode}} + + {{/if}} + {{#ifgteq system.adaptation.totems.adapted.value @adaptedtotem}} +
    + {{/ifgteq}} +
    + {{/repeat}} +
    +
    + +
    +
      + {{#if system.identity.totem }} +
    • +
      +

      {{ smarttl 'TOTEMS' system.identity.totem 'name' }}

      + {{ smarttl 'TOTEMS' system.identity.totem 'name' }} +
      +
    • + {{/if}} + {{#if system.identity.theme}} +
    • +
      + +

      {{{ system.identity.theme }}}

      +
      +
    • + {{/if}} + {{#if system.identity.instincts}} +
    • +
      + +

      {{{ system.identity.instincts }}}

      +
      +
    • + {{/if}} + {{#if system.identity.prohibits}} +
    • +
      + +

      {{{ system.identity.prohibits }}}

      +
      +
    • + {{/if}} + {{#if system.identity.objectives}} +
    • +
      + +

      {{{ system.identity.objectives }}}

      +
      +
    • + {{/if}} + {{#if system.identity.relations}} +
    • +
      + + {{{ system.identity.relations }}} +
      +
    • + {{/if}} +
    + +
    diff --git a/templates/actor/appv2/character-stories.hbs b/templates/actor/appv2/character-stories.hbs new file mode 100644 index 0000000..89bf48a --- /dev/null +++ b/templates/actor/appv2/character-stories.hbs @@ -0,0 +1,49 @@ +
    + {{> "systems/vermine2047/templates/actor/appv2/character-header.hbs"}} +
    +
    +

    {{ localize 'IDENTITY.theme'}}

    + {{#if isEditMode}} + + {{else}} +

    {{{ system.identity.theme }}}

    + {{/if}} +

    {{ localize 'IDENTITY.objectives'}}

    + {{#if isEditMode}} + + {{else}} +

    {{{ system.identity.objectives }}}

    + {{/if}} +
    +
    +

    {{ localize 'IDENTITY.instincts'}}

    + {{#if isEditMode}} + + {{else}} +

    {{{ system.identity.instincts }}}

    + {{/if}} +

    {{ localize 'IDENTITY.prohibits'}}

    + {{#if isEditMode}} + + {{else}} +

    {{{ system.identity.prohibits }}}

    + {{/if}} +
    +
    +
    +
    +

    {{ localize 'IDENTITY.notes'}}

    + {{editor system.identity.biography target="system.identity.biography" + button=true owner=owner editable=editable}} +
    +
    +

    {{ localize 'IDENTITY.relations'}}

    + {{editor system.identity.relations target="system.identity.relations" + button=true owner=owner editable=editable}} +
    +
    +
    diff --git a/templates/actor/appv2/character-totem.hbs b/templates/actor/appv2/character-totem.hbs new file mode 100644 index 0000000..f40b1f4 --- /dev/null +++ b/templates/actor/appv2/character-totem.hbs @@ -0,0 +1,222 @@ +
    + {{> "systems/vermine2047/templates/actor/appv2/character-header.hbs"}} + {{#if system.identity.totem}} +
    + {{smarttl 'TOTEMS' system.identity.totem 'name'}} +

    + {{smarttl "TOTEMS" system.identity.totem "name"}} +

    +

    {{smarttl "TOTEMS" system.identity.totem "description"}}

    +
    +
    +
    Principes
    {{smarttl "TOTEMS" system.identity.totem "instincts"}}
    +
    +
    +
    Interdits
    {{smarttl "TOTEMS" system.identity.totem "bans"}}
    +
    +
    +
    +
    + + {{else}} + +

    choisissez un totem

    + + {{/if}} + + {{!-- Abstract Items --}} +
    +
    +

    + {{localize "ITEMS.abilities"}} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each abilities as |item id|}} +
    1. +
      + {{item.name}} + {{item.system.level.value}} + + {{item.system.learn.threshold}}/{{item.system.learn.hindrance}} + +
      +
      + {{#if item.system.description}} + + {{/if}} + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    +
    +

    + {{localize "ITEMS.specialties"}} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each specialties as |item id|}} +
    1. +
      + {{item.name}} + {{item.system.skill}} +
      +
      + {{#if item.system.description}} + + {{/if}} + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    +
    +

    + {{localize "ITEMS.backgrounds"}} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each backgrounds as |item id|}} +
    1. + +
      + {{#if item.system.description}} + + {{/if}} + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    +
    +

    + {{localize "ITEMS.traumas"}} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each traumas as |item id|}} +
    1. + +
      + {{#if item.system.description}} + + {{/if}} + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    +
    +

    + {{localize "ITEMS.evolutions"}} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each evolutions as |item id|}} +
    1. + +
      + {{#if item.system.description}} + + {{/if}} + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    +
    +
    diff --git a/templates/actor/appv2/creature-combat.hbs b/templates/actor/appv2/creature-combat.hbs new file mode 100644 index 0000000..6c8433d --- /dev/null +++ b/templates/actor/appv2/creature-combat.hbs @@ -0,0 +1,75 @@ +
    +
    + {{!-- Computed Values --}} +
    +

    {{ localize 'VERMINE.computed_values' }}

    +
    +
    + + {{ system.computed.attack }} +
    +
    + + {{ system.computed.damage }} +
    +
    + + {{ system.computed.vigor }} +
    +
    + + {{ system.computed.reaction }} +
    +
    + + {{ system.computed.pools }} +
    +
    + + {{ system.computed.protection }} +
    +
    +
    + + {{!-- Wounds --}} +
    +

    {{ localize "VERMINE.wounds.name" }}

    +
      +
    • + {{ localize 'VERMINE.wounds.light' }} ({{ system.minorWound.threshold }}D) + {{#repeat system.minorWound.max }} + + {{/repeat}} +
    • +
    • + {{ localize 'VERMINE.wounds.heavy' }} ({{ system.majorWound.threshold }}D) + {{#repeat system.majorWound.max }} + + {{/repeat}} +
    • +
    • + {{ localize 'VERMINE.wounds.deadly' }} ({{ system.deadlyWound.threshold }}D) + {{#repeat system.deadlyWound.max }} + + {{/repeat}} +
    • +
    +
    +
    + + {{!-- Equipment --}} +
    +

    {{ localize 'VERMINE.gear' }}

    + {{editor system.equipment.description target="system.equipment.description" button=true owner=owner editable=editable}} +
    +
    diff --git a/templates/actor/appv2/creature-effects.hbs b/templates/actor/appv2/creature-effects.hbs new file mode 100644 index 0000000..5e27d31 --- /dev/null +++ b/templates/actor/appv2/creature-effects.hbs @@ -0,0 +1,51 @@ +
    +

    {{ localize "UI.effects.name" }}

    +
      + {{#each effects as |section sid|}} +
    1. +

      {{#if (eq section.type 'temporary')}} + {{localize "UI.effects.temporary" }} + {{else if (eq section.type 'passive')}} + {{localize "UI.effects.passive" }} + {{else if (eq section.type 'inactive')}} + {{localize "UI.effects.inactive" }} + {{/if}} +

      +
      {{localize 'UI.source'}}
      +
      {{localize 'UI.duration'}}
      +
      + {{#if @root.isEditMode}} + + {{localize "UI.add"}} + + {{/if}} +
      +
    2. + +
        + {{#each section.effects as |effect|}} +
      1. + +
        {{effect.sourceName}}
        +
        {{effect.duration.label}}
        +
        + {{#if @root.isEditMode}} + + + + + + + {{/if}} +
        +
      2. + {{/each}} +
      + {{/each}} +
    +
    diff --git a/templates/actor/appv2/creature-info.hbs b/templates/actor/appv2/creature-info.hbs new file mode 100644 index 0000000..1ea02e7 --- /dev/null +++ b/templates/actor/appv2/creature-info.hbs @@ -0,0 +1,73 @@ +
    +
    +
    +

    {{ localize 'IDENTITY.profile' }}

    + {{#if isEditMode}} + + {{else}} + {{system.identity.profile}} + {{/if}} +
    +
    +

    {{ localize 'IDENTITY.origin' }}

    + {{#if isEditMode}} + + {{else}} + {{system.identity.origin}} + {{/if}} +
    +
    +

    {{ localize 'IDENTITY.theme' }}

    + {{#if isEditMode}} + + {{else}} + {{system.identity.theme}} + {{/if}} +
    +
    +

    {{ localize 'ADVERSITY.skills' }}

    + {{#if isEditMode}} + + {{else}} + {{system.skills}} + {{/if}} +
    +
    + +
    +

    {{ localize 'IDENTITY.notes' }}

    + {{editor system.identity.notes target="system.identity.notes" button=true owner=owner editable=editable}} +
    + + {{!-- Modes --}} +
    +

    {{ localize 'VERMINE.modes' }}

    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    diff --git a/templates/actor/appv2/creature-main.hbs b/templates/actor/appv2/creature-main.hbs new file mode 100644 index 0000000..6fc8e9b --- /dev/null +++ b/templates/actor/appv2/creature-main.hbs @@ -0,0 +1,102 @@ +
    + {{!-- Toggle Edit/Play --}} +
    + {{#if isEditMode}} + + {{else}} + + {{/if}} +
    + + {{!-- Sheet Header --}} +
    + +
    +
    +

    + {{#if isEditMode}} + + {{else}} + {{actor.name}} + {{/if}} +

    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.pattern.value}} + {{/if}} +
    +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.role.value}} + {{/if}} +
    +
    +
    +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.size.value}} + {{/if}} +
    +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.pack.value}} + {{/if}} +
    +
    +
    + +
    + {{ localize 'VERMINE.total_attack' }}: {{ system.computed.attack }} + {{ localize 'ADVERSITY.damage' }}: {{ system.computed.damage }} +
    +
    +
    +
    +
    +
    diff --git a/templates/actor/appv2/creature-stats.hbs b/templates/actor/appv2/creature-stats.hbs new file mode 100644 index 0000000..4d097d4 --- /dev/null +++ b/templates/actor/appv2/creature-stats.hbs @@ -0,0 +1,61 @@ +
    +

    {{ localize 'VERMINE.base_values' }}

    +
    +
    +

    {{ localize 'ADVERSITY.pattern' }}: {{ patternLabel }}

    +
      +
    • {{ localize 'ADVERSITY.attack' }}: {{ creaturePatternLevel "attack" system.pattern.value }}
    • +
    • {{ localize 'ADVERSITY.damage' }}: {{ creaturePatternLevel "damage" system.pattern.value }}
    • +
    • {{ localize 'ADVERSITY.wounds' }}: + {{ creaturePatternLevel "minorWound" system.pattern.value }}/ + {{ creaturePatternLevel "majorWound" system.pattern.value }}/ + {{ creaturePatternLevel "deadlyWound" system.pattern.value }} +
    • +
    +
    +
    +

    {{ localize 'ADVERSITY.size' }}: {{ sizeLabel }}

    +
      +
    • {{ localize 'ADVERSITY.attack' }}: {{ creatureSizeLevel "attack" system.size.value }}
    • +
    • {{ localize 'ADVERSITY.vigor' }}: {{ creatureSizeLevel "vigor" system.size.value }}
    • +
    • {{ localize 'ADVERSITY.wounds' }}: + {{ creatureSizeLevel "minorWound" system.size.value }}/ + {{ creatureSizeLevel "majorWound" system.size.value }}/ + {{ creatureSizeLevel "deadlyWound" system.size.value }} +
    • +
    +
    +
    +

    {{ localize 'ADVERSITY.role' }}: {{ roleLabel }}

    +
      +
    • {{ localize 'ADVERSITY.reaction' }}: {{ creatureRoleLevel "reaction" system.role.value }} + {{ creatureRoleLevel "reaction_bonus" system.role.value }}
    • +
    • {{ localize 'ADVERSITY.pools' }}: {{ creatureRoleLevel "pools" system.role.value }}
    • +
    • {{ localize 'ADVERSITY.gear' }}: {{ creatureRoleLevel "gear" system.role.value }}
    • +
    • {{ localize 'ADVERSITY.protection' }}: {{ creatureRoleLevel "protection" system.role.value }}
    • +
    +
    +
    + +
    +
    +

    {{ localize 'ADVERSITY.pack' }}: {{ packLabel }}

    +
      +
    • {{ localize 'ADVERSITY.attack' }}: {{ creaturePackLevel "attack" system.pack.value }}
    • +
    • {{ localize 'ADVERSITY.damage' }}: {{ creaturePackLevel "damage" system.pack.value }}
    • +
    • {{ localize 'ADVERSITY.wounds' }}: + {{ creaturePackLevel "minorWound" system.pack.value }}/ + {{ creaturePackLevel "majorWound" system.pack.value }}/ + {{ creaturePackLevel "deadlyWound" system.pack.value }} +
    • +
    +
    +
    +

    {{ localize 'VERMINE.wound_thresholds' }}

    +
      +
    • {{ localize 'VERMINE.wounds.light' }}: {{ system.minorWound.threshold }}D
    • +
    • {{ localize 'VERMINE.wounds.heavy' }}: {{ system.majorWound.threshold }}D
    • +
    • {{ localize 'VERMINE.wounds.deadly' }}: {{ system.deadlyWound.threshold }}D
    • +
    +
    +
    +
    diff --git a/templates/actor/appv2/group-gear.hbs b/templates/actor/appv2/group-gear.hbs new file mode 100644 index 0000000..08a9758 --- /dev/null +++ b/templates/actor/appv2/group-gear.hbs @@ -0,0 +1,45 @@ +
    +
    +
    +
      +
    1. +
      {{ localize 'IDENTITY.name'}}
      +
      {{ localize 'VERMINE.qty'}}
      +
      {{ localize 'VERMINE.weight'}}
      +
      + {{#if isEditMode}} + + {{/if}} +
      +
    2. + {{#each gear as |item id|}} +
    3. +
      +
      + +
      + {{item.name}} +
      + + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    4. + {{/each}} +
    +
    +
    +

    {{ localize 'IDENTITY.notes'}}

    + {{editor system.equipment.description target="system.equipment.description" button=true owner=owner editable=editable}} +
    +
    +

    {{localize 'ITEMS.weapons'}}

    + {{> "systems/vermine2047/templates/actor/parts/actor-weapons.hbs"}} +

    {{localize 'ITEMS.defenses'}}

    + {{> "systems/vermine2047/templates/actor/parts/actor-defenses.hbs"}} +

    {{localize 'ITEMS.vehicles'}}

    + {{> "systems/vermine2047/templates/actor/group/group-vehicles.hbs"}} +
    diff --git a/templates/actor/appv2/group-info.hbs b/templates/actor/appv2/group-info.hbs new file mode 100644 index 0000000..24e04ea --- /dev/null +++ b/templates/actor/appv2/group-info.hbs @@ -0,0 +1,157 @@ +
    +
    + {{!-- Group Identity --}} + +
    +

    {{ localize 'IDENTITY.origin' }}

    +
    + {{#if isEditMode}} + + {{else}} + {{system.identity.origin}} + {{/if}} +
    +
    + +
    +

    {{ localize 'IDENTITY.theme' }}

    +
    + {{#if isEditMode}} + + {{else}} + {{system.identity.theme}} + {{/if}} +
    +
    + + {{!-- Group Notes --}} +
    +

    {{ localize 'IDENTITY.notes' }}

    + {{editor system.identity.notes target="system.identity.notes" button=true owner=owner editable=editable}} +
    + + {{!-- Group Abilities (from items) --}} +
    +

    + {{ localize 'VERMINE.abilities' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each abilities as |item id|}} + {{#if (ne item.type 'totem')}} +
    1. + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/if}} + {{/each}} +
    +
    + + {{!-- Specialties --}} +
    +

    + {{ localize 'ITEMS.specialties' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each specialties as |item id|}} +
    1. + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    + + {{!-- Backgrounds --}} +
    +

    + {{ localize 'ITEMS.backgrounds' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each backgrounds as |item id|}} +
    1. + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    + + {{!-- Traumas --}} +
    +

    + {{ localize 'ITEMS.traumas' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each traumas as |item id|}} +
    1. + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    + + {{!-- Evolutions --}} +
    +

    + {{ localize 'ITEMS.evolutions' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each evolutions as |item id|}} +
    1. + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    +
    +
    +
    diff --git a/templates/actor/appv2/group-main.hbs b/templates/actor/appv2/group-main.hbs new file mode 100644 index 0000000..7c06b44 --- /dev/null +++ b/templates/actor/appv2/group-main.hbs @@ -0,0 +1,68 @@ +
    + {{!-- Toggle Edit/Play --}} +
    + {{#if isEditMode}} + + {{else}} + + {{/if}} +
    + + {{!-- HEADER --}} +
    +
    + +
    +
    +

    + + {{#if isEditMode}} + + {{else}} + {{actor.name}} + {{/if}} +

    +
    + + {{#if isEditMode}} + + {{else}} + {{system.identity.profile}} + {{/if}} +
    +
    +
    +

    + + {{#if (eq system.identity.totem "")}} + Choisissez… + {{ else }} + {{ smarttl "TOTEMS" system.identity.totem }} + {{/if}} +

    +
    + + {{#if isEditMode}} + + {{else}} + {{system.level.value}} + {{/if}} + / {{system.level.max}} +
    +
    + + {{#if isEditMode}} + + {{else}} + {{system.reputation.value}} + {{/if}} + / {{system.reputation.max}} +
    +
    +
    +
    diff --git a/templates/actor/appv2/group-reserve.hbs b/templates/actor/appv2/group-reserve.hbs new file mode 100644 index 0000000..2de5d41 --- /dev/null +++ b/templates/actor/appv2/group-reserve.hbs @@ -0,0 +1,220 @@ +
    +
    + {{!-- Group Reserve and Morale --}} +
    +

    {{ localize 'VERMINE.reserve' }}

    +
    + + {{#if isEditMode}} + + {{else}} + {{system.reserve.value}} + {{/if}} + / {{system.reserve.max}}D +
    +
    + + {{#if isEditMode}} + + {{else}} + {{system.morale.level}} + {{/if}} + {{#if isEditMode}} + + {{else}} + {{system.morale.value}} + {{/if}} + / {{system.morale.max}}D +
    +
    + + {{!-- Group Members --}} +
    +

    + {{ localize 'VERMINE.group_members' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each system.members as |memberId|}} + {{#with (lookup ../resolvedMembers memberId) as |member|}} +
    1. +
      + {{#if member}} + {{member.name}} + {{else}} + ID: {{memberId}} + {{/if}} +
      +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/with}} + {{/each}} +
    +
    + + {{!-- Group Encounters --}} +
    +

    + {{ localize 'VERMINE.encounters' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each system.encounters as |encounterId|}} + {{#with (lookup ../resolvedEncounters encounterId) as |encounter|}} +
    1. +
      + {{#if encounter}} + {{encounter.name}} + {{else}} + ID: {{encounterId}} + {{/if}} +
      +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/with}} + {{/each}} +
    +
    + + {{!-- Group Objectives --}} +
    +

    {{ localize 'VERMINE.objectives' }}

    + +
    +
    {{ localize 'VERMINE.major_objectives' }}
    +
      + {{#each system.objectives.major as |objective id|}} +
    1. +
      + {{#if @root.isEditMode}} + + {{else}} + {{objective}} + {{/if}} +
      +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    + {{#if isEditMode}} + {{ localize 'ITEMS.new_objective' }} + {{/if}} +
    + +
    +
    {{ localize 'VERMINE.minor_objectives' }}
    +
      + {{#each system.objectives.minor as |objective id|}} +
    1. +
      + {{#if @root.isEditMode}} + + {{else}} + {{objective}} + {{/if}} +
      +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/each}} +
    + {{#if isEditMode}} + {{ localize 'ITEMS.new_objective' }} + {{/if}} +
    +
    + + {{!-- Group Abilities --}} +
    +

    + {{ localize 'VERMINE.group_abilities' }} + {{#if isEditMode}} + + {{/if}} +

    +
      + {{#each abilities as |item id|}} + {{#if (ne item.type 'totem')}} +
    1. + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    2. + {{/if}} + {{/each}} +
    +
    + + {{!-- Totem Section --}} +
    +

    {{ localize 'IDENTITY.totem' }}

    +
    + + {{#if isEditMode}} + + {{else}} + {{system.identity.instincts}} + {{/if}} +
    +
    + + {{#if isEditMode}} + + {{else}} + {{system.identity.prohibits}} + {{/if}} +
    +
    + {{#if (eq system.identity.totem "")}} + {{ localize 'VERMINE.totem_picker' }} + {{else}} +
    + {{ smarttl "TOTEMS" system.identity.totem }} + {{ localize 'UI.effect_edit' }} +
    + {{/if}} +
    +
    +
    +
    diff --git a/templates/actor/appv2/group-road.hbs b/templates/actor/appv2/group-road.hbs new file mode 100644 index 0000000..678551d --- /dev/null +++ b/templates/actor/appv2/group-road.hbs @@ -0,0 +1,37 @@ +
    +
      +
    1. +
      {{ localize 'IDENTITY.name'}}
      +
      {{ localize 'VERMINE.mobility'}}
      +
      {{ localize 'VERMINE.rarity'}}
      +
      {{ localize 'VERMINE.reliability'}}
      +
      + {{#if isEditMode}} + + {{/if}} +
      +
    2. + {{#each vehicles as |item id|}} +
    3. +
      +
      + +
      + {{item.name}} +
      + + + +
      + {{#if @root.isEditMode}} + + {{/if}} +
      +
    4. + {{/each}} +
    +
    diff --git a/templates/actor/appv2/npc-characteristics.hbs b/templates/actor/appv2/npc-characteristics.hbs new file mode 100644 index 0000000..a8250e6 --- /dev/null +++ b/templates/actor/appv2/npc-characteristics.hbs @@ -0,0 +1,116 @@ +
    +

    {{ localize 'VERMINE.abilities' }}

    + + {{!-- Caracteristiques physiques --}} +
    +

    {{ localize 'VERMINE.ability_category.physical' }}

    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.vigor.value}}D +
    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.health.value}}D +
    +
    +
    +
    + + {{!-- Caracteristiques manuelles --}} +
    +

    {{ localize 'VERMINE.ability_category.manual' }}

    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.precision.value}}D +
    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.reflexes.value}}D +
    +
    +
    +
    + + {{!-- Caracteristiques mentales --}} +
    +

    {{ localize 'VERMINE.ability_category.mental' }}

    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.knowledge.value}}D +
    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.perception.value}}D +
    +
    +
    +
    + + {{!-- Caracteristiques sociales --}} +
    +

    {{ localize 'VERMINE.ability_category.social' }}

    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.will.value}}D +
    +
    +
    + +
    + {{#if isEditMode}} + + {{/if}} + {{system.abilities.empathy.value}}D +
    +
    +
    +
    + + {{!-- Reserves --}} +
    +

    {{ localize 'VERMINE.reserves' }}

    +
    +
    + {{ localize 'VERMINE.effort' }} : {{system.attributes.effort.value}}/{{system.attributes.effort.max}}D +
    +
    + {{ localize 'VERMINE.self_control' }} : {{system.attributes.self_control.value}}/{{system.attributes.self_control.max}}D +
    +
    +
    +
    diff --git a/templates/actor/appv2/npc-combat.hbs b/templates/actor/appv2/npc-combat.hbs new file mode 100644 index 0000000..de167d5 --- /dev/null +++ b/templates/actor/appv2/npc-combat.hbs @@ -0,0 +1,110 @@ +
    +
    +
    +

    {{ localize "VERMINE.self_control"}}

    +

    + {{#if isEditMode}} + + {{/if}} + {{ system.attributes.self_control.value }} / {{ + system.attributes.self_control.max }} +

    +
    +
    +

    {{ localize "VERMINE.effort"}}

    +

    + {{#if isEditMode}} + + {{/if}} + {{ system.attributes.effort.value }} / {{ + system.attributes.effort.max }} +

    +
    +
    +

    {{ localize + "VERMINE.wounds.name"}}

    +
      +
    • {{ localize 'VERMINE.wounds.light'}} ({{ + system.minorWound.threshold }}) + {{#repeat system.minorWound.max }} + + {{/repeat}}
    • +
    • {{ localize 'VERMINE.wounds.heavy'}} ({{ + system.majorWound.threshold }}) + {{#repeat system.majorWound.max }} + + {{/repeat}} +
    • +
    • {{ localize 'VERMINE.wounds.deadly'}} ({{ + system.deadlyWound.threshold }}) + {{#repeat system.deadlyWound.max }} + + {{/repeat}} +
    • +
    +

    {{ localize "UI.effects.name"}}

    +
      + {{#each effects as |section sid|}} +
    1. +

      {{#if (eq section.type + 'temporary')}} + {{localize "UI.effects.temporary" }} + {{else if (eq section.type 'passive')}} + {{localize "UI.effects.passive" }} + {{else if (eq section.type 'inactive')}} + {{localize "UI.effects.inactive" }} + {{/if}} +

      +
      {{localize 'UI.source'}}
      +
      {{localize 'UI.duration'}}
      +
      + {{#if @root.isEditMode}} + + {{localize "UI.add"}} + + {{/if}} +
      +
    2. + +
        + {{#each section.effects as |effect|}} +
      1. + +
        {{effect.sourceName}}
        +
        {{effect.duration.label}}
        +
        + {{#if @root.isEditMode}} + + + + + + + {{/if}} +
        +
      2. + {{/each}} +
      + {{/each}} +
    +
    diff --git a/templates/actor/appv2/npc-main.hbs b/templates/actor/appv2/npc-main.hbs new file mode 100644 index 0000000..eb917e3 --- /dev/null +++ b/templates/actor/appv2/npc-main.hbs @@ -0,0 +1,116 @@ +
    + {{!-- Toggle Edit/Play --}} +
    + {{#if isEditMode}} + + {{else}} + + {{/if}} +
    + + {{!-- Sheet Header --}} +
    + +
    +

    + {{#if isEditMode}} + + {{else}} + {{actor.name}} + {{/if}} +

    + + {{!-- Niveaux de Menace, Experience, Role --}} +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.threat.value}} + {{/if}} +
    +
    + +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.experience.value}} + {{/if}} +
    +
    + +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.role.value}} + {{/if}} +
    +
    +
    + + {{!-- Totem et Origine --}} +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.identity.totem}} + {{/if}} +
    +
    +
    + +
    + {{#if isEditMode}} + + {{else}} + {{system.identity.origin}} + {{/if}} +
    +
    +
    +
    +
    +
    diff --git a/templates/actor/appv2/npc-notes.hbs b/templates/actor/appv2/npc-notes.hbs new file mode 100644 index 0000000..81f1ff9 --- /dev/null +++ b/templates/actor/appv2/npc-notes.hbs @@ -0,0 +1,22 @@ +
    +

    {{ localize 'IDENTITY.notes' }}

    +
    +
    + + {{#if isEditMode}} + + {{else}} + {{system.identity.profile}} + {{/if}} +
    +
    + + {{#if isEditMode}} + + {{else}} + {{system.identity.theme}} + {{/if}} +
    +
    + {{editor system.identity.notes target="system.identity.notes" button=true owner=owner editable=editable}} +
    diff --git a/templates/actor/appv2/npc-skills.hbs b/templates/actor/appv2/npc-skills.hbs new file mode 100644 index 0000000..7260e37 --- /dev/null +++ b/templates/actor/appv2/npc-skills.hbs @@ -0,0 +1,44 @@ +
    +

    {{ localize 'VERMINE.skills' }}

    + + {{!-- Category Selector --}} +
    + + {{#if isEditMode}} + + {{else}} + {{system.skill_categories.preferred}} + {{/if}} +
    + + {{!-- Skills Grid by Category --}} +
    + {{#each config.skillCategories as |category key|}} + {{#ife key "preferred"}}{{else}} + {{> "systems/vermine2047/templates/actor/parts/npc-skill-category.hbs" + categoryKey=key + categoryLabel=(concat "VERMINE.skill_category." key) + }} + {{/ife}} + {{/each}} +
    + + {{!-- Free Skills text field --}} +
    + + {{#if isEditMode}} + + {{else}} + {{system.freeSkills}} + {{/if}} +
    +
    diff --git a/templates/actor/appv2/npc-threat.hbs b/templates/actor/appv2/npc-threat.hbs new file mode 100644 index 0000000..149f3b1 --- /dev/null +++ b/templates/actor/appv2/npc-threat.hbs @@ -0,0 +1,75 @@ +
    +

    {{ localize 'ADVERSITY.threat_details' }}

    +
    + {{!-- Menace --}} +
    +

    {{ localize 'ADVERSITY.threat' }}

    +
      +
    • + {{ localize 'ADVERSITY.attack' }}: + {{ npcThreatLevel "attack" system.threat.value }} +
    • +
    • + {{ localize 'ADVERSITY.vigor' }}: + {{ npcThreatLevel "vigor" system.threat.value }} +
    • +
    • + {{ localize 'ADVERSITY.wounds' }}: + {{ npcThreatLevel "minorWound" system.threat.value }} / + {{ npcThreatLevel "majorWound" system.threat.value }} / + {{ npcThreatLevel "deadlyWound" system.threat.value }} +
    • +
    +
    + + {{!-- Experience --}} +
    +

    {{ localize 'ADVERSITY.experience' }}

    +
      +
    • + {{ localize 'ADVERSITY.action' }}: + {{ npcExperienceLevel "action" system.experience.value }}D +
    • +
    • + {{ localize 'ADVERSITY.specialties' }}: + {{ npcExperienceLevel "specialties" system.experience.value }} +
    • +
    • + {{ localize 'ADVERSITY.rerolls' }}: + {{ npcExperienceLevel "rerolls" system.experience.value }}D +
    • +
    • + {{ localize 'ADVERSITY.contact' }}: + {{ npcExperienceLevel "contact" system.experience.value }} +
    • +
    +
    + + {{!-- Role --}} +
    +

    {{ localize 'ADVERSITY.role' }}

    +
      +
    • + {{ localize 'ADVERSITY.reaction' }}: + {{ npcRoleLevel "reaction" system.role.value }} + {{ npcRoleLevel "reaction_bonus" system.role.value }} +
    • +
    • + {{ localize 'ADVERSITY.pools' }}: + {{ npcRoleLevel "pools" system.role.value }}D +
    • +
    • + {{ localize 'ADVERSITY.gear' }}: + {{ npcRoleLevel "gear" system.role.value }} +
    • +
    • + {{ localize 'ADVERSITY.gear_hindrance' }}: + {{ npcRoleLevel "gear_hindrance" system.role.value }} +
    • +
    • + {{ localize 'ADVERSITY.protection' }}: + {{ npcRoleLevel "protection" system.role.value }} +
    • +
    +
    +
    +
    diff --git a/templates/actor/character/character-header.hbs b/templates/actor/character/character-header.hbs index 33a22c3..7d546e9 100644 --- a/templates/actor/character/character-header.hbs +++ b/templates/actor/character/character-header.hbs @@ -40,7 +40,7 @@ data-dtype="Number" /> - diff --git a/templates/actor/character/character-id.hbs b/templates/actor/character/character-id.hbs index 0a6e2eb..994db5e 100644 --- a/templates/actor/character/character-id.hbs +++ b/templates/actor/character/character-id.hbs @@ -25,8 +25,12 @@
    {{#repeat system.adaptation.totems.human.max 1 "humantotem"}} -
    +
    + {{#if actor.flags.world.editMode}} + + {{/if}} {{#ifgteq system.adaptation.totems.human.value @humantotem}}
    {{/ifgteq}} @@ -36,11 +40,16 @@
    {{#repeat system.adaptation.totems.adapted.max 1 "adaptedtotem"}} -
    +
    + {{#if actor.flags.world.editMode}} + + {{/if}} {{#ifgteq system.adaptation.totems.adapted.value @adaptedtotem}}
    - {{/ifgteq}}
    + {{/ifgteq}} +
    {{/repeat}}
    diff --git a/templates/actor/group/group-info.hbs b/templates/actor/group/group-info.hbs index 76b54aa..306fd93 100644 --- a/templates/actor/group/group-info.hbs +++ b/templates/actor/group/group-info.hbs @@ -1,13 +1,5 @@
    {{!-- Group Identity --}} - -
    -

    {{ localize 'IDENTITY.profile' }}

    -
    - -
    -

    {{ localize 'IDENTITY.origin' }}

    @@ -25,24 +17,6 @@
    -
    -

    {{ localize 'VERMINE.level' }}

    -
    - - / {{system.level.max}} -
    -
    - -
    -

    {{ localize 'VERMINE.reputation' }}

    -
    - - / {{system.reputation.max}} -
    -
    - {{!-- Group Notes --}}

    {{ localize 'IDENTITY.notes' }}

    diff --git a/templates/dialogs/roll-dialog.hbs b/templates/dialogs/roll-dialog.hbs index 20c5c75..d74475d 100644 --- a/templates/dialogs/roll-dialog.hbs +++ b/templates/dialogs/roll-dialog.hbs @@ -1,9 +1,4 @@ -
    - +
    @@ -359,4 +354,12 @@
    -
    +
    + + +
    +
    diff --git a/templates/item/partials/damages.hbs b/templates/item/partials/damages.hbs index 28b3bb6..9c735f1 100644 --- a/templates/item/partials/damages.hbs +++ b/templates/item/partials/damages.hbs @@ -10,6 +10,7 @@ type="radio" name="system.damages.value" value="{{@index}}" + data-action="clickDamage" {{#ife system.damages.value @index }} checked="true" {{/ife }} diff --git a/templates/item/partials/traits.hbs b/templates/item/partials/traits.hbs index 3968dcc..bd55b49 100644 --- a/templates/item/partials/traits.hbs +++ b/templates/item/partials/traits.hbs @@ -1,6 +1,6 @@

    traits - + diff --git a/templates/roll-message.hbs b/templates/roll-message.hbs index 783d72b..ac3b32e 100644 --- a/templates/roll-message.hbs +++ b/templates/roll-message.hbs @@ -41,7 +41,8 @@ {{set diceTypeVal="regular"}} {{/ifincludes}}
  • + data-dice-type="{{diceTypeVal}}" + data-result="{{die.result}}"> {{die.result}}
  • {{/each}}