Compare commits

..

27 Commits

Author SHA1 Message Date
uberwald b97f8688e4 Foundryv14 migration
Release Creation / build (release) Successful in 46s
2026-04-01 22:39:25 +02:00
uberwald 9836bd50e6 Resultat impari sur D20 dans messages et corrections diverses
Release Creation / build (release) Successful in 46s
2026-03-10 21:37:33 +01:00
uberwald 6b909b192b Resultat impari sur D20 dans messages et corrections diverses 2026-03-10 21:37:20 +01:00
uberwald e7a6c15bf7 Remove node_modules from repository
Release Creation / build (release) Successful in 56s
2026-01-09 17:25:00 +01:00
uberwald 1909ff443d Remove node_modules from repository 2026-01-09 17:24:36 +01:00
uberwald c72c9d8b02 Upgrade to auto-release 2026-01-09 17:23:02 +01:00
uberwald 536812e871 Upgrade to auto-release 2026-01-09 17:22:52 +01:00
uberwald 66fe1418f0 Migration datamodels ! 2026-01-09 17:11:12 +01:00
uberwald 901df5b395 Enhance CSS+fonts 2025-10-26 21:41:25 +01:00
uberwald f5d84832f3 CSS/Font enhancements 2025-10-26 15:08:45 +01:00
uberwald 216360e0d8 Correction sur usage BA sur un jet impair au D20 2025-06-24 22:11:57 +02:00
uberwald a0502dc467 Corrections cosmetiques v13 2025-06-03 13:49:09 +02:00
uberwald c8b94b74a1 Corrections cosmetiques v13 2025-06-03 13:48:23 +02:00
uberwald c9e4f125b4 Foundry v13 migration 2025-05-01 20:40:20 +02:00
uberwald e840b70ac9 Add UK/US translation 2024-09-10 13:17:45 +02:00
uberwald 03b8779862 Add UK/US translation 2024-09-10 07:20:55 +02:00
uberwald 580a2ccaba Corrections suite a migration Foundry v12 2024-05-01 09:46:37 +02:00
uberwald 699dba5511 Compatibilité v11/v12 2024-04-26 11:45:58 +02:00
uberwald aac552102a Add new automations 2024-04-05 22:33:36 +02:00
uberwald 4762dc33ac PNJ & Creatures 2024-03-03 08:20:30 +01:00
uberwald 0a030460e4 PNJ & Creatures 2024-03-02 23:58:27 +01:00
uberwald ed2dc58680 Enhance stats 2024-02-08 12:51:30 +01:00
uberwald 9c20f277ea Gestion plus fine ame+sante 2023-12-29 18:36:37 +01:00
uberwald 2a8617d781 Automatisations de combats, WIP 2023-12-24 12:38:02 +01:00
uberwald 91ad26730a Automatisations de combats, WIP 2023-12-22 09:30:26 +01:00
uberwald e1816b3dd7 CSS+Predilection+Niveau de competence 2023-12-21 19:21:06 +01:00
uberwald c48401a199 CSS+Predilection+Niveau de competence 2023-12-21 15:41:17 +01:00
266 changed files with 19235 additions and 3656 deletions
+63
View File
@@ -0,0 +1,63 @@
name: Release Creation
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
#- uses: actions/checkout@v3
- uses: RouxAntoine/checkout@v3.5.4
# get part of the tag after the `v`
- name: Extract tag version number
id: get_version
uses: battila7/get-version-action@v2
# Substitute the Manifest and Download URLs in the system.json
- name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1
with:
files: 'system.json'
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}}
manifest: https://www.uberwald.me/gitea/public/fvtt-mournblade/releases/download/latest/system.json
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-mournblade.zip
# Create a zip file with all files required by the module to add to the release
- run: |
apt update -y
apt install -y zip
- run: zip -r ./fvtt-mournblade.zip system.json README.md LICENSE assets/ lang/ modules/ packs/ styles/ templates/ template.json
- name: setup go
uses: https://github.com/actions/setup-go@v4
with:
go-version: '>=1.20.1'
- name: Use Go Action
id: use-go-action
uses: https://gitea.com/actions/release-action@main
with:
files: |-
./fvtt-mournblade.zip
system.json
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}'
- name: Publish to Foundry server
uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1
with:
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
id: 'fvtt-mournblade'
version: ${{github.event.release.tag_name}}
manifest: 'https://www.uberwald.me/gitea/public/fvtt-mournblade/releases/download/latest/system.json'
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-mournblade.zip'
compatibility-minimum: '13'
compatibility-verified: '13'
+2
View File
@@ -1 +1,3 @@
.history/ .history/
node_modules
.github/
Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

+35
View File
@@ -0,0 +1,35 @@
const gulp = require('gulp');
const less = require('gulp-less');
const sourcemaps = require('gulp-sourcemaps');
// Paths
const paths = {
styles: {
src: 'less/**/*.less',
dest: 'styles/'
}
};
// Compile LESS to CSS
function styles() {
return gulp.src('less/mournblade.less')
.pipe(sourcemaps.init())
.pipe(less())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.styles.dest));
}
// Watch files
function watchFiles() {
gulp.watch(paths.styles.src, styles);
}
// Define complex tasks
const build = gulp.series(styles);
const watch = gulp.series(build, watchFiles);
// Export tasks
exports.styles = styles;
exports.build = build;
exports.watch = watch;
exports.default = build;
+204
View File
@@ -0,0 +1,204 @@
{
"Adresse": "Agility",
"Clairvoyance": "Clarity",
"MNBL.abilities": "Gifts/Pacts",
"MNBL.activatedrunes": "Activated Runes",
"MNBL.addpredilection": "Add a specialization",
"MNBL.aimingbonus": "Aiming Bonus",
"MNBL.alignement": "Alignment",
"MNBL.all": "All",
"MNBL.allegiance": "Allegiance",
"MNBL.applydamage": "Apply damage/bonus/penalty",
"MNBL.aspect": "Aspect",
"MNBL.Assaut": "Smite",
"MNBL.attack": "Attack",
"MNBL.attackcapacity": "Offensive Ability",
"MNBL.attackmountbonus": "Mounted attacker vs ground defender (+5)",
"MNBL.attacks": "Attacks",
"MNBL.attribut": "Attribute",
"MNBL.attributes": "Attributes",
"MNBL.automalus": "Auto Penalty",
"MNBL.base": "Base",
"MNBL.beastslords": "Beast Lords",
"MNBL.bio": "Bio & Notes",
"MNBL.bonus": "Bonus",
"MNBL.candoublebonusskill": "Bonuses can be doubled (cf. Profession)",
"MNBL.chaos": "Chaos",
"MNBL.chaotictraits": "Chaotic Traits",
"MNBL.charge": "Charge",
"MNBL.consumed": "Consumed",
"MNBL.contain": "Contain the opponent",
"MNBL.creatureresourcecost": "Resource Cost (creatures)",
"MNBL.current": "Current",
"MNBL.currentmax": "Current Max",
"MNBL.damage": "Damage",
"MNBL.damagebonus": "Damage Bonus",
"MNBL.defense": "Defense",
"MNBL.defensebonus": "Defense Bonus",
"MNBL.defensecapacity": "Defensive Ability",
"MNBL.dice": "Dice",
"MNBL.difficulty": "Difficulty",
"MNBL.dirtyattack": "Cheap Shot",
"MNBL.disadvantagepositions": "Disadvantageous positions (Max bonus +15)",
"MNBL.disarm": "Disarm",
"MNBL.doubleD20": "Double d20 (1 Shard Point)",
"MNBL.dramaticfailure": "Dramatic Failure",
"MNBL.oddresult": "Odd result — the die counts as 0",
"MNBL.duration": "Duration",
"MNBL.easy": "Easy (5)",
"MNBL.eclat": "Shard",
"MNBL.elementslords": "Elemental Lords",
"MNBL.equipmentactions": "Equipment/Actions",
"MNBL.equipments": "Equipments",
"MNBL.equipped": "Equipped",
"MNBL.exp": "Experience",
"MNBL.eyes": "Eyes",
"MNBL.failure": "Failure",
"MNBL.feint": "Feint",
"MNBL.flee": "Flee",
"MNBL.formula": "Formula",
"MNBL.genre": "Gender",
"MNBL.gifts": "Gifts",
"MNBL.goodadventure": "Good Adventure",
"MNBL.hair": "Hair",
"MNBL.hard": "Hard (15)",
"MNBL.hascover": "Cover",
"MNBL.hazardous": "Tricky (20)",
"MNBL.health": "Health",
"MNBL.healthmalus": "Health Penalty",
"MNBL.heavycover": "Almost complete (-10)",
"MNBL.heroicsuccess": "Heroic Success",
"MNBL.highlanguage": "High Melnibonéan",
"MNBL.ignorearmor": "Ignore Armor",
"MNBL.ignorehealthmalus": "Ignore Health Penalty",
"MNBL.ignoresoulmalus": "Ignore Soul Penalty",
"MNBL.immobilize": "Immobilize",
"MNBL.insane": "Ridiculous (25)",
"MNBL.isdefense": "Defensive",
"MNBL.knockout": "Knock Out",
"MNBL.law": "Law",
"MNBL.legacy": "Legacy",
"MNBL.lessthanshort": "Less than short (10)",
"MNBL.lethal": "Lethal",
"MNBL.level": "Level",
"MNBL.lightcover": "Buckler or light (-2)",
"MNBL.longmore": "Long and more (25)",
"MNBL.longrange": "Long Range",
"MNBL.malus": "Penalty",
"MNBL.margin": "Margin",
"MNBL.medium": "Average (10)",
"MNBL.mediumcover": "Pavise or half (-5)",
"MNBL.mediummore": "Medium and more (20)",
"MNBL.mediumrange": "Medium Range",
"MNBL.meleethrowweapon": "Melee and Throwing Weapon",
"MNBL.meleeweapon": "Melee Weapon",
"MNBL.mode": "Mode",
"MNBL.modifier": "Modifiers",
"MNBL.modifiertype": "Modifier Type",
"MNBL.mounted": "Mounted",
"MNBL.nextactionmalus": "Penalty for next action",
"MNBL.nextattackbonus": "Bonus for next attack",
"MNBL.none": "None",
"MNBL.noneunknwon": "None/Unknown",
"MNBL.nonlethal": "Non-Lethal",
"MNBL.nonlethaldamage": "Non-Lethal Damage",
"MNBL.notarget": "No designated target",
"MNBL.origin": "Origin",
"MNBL.pacts": "Pacts",
"MNBL.points": "Points",
"MNBL.preciseattack": "Precision Attack",
"MNBL.predilections": "Specializations",
"MNBL.preferredhand": "Preferred Hand",
"MNBL.prerequisites": "Prerequisites",
"MNBL.price": "Price",
"MNBL.profession": "Profession",
"MNBL.pronounced": "Spoken",
"MNBL.pronouncedrune": "Spoken Rune",
"MNBL.pronouncerune": "Speak the rune",
"MNBL.protections": "Protections",
"MNBL.puremadness": "Insane (30)",
"MNBL.quantity": "Quantity",
"MNBL.range": "Range",
"MNBL.rarity": "Rarity",
"MNBL.registeredmodifiers": "Registered Modifiers",
"MNBL.reloadduration": "Reload Time",
"MNBL.ressources": "Resources",
"MNBL.roll": "Roll",
"MNBL.runes": "Runes",
"MNBL.runningtarget": "Running target (-5/-10 depending on range)",
"MNBL.shootmodifier": "Shooting Modifiers",
"MNBL.shootweapon": "Shoot Weapon",
"MNBL.shortmore": "Short and more (10)",
"MNBL.shortrange": "Short Range",
"MNBL.size": "Size",
"MNBL.skill": "Skill",
"MNBL.skills": "Skills",
"MNBL.smallroomtarget": "Target in confined space (+5)",
"MNBL.soul": "Soul",
"MNBL.soulmalus": "Soul Penalty",
"MNBL.soulmultiplier": "Soul Multiplier",
"MNBL.soulpoints": "Soul Points",
"MNBL.specialactions": "Special Actions",
"MNBL.specialweapon": "Special (ability/gift)",
"MNBL.speciestrait": "Species Trait",
"MNBL.speed": "Speed",
"MNBL.success": "Success",
"MNBL.target": "Target",
"MNBL.targetbelow": "Target below (+5)",
"MNBL.targetcantmove": "Target immobilized (+5)",
"MNBL.targetdefense": "Opponent's Defense",
"MNBL.targetground": "Target on the ground (+5)",
"MNBL.targetseeshoot": "Target is aware of the shot",
"MNBL.tendancies": "Tendencies",
"MNBL.throwweapon": "Throwing Weapon",
"MNBL.totalprotection": "Total Protection",
"MNBL.traced": "Written",
"MNBL.tracedrune": "Written Rune",
"MNBL.tracerune": "Write the rune",
"MNBL.treasuremoney": "Treasures and Money",
"MNBL.twohands": "Two-handed",
"MNBL.type": "Type",
"MNBL.unarmedtarget": "Unarmed target (+5)",
"MNBL.unit": "Unit",
"MNBL.use": "Use",
"MNBL.usedpredilection": "Used Predilection",
"MNBL.value": "Value",
"MNBL.weapon": "Weapon",
"MNBL.weaponbonusattack": "Handling Bonus (offensive)",
"MNBL.weaponbonusdefense": "Handling Bonus (defensive)",
"MNBL.weapons": "Weapons",
"MNBL.weaponscapacities": "Weapons/Abilities",
"MNBL.weapontype": "Weapon Type",
"MNBL.weight": "Weight",
"MNBL.total": "Total",
"Présence": "Presence",
"Puissance": "Might",
"Trempe": "Mettle",
"TYPES": {
"Actor": {
"creature": "Creature",
"personnage": "Character"
},
"Item": {
"arme": "Weapon",
"bouclier": "Shield",
"capacite": "Ability",
"competence": "Skill",
"don": "Gifts",
"equipement": "Equipment",
"heritage": "Background",
"metier": "Profession",
"modifier": "Modifier",
"monnaie": "Currency",
"origine": "Origin",
"pacte": "Pacts",
"protection": "Protections",
"rune": "Rune",
"runeeffect": "Rune Effect",
"tendance": "Signs of Chaos",
"traitchaotique": "Background",
"traitespece": "Species Trait"
}
}
}
+220 -22
View File
@@ -1,27 +1,225 @@
{ {
"ACTOR": { "TYPES": {
"TypePersonnage": "Personnage", "Actor": {
"TypePNJ": "PNJ" "personnage": "Personnage",
"creature": "Créature"
}, },
"Item": {
"ITEM": { "arme": "Arme",
"TypeArme": "Arme", "competence": "Compétence",
"TypeCompetence": "Compétence", "protection": "Protection",
"TypeProtection": "Protection", "pacte": "Pacte",
"TypePacte": "Pacte", "traitchaotique": "Trait Chaotique",
"TypeTraitchaotique": "Trait Chaotique", "monnaie": "Monnaie",
"TypeMonnaie": "Monnaie", "don": "Don",
"TypeDon": "Don", "tendance": "Tendance",
"TypeTendance": "Tendance", "rune": "Rune",
"TypeRune": "Rune", "equipement": "Equipement",
"TypeEquipement": "Equipement", "capacite": "Capacité",
"TypeCapacite": "Capacité", "origine": "Origine",
"TypeOrigine": "Origine", "heritage": "Héritage",
"TypeHeritage": "Héritage", "metier": "Métier",
"TypeMetier": "Métier", "runeeffect": "Effet de Rune",
"TypeRuneeffect": "Effet de Rune", "bouclier": "Bouclier",
"TypeBouclier": "Bouclier" "modifier": "Modificateur",
"traitespece": "Trait d'Espèce"
} }
},
"Adresse": "Adresse",
"Puissance": "Puissance",
"Clairvoyance": "Clairvoyance",
"Présence": "Présence",
"Trempe": "Trempe",
"MNBL.assaut": "Assaut",
"MNBL.preciseattack": "Attaque Précise",
"MNBL.feint": "Feinte",
"MNBL.dirtyattack": "Coup Bas",
"MNBL.charge": "Charger",
"MNBL.contain": "Contenir l'adversaire",
"MNBL.disarm": "Désarmer",
"MNBL.none": "Aucun",
"MNBL.lightcover": "Rondache ou léger (-2)",
"MNBL.mediumcover": "Pavois ou à moitié (-5)",
"MNBL.heavycover": "Quasi complet (-10)",
"MNBL.roll": "Jet",
"MNBL.defensecapacity": "Capacité défensive",
"MNBL.attackcapacity": "Capacité offensive",
"MNBL.lessthanshort": "Moins que courte (10)",
"MNBL.shortmore": "Courte et + (10)",
"MNBL.mediummore": "Moyenne et + (20)",
"MNBL.longmore": "Longue et + (25)",
"MNBL.noneunknwon": "Aucune/Inconnue",
"MNBL.easy": "Facile (5)",
"MNBL.medium": "Moyenne (10)",
"MNBL.hard": "Ardue (15)",
"MNBL.hazardous": "Hasardeuse (20)",
"MNBL.insane": "Insensée (25)",
"MNBL.puremadness": "Pure Folie (30)",
"MNBL.pronouncerune": "Prononcer la rune",
"MNBL.tracerune": "Tracer la rune",
"MNBL.pronounced": "Prononcée",
"MNBL.traced": "Tracée",
"MNBL.meleeweapon": "Arme de contact",
"MNBL.meleethrowweapon": "Arme de contact et de Jet",
"MNBL.throwweapon": "Arme de Lancer",
"MNBL.shootweapon": "Arme de Tir",
"MNBL.specialweapon": "Spécial (capacité/don)",
"MNBL.all": "Tous",
"MNBL.beastslords": "Seigneurs des Bêtes",
"MNBL.elementslords": "Seigneurs Elementaires",
"MNBL.law": "Loi",
"MNBL.chaos": "Chaos",
"MNBL.level": "Niveau",
"MNBL.points": "Points",
"MNBL.aspect": "Aspect",
"MNBL.margin": "Marge",
"MNBL.goodadventure" : "Bonne Aventure",
"MNBL.base": "Base",
"MNBL.current": "Actuelle",
"MNBL.alignement" : "Alignement",
"MNBL.eclat": "Eclat",
"MNBL.exp": "Expérience",
"MNBL.attributes": "Attributs",
"MNBL.skills": "Compétences",
"MNBL.abilities": "Dons/Pactes",
"MNBL.equipmentactions": "Equipements/Actions",
"MNBL.bio": "Bio&Notes",
"MNBL.health": "Santé",
"MNBL.bonus": "Bonus",
"MNBL.malus": "Malus",
"MNBL.nonlethal": "Non Létaux",
"MNBL.lethal": "Létaux",
"MNBL.automalus" : "Malus Auto",
"MNBL.soul": "Ame",
"MNBL.currentmax": "Max Actuel",
"MNBL.consumed": "Consommé",
"MNBL.damagebonus": "B. Dégats",
"MNBL.speed": "Vitesse",
"MNBL.defense": "Défense",
"MNBL.totalprotection": "Protection Totale",
"MNBL.modifier": "Modificateurs",
"MNBL.type": "Type",
"MNBL.value": "Valeur",
"MNBL.gifts": "Dons",
"MNBL.allegiance": "Allégeance",
"MNBL.pacts": "Pactes",
"MNBL.runes": "Runes",
"MNBL.highlanguage": "Haut Parler",
"MNBL.difficulty": "Difficulté",
"MNBL.tendancies": "Tendances",
"MNBL.chaotictraits": "Traits Chaotique",
"MNBL.activatedrunes": "Runes actives",
"MNBL.mode": "Mode",
"MNBL.duration": "Durée",
"MNBL.treasuremoney": "Richesses et Argent",
"MNBL.quantity": "Quantité",
"MNBL.unit": "Unité",
"MNBL.specialactions": "Actions spéciales",
"MNBL.knockout": "Assomer",
"MNBL.flee": "Fuir",
"MNBL.immobilize": "Immobiliser",
"MNBL.mounted": "Monté",
"MNBL.weapons": "Armes",
"MNBL.attack": "Attaque",
"MNBL.damage": "Dégats",
"MNBL.protections": "Protections",
"MNBL.equipments": "Equipements",
"MNBL.equipment": "Equipement",
"MNBL.origin": "Origine",
"MNBL.legacy": "Héritage",
"MNBL.profession": "Métier",
"MNBL.genre": "Genre",
"MNBL.size": "Taille",
"MNBL.hair": "Cheveux",
"MNBL.eyes": "Yeux",
"MNBL.preferredhand": "Main Préférée",
"MNBL.weight": "Poids",
"MNBL.soulmultiplier": "Multiplicateur d'âme",
"MNBL.ignorehealthmalus": "Ignore le malus de santé",
"MNBL.ignoresoulmalus": "Ignore le malus d'âme",
"MNBL.weapon": "Arme",
"MNBL.nextattackbonus": "Bonus pour prochaine attaque",
"MNBL.nextactionmalus": "Malus au défenseur pour prochaine action",
"MNBL.applydamage": "Appliquer les dégats/bonus/malus",
"MNBL.attribut": "Attribut",
"MNBL.skill": "Compétence",
"MNBL.target": "Cible",
"MNBL.usedpredilection": "Prédilection utilisée",
"MNBL.soulpoints": "Points d'âme",
"MNBL.formula": "Formule",
"MNBL.dice":"Dé",
"MNBL.success": "Succés",
"MNBL.failure": "Echec",
"MNBL.heroicsuccess": "Succés Héroïque",
"MNBL.dramaticfailure": "Echec Dramatique",
"MNBL.oddresult": "Résultat impair — le dé compte pour 0",
"MNBL.attackmountbonus": "Attaquant monté vs def. au sol (+5)",
"MNBL.targetdefense": "Défense adversaire",
"MNBL.shootmodifier": "Modificateurs de Tir",
"MNBL.aimingbonus": "Bonus de visée",
"MNBL.targetseeshoot": "La cible est consciente du tir",
"MNBL.notarget": "Pas de cible désignée",
"MNBL.runningtarget": "La cible court (-5/-10 selon portée)",
"MNBL.hascover": "Couvert",
"MNBL.range": "Portée",
"MNBL.disadvantagepositions": "Positions désavantageuses (Bonus max +15)",
"MNBL.targetground": "Cible au sol (+5)",
"MNBL.unarmedtarget": "Cible désarmée (+5)",
"MNBL.smallroomtarget": "Cible en espace restreint (+5)",
"MNBL.targetcantmove": "Cible immobilisée (+5)",
"MNBL.targetbelow": "Cible surplombée (+5)",
"MNBL.healthmalus": "Malus de santé",
"MNBL.soulmalus": "Malus d'âme",
"MNBL.registeredmodifiers": "Modificateurs enregistrés",
"MNBL.doubleD20": "Doubler le d20 (1 Point d'Eclat)",
"MNBL.pronouncedrune": "Rune prononcée",
"MNBL.tracedrune": "Rune tracée",
"MNBL.equipped": "Equipé",
"MNBL.rarity": "Rareté",
"MNBL.price": "Prix",
"MNBL.modifiertype": "Type de modificateur",
"MNBL.prerequisites": "Prérequis",
"MNBL.predilections": "Prédilections",
"MNBL.candoublebonusskill": "Les bonus de Bonne Aventure et d'Eclat peuvent être doublés (cf. Métier)",
"MNBL.addpredilection": "Ajouter une prédilection",
"MNBL.defensebonus": "Bonus de défense",
"MNBL.nonlethaldamage": "Dégâts non létaux",
"MNBL.weapontype": "Type d'arme",
"MNBL.weaponbonusattack": "Bonus de maniement (offensif)",
"MNBL.weaponbonusdefense": "Bonus de maniement (défensif)",
"MNBL.isdefense": "Défensive",
"MNBL.twohands": "A deux mains",
"MNBL.ignorearmor": "Ignore l'armure",
"MNBL.creatureresourcecost": "Cout en Ressources (créatures)",
"MNBL.shortrange": "Portée courte",
"MNBL.mediumrange":"Portée moyenne",
"MNBL.longrange":"Portée longue",
"MNBL.reloadduration": "Temps de rechargement",
"MNBL.attacks": "Attaques",
"MNBL.ressources": "Ressources",
"MNBL.weaponscapacities": "Armes/Capacités",
"MNBL.use": "Utiliser",
"MNBL.speciestrait": "Trait d'espèce",
"MNBL.attribute": "Attribut",
"MNBL.Protections": "Protections",
"MNBL.rune": "Rune",
"MNBL.total": "Total",
"MNBL.description": "Description",
"MNBL.details": "Détails",
"MNBL.sacrifice": "Sacrifice",
"MNBL.identity": "Identité",
"MNBL.gmtools": "Outils MJ"
} }
+874
View File
@@ -0,0 +1,874 @@
/* ==================== Actor Sheet Styles ==================== */
// NOTE: Ce fichier surcharge certaines règles de simple-converted.less
// Les sélecteurs .fvtt-mournblade.actor sont plus spécifiques et prennent priorité
.fvtt-mournblade.actor {
// Background pour toute la fiche d'acteur
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
// AppV2 - Structure flex pour permettre le scroll
.window-content {
height: 100%;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
// Main form structure
form,
.sheet-form-layout {
height: 100%;
background: url("../assets/ui/pc_sheet_bg.webp") repeat-y;
color: black;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
padding: 0;
margin: 0;
// La section racine du template (if nested inside form, or simple direct children handling)
> section {
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
}
}
// SURCHARGE: simple-converted.less définit flex: 0 0 210px
.sheet-header {
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
padding: 0.5rem;
margin: 0;
flex: 0 0 auto !important; // Override simple-converted
overflow: visible !important; // Override simple-converted
.background-sheet-header {
background: transparent;
}
.header-main-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.header-stats-grid {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
gap: 0.75rem;
padding: 0.5rem;
background: rgba(0, 0, 0, 0.15);
border: 2px solid rgba(139, 69, 19, 0.5);
border-radius: 4px;
}
.stat-group {
display: flex;
flex-direction: column;
gap: 0.3rem;
padding: 0.35rem 0.6rem;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(139, 69, 19, 0.4);
border-radius: 3px;
&.stat-group-health {
border-left: 3px solid rgba(200, 0, 0, 0.6);
background: rgba(40, 0, 0, 0.15);
}
}
.stat-title {
font-size: 0.75rem;
font-weight: bold;
color: #f5e6d3;
margin: 0;
padding: 0 0 0.3rem 0;
border-bottom: 1px solid rgba(139, 69, 19, 0.5);
text-transform: uppercase;
letter-spacing: 0.5px;
font-family: "CentaurMT", serif;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
.stat-values {
display: flex;
flex-direction: row;
gap: 0.5rem;
flex-wrap: wrap;
justify-content: center;
}
.stat-item {
display: flex;
flex-direction: column;
gap: 0.2rem;
align-items: center;
flex: 1;
min-width: 0;
}
.stat-label {
font-size: 0.7rem;
color: #d4c5b0;
margin: 0;
font-weight: 600;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
}
.stat-value {
font-size: 0.85rem;
font-weight: bold;
color: #fff;
padding: 0.25rem 0.5rem;
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(139, 69, 19, 0.6);
border-radius: 3px;
min-width: 2.5rem;
text-align: center;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
width: 100%;
}
.stat-input {
font-size: 0.8rem;
font-weight: 600;
padding: 0.25rem 0.4rem;
background: rgba(40, 30, 20, 0.7);
border: 1px solid rgba(139, 69, 19, 0.7);
color: #fff;
border-radius: 3px;
text-align: center;
min-width: 2.5rem;
max-width: 100%;
width: 100%;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
&.stat-input-single {
min-width: 3.5rem;
}
&.stat-input-damage {
border-color: rgba(200, 0, 0, 0.8);
background: rgba(80, 0, 0, 0.5);
}
&:hover {
background: rgba(60, 45, 30, 0.8);
border-color: rgba(255, 102, 0, 0.6);
}
&:focus {
outline: none;
border-color: rgba(255, 102, 0, 1);
box-shadow: 0 0 6px rgba(255, 102, 0, 0.5), inset 0 1px 3px rgba(0, 0, 0, 0.3);
background: rgba(70, 50, 35, 0.9);
}
}
.header-fields {
h4.item-name-label.competence-name {
font-size: 0.75rem;
padding-top: 3px;
}
.item-name-label.competence-name {
font-size: 0.75rem;
}
label.item-name-label.competence-name {
font-size: 0.7rem;
}
.item-field-label-short {
font-size: 0.8rem;
}
.status-small-label {
font-size: 0.8rem;
}
}
}
// Sheet navigation tabs
// SURCHARGE: simple-converted.less définit flex: 0
.sheet-tabs {
margin: 0;
padding: 0 0.5rem;
flex: 0 0 auto !important; // Override simple-converted
}
// Sheet body - section scrollable
// SURCHARGE CRITIQUE: simple-converted.less définit height: auto sur .sheet-body et .tab
// Ces surcharges permettent le scroll vertical
.sheet-body {
margin: 0;
padding: 0.5rem;
flex: 1 !important; // Override simple-converted
min-height: 0 !important; // Critique pour le scroll
overflow-y: auto !important; // Override simple-converted
.tab {
padding: 0;
height: auto !important; // Override simple-converted qui met height: auto
&:not(.active) {
display: none;
}
// Assurer que les grilles peuvent scroller
.grid, .grid-2col {
height: auto;
overflow: visible;
}
.sheet-box {
height: auto;
}
}
// Listes compactes dans les sections
.compact-list {
list-style: none;
margin: 0;
padding: 0;
li.item {
padding: 0.2rem 0.4rem;
margin-bottom: 0.15rem;
background: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(139, 69, 19, 0.3);
border-radius: 3px;
&:hover {
background: rgba(0, 0, 0, 0.15);
}
&.items-title-bg {
background: rgba(0, 0, 0, 0.3);
border-color: rgba(139, 69, 19, 0.5);
font-weight: 600;
margin-bottom: 0.3rem;
.item-name-label-header {
color: #f5e6d3;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
}
}
}
}
// Contrôles d'items (edit, delete, equip, etc.)
.item-controls {
display: flex;
align-items: center;
gap: 0.3rem;
justify-content: flex-end;
.item-control {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
color: #5a3a1a;
text-decoration: none;
cursor: pointer;
border-radius: 3px;
transition: all 0.2s;
&:hover {
color: #2a1a0a;
background: rgba(139, 69, 19, 0.2);
transform: scale(1.1);
}
i {
font-size: 0.85rem;
}
&.item-edit {
color: #4a7c59;
&:hover {
color: #2d5a3a;
background: rgba(74, 124, 89, 0.2);
}
}
&.item-delete {
color: #a04040;
&:hover {
color: #802020;
background: rgba(160, 64, 64, 0.2);
}
}
&.item-equip {
color: #6b5b3a;
&:hover {
color: #4a3a1a;
background: rgba(107, 91, 58, 0.2);
}
i.fa-circle {
color: #4a7c59;
}
i.fa-genderless {
color: #8a6a4a;
}
}
&.item-add {
color: #4a7c59;
&:hover {
color: #2d5a3a;
background: rgba(74, 124, 89, 0.2);
}
}
}
}
.item-controls-fixed {
min-width: 3.2rem;
max-width: 3.2rem;
}
// Couleurs pour les labels et textes dans les onglets
h4, h3, label, span.item-name-label, span.competence-name,
.label-name, .generic-label, .item-field-label-short,
.item-field-label-medium, .item-field-label-long,
.short-label, .items-title-text {
color: #3a2a1a !important;
text-shadow: 0px 0px 1px rgba(255, 255, 255, 0.3);
}
// Inputs dans le corps
input[type="text"], input[type="number"], select {
color: #2a1a0a;
background: rgba(255, 250, 240, 0.8);
border: 1px solid rgba(139, 69, 19, 0.5);
height: 24px;
padding: 0.15rem 0.3rem;
line-height: 1.2;
&:focus {
background: rgba(255, 255, 245, 0.95);
border-color: rgba(139, 69, 19, 0.8);
}
}
// Titres de sections
h3, h4 {
font-weight: bold;
color: #2a1a0a !important;
}
// Titres de sections Santé, Âme, Combat
h4.item-name-label.competence-name {
font-size: 0.85rem;
margin-top: 0.2rem;
margin-bottom: 0.3rem;
padding-top: 0;
}
// Section grids pour Santé, Âme, Combat
.section-grid {
display: flex;
flex-direction: column;
gap: 0.2rem;
padding: 0.3rem 0.4rem;
margin-bottom: 0.3rem;
background: rgba(0, 0, 0, 0.15);
border: 2px solid rgba(139, 69, 19, 0.5);
border-radius: 4px;
.section-title {
font-size: 0.85rem;
font-weight: bold;
color: #f5e6d3;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0 0 0.2rem 0;
padding: 0.2rem 0.4rem;
background: rgba(0, 0, 0, 0.3);
border-left: 3px solid rgba(139, 69, 19, 0.8);
border-radius: 2px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
}
.grid-row {
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
gap: 0.4rem;
padding: 0.25rem 0.4rem;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(139, 69, 19, 0.4);
border-radius: 3px;
input {
width: 60px;
min-width: 60px;
}
&.attr-row {
grid-template-columns: 40px 1fr auto;
gap: 0.5rem;
.item-name-img {
width: 32px;
height: 32px;
border-radius: 3px;
border: 1px solid rgba(139, 69, 19, 0.5);
}
.label-name {
display: flex;
align-items: center;
a {
color: #f5e6d3;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
font-weight: 600;
cursor: pointer;
&:hover {
color: #fff;
text-shadow: 0 0 4px rgba(255, 200, 100, 0.8);
}
}
}
select {
width: 80px;
}
}
.label-name {
font-weight: 600;
font-size: 0.9rem;
color: #d4c5b0;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
}
.stat-label {
font-weight: 600;
font-size: 0.9rem;
color: #d4c5b0;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
}
.value-display {
text-align: center;
font-weight: bold;
font-size: 1rem;
color: #fff;
background: rgba(0, 0, 0, 0.4);
padding: 0.2rem 0.4rem;
border-radius: 3px;
border: 1px solid rgba(139, 69, 19, 0.6);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
}
.malus-value {
text-align: center;
font-weight: bold;
font-size: 1rem;
color: #ff9999;
background: rgba(0, 0, 0, 0.4);
padding: 0.2rem 0.4rem;
border-radius: 3px;
border: 1px solid rgba(200, 50, 50, 0.6);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
}
&.damage-row {
grid-template-columns: 120px 1fr;
.damage-label {
font-size: 0.95rem;
font-weight: 600;
}
.damage-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
input {
width: 60px;
min-width: 60px;
flex-shrink: 0;
text-align: center;
font-size: 1.1rem;
font-weight: bold;
}
.plus-minus-button {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
font-weight: bold;
border-radius: 4px;
border: 2px solid rgba(139, 69, 19, 0.5);
background: linear-gradient(to bottom, rgba(210, 180, 140, 0.9), rgba(180, 150, 110, 0.9));
color: #2a1a0a;
cursor: pointer;
transition: all 0.15s;
&:hover {
background: linear-gradient(to bottom, rgba(230, 200, 160, 0.95), rgba(200, 170, 130, 0.95));
border-color: rgba(139, 69, 19, 0.8);
transform: scale(1.08);
}
&:active {
transform: scale(0.95);
}
}
}
}
&.soul-consumed-row {
grid-template-columns: 120px 1fr;
}
&.soul-malus-row {
grid-template-columns: 90px 1fr;
gap: 0.4rem;
.label-name {
font-size: 0.85rem;
}
.malus-value {
font-size: 0.9rem;
padding: 0.2rem 0.3rem;
}
}
&.malus-row {
grid-template-columns: 55px 60px 75px 50px;
gap: 0.3rem;
.label-name {
font-size: 0.85rem;
}
input {
width: 100%;
min-width: unset;
}
.malus-value {
font-size: 0.9rem;
padding: 0.2rem 0.3rem;
}
}
}
&.combat-grid {
.grid-row.combat-stat {
grid-template-columns: 85px 50px 60px 55px;
gap: 0.4rem;
.stat-label {
font-size: 0.85rem;
}
.stat-base {
text-align: center;
font-weight: bold;
font-size: 0.85rem;
color: #fff;
background: rgba(0, 0, 0, 0.4);
padding: 0.2rem 0.3rem;
border-radius: 3px;
border: 1px solid rgba(139, 69, 19, 0.6);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
}
input {
width: 50px;
min-width: 50px;
text-align: center;
}
.stat-total {
text-align: center;
font-weight: bold;
font-size: 0.85rem;
color: #fff;
background: rgba(0, 0, 0, 0.4);
padding: 0.2rem 0.3rem;
border-radius: 3px;
border: 1px solid rgba(139, 69, 19, 0.6);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
}
}
.grid-row.protection-row {
grid-template-columns: 1fr 1fr;
.protection-value {
text-align: center;
font-weight: bold;
font-size: 1.2rem;
color: #fff;
background: rgba(0, 0, 0, 0.4);
padding: 0.25rem 0.4rem;
border-radius: 3px;
border: 1px solid rgba(139, 69, 19, 0.6);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
}
}
}
}
// Boutons d'actions spéciales
.action-buttons {
display: flex;
flex-direction: column;
gap: 0.3rem;
padding: 0.3rem 0;
button {
margin: 0;
}
}
.action-buttons-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
padding: 0.3rem 0;
button {
margin: 0;
flex: 1;
}
.mounted-checkbox {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.3rem 0.6rem;
white-space: nowrap;
label {
margin: 0;
font-size: 0.9rem;
}
input[type="checkbox"] {
margin: 0;
cursor: pointer;
}
}
}
}
}
/* ==================== Creature Sheet Specific Styles ==================== */
.fvtt-mournblade.actor.creature-sheet {
// Variant background pour creatures - teinte légèrement différente
.background-sheet-header-creature {
background: linear-gradient(135deg, rgba(0, 60, 0, 0.15) 0%, rgba(20, 80, 20, 0.1) 100%);
border: 2px solid rgba(34, 139, 34, 0.5);
border-radius: 4px;
padding: 0;
}
// Légère teinte verte pour les sections
.sheet-box {
&.color-bg-archetype {
background: linear-gradient(135deg, rgba(0, 40, 0, 0.08) 0%, rgba(20, 60, 20, 0.05) 100%);
}
}
// Header simplifié pour creatures
.sheet-header {
flex: 0 0 auto !important;
padding: 0;
}
.profile-img {
width: 80px;
height: 80px;
border-radius: 8px;
border: 2px solid rgba(34, 139, 34, 0.6);
object-fit: cover;
cursor: pointer;
flex-shrink: 0;
}
// Tabs avec teinte verte
nav.tabs {
.item.active {
border-bottom-color: rgba(34, 139, 34, 0.8);
color: #228b22;
}
}
// Section titles avec teinte verte
.section-title {
color: #1a5a1a;
border-bottom-color: rgba(34, 139, 34, 0.3);
}
// Items headers
.items-title-bg {
background: linear-gradient(135deg, rgba(0, 60, 0, 0.15) 0%, rgba(20, 80, 20, 0.1) 100%);
border-bottom: 1px solid rgba(34, 139, 34, 0.3);
}
}
// PNJ Sheet - Orange/Copper theme for visual distinction
.fvtt-mournblade.actor.pnj-sheet {
// Variant background pour PNJs - teinte orange/cuivre
.background-sheet-header-creature {
background: linear-gradient(135deg, rgba(80, 40, 0, 0.15) 0%, rgba(100, 50, 0, 0.1) 100%);
border: 2px solid rgba(205, 127, 50, 0.5);
border-radius: 4px;
padding: 0;
}
// Légère teinte orange/cuivre pour les sections
.sheet-box {
&.color-bg-archetype {
background: linear-gradient(135deg, rgba(60, 30, 0, 0.08) 0%, rgba(80, 40, 0, 0.05) 100%);
}
}
// Header simplifié pour PNJs
.sheet-header {
flex: 0 0 auto !important;
padding: 0;
}
.profile-img {
width: 80px;
height: 80px;
border-radius: 8px;
border: 2px solid rgba(205, 127, 50, 0.6);
object-fit: cover;
cursor: pointer;
flex-shrink: 0;
}
// Tabs avec teinte orange/cuivre
nav.tabs {
.item.active {
border-bottom-color: rgba(205, 127, 50, 0.8);
color: #cd7f32;
}
}
// Section titles avec teinte orange/cuivre
.section-title {
color: #b8734d;
border-bottom-color: rgba(205, 127, 50, 0.3);
}
// Items headers
.items-title-bg {
background: linear-gradient(135deg, rgba(80, 40, 0, 0.15) 0%, rgba(100, 50, 0, 0.1) 100%);
border-bottom: 1px solid rgba(205, 127, 50, 0.3);
}
}
// GM Tools section - labels plus larges
.gm-tools-section {
.grid {
.item-list .flexrow.item {
.label-name {
flex: 1;
min-width: 12rem;
}
}
}
}
// Alignment group in header
.stat-group-alignment {
.stat-values.alignment-values {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.alignment-row {
display: flex;
align-items: center;
gap: 0.3rem;
.alignment-label {
font-weight: bold;
min-width: 4rem;
font-size: 0.85rem;
}
.alignment-inputs {
display: flex;
align-items: center;
gap: 0.2rem;
flex: 1;
}
.stat-label-mini {
font-size: 0.7rem;
min-width: 2.5rem;
}
.stat-input-mini {
width: 2.5rem;
height: 1.5rem;
padding: 0.1rem 0.2rem;
font-size: 0.8rem;
}
select.stat-input-mini {
width: 3rem;
}
}
.alignment-info {
display: flex;
justify-content: space-around;
margin-top: 0.2rem;
padding-top: 0.2rem;
border-top: 1px solid rgba(139, 69, 19, 0.3);
.info-label {
font-size: 0.75rem;
color: #f5e6d3;
strong {
color: #ffd700;
}
}
}
}
+299
View File
@@ -0,0 +1,299 @@
/* ==================== Item Sheet Styles ==================== */
/* Item header with image and name */
.fvtt-mournblade.item {
/* Background pour toute la fiche d'item */
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
display: flex;
flex-direction: column;
height: 100%;
padding: 0;
margin: 0;
/* AppV2 - Remove window content padding */
.window-content {
padding: 0;
margin: 0;
}
/* AppV2 - Main section structure */
section {
background: url("../assets/ui/pc_sheet_bg.webp") repeat-y;
color: black;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
padding: 0;
margin: 0;
}
/* AppV2 Item Sheets - Disabled inputs readability */
input:disabled,
select:disabled {
color: #000000;
opacity: 0.8;
background-color: rgba(255, 255, 255, 0.5);
}
/* Inputs and selects styling */
input[type="text"],
input[type="number"],
select {
color: #000000;
background-color: rgba(255, 255, 255, 0.7);
border: 1px solid #999999;
margin: 0;
padding: 2px 4px;
font-family: "CentaurMT", serif;
font-size: 0.85rem;
}
textarea {
margin: 0;
padding: 2px 4px;
}
input[type="checkbox"] {
width: auto;
height: auto;
margin: 0 4px;
align-self: center;
}
.header {
flex: 0 0 auto;
border-bottom: 1px solid #999;
margin: 0;
}
.sheet-header {
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
padding: 0.5rem;
background: url("../assets/ui/pc_sheet_bg.webp") repeat;
flex: 0 0 auto;
}
.item-sheet-img {
flex: 0 0 64px;
width: 64px;
height: 64px;
object-fit: cover;
border: 1px solid #999;
border-radius: 4px;
cursor: pointer;
&:hover {
box-shadow: 0 0 8px rgba(255, 102, 0, 0.8);
}
}
.header-fields {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.header-actions {
flex: 0 0 auto;
display: flex;
align-items: center;
gap: 0.5rem;
button {
background: rgba(0, 0, 0, 0.1);
border: 1px solid #999;
padding: 0.25rem 0.5rem;
cursor: pointer;
font-family: "CentaurMT", serif;
&:hover {
background: rgba(255, 102, 0, 0.2);
box-shadow: 0 0 5px rgba(255, 102, 0, 0.5);
}
}
.chat-card-button {
background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%);
border: 2px ridge #846109;
color: #d4b5a8;
padding: 0.3rem 0.5rem;
transition: all 0.2s ease;
i {
font-size: 0.9rem;
}
&:hover {
background: linear-gradient(to bottom, #800000 5%, #3e0101 100%);
color: #ffffff;
box-shadow: 0 0 8px rgba(128, 0, 0, 0.6);
}
&:active {
position: relative;
top: 1px;
}
}
}
.sheet-header h1.charname {
height: 50px;
padding: 0;
margin: 5px 0;
border-bottom: 0;
font-weight: bold;
font-size: 2rem;
font-family: "CentaurMT";
}
.sheet-header h1.charname input {
width: 100%;
height: 100%;
margin: 0;
font-weight: bold;
font-family: "CentaurMT";
font-size: 2rem;
text-align: left;
border: 0 none;
&:focus {
outline: 1px solid #ff6600;
}
}
/* Tabs - Modern style matching actor sheets */
nav.tabs {
display: flex;
border-bottom: 1px solid #7a7971;
margin: 0;
padding: 4px;
background-color: #1c1c1c;
flex: 0 0 auto;
}
nav.tabs a.item {
padding: 6px 12px;
color: #d4af37;
text-decoration: none;
border: 1px solid transparent;
border-radius: 4px 4px 0 0;
margin-right: 4px;
transition: all 0.2s;
i {
display: none; // Hide icons to match actor sheets
}
&:hover {
background-color: rgba(212, 175, 55, 0.1);
}
&.active {
background-color: #2a2a2a;
border-bottom-color: #d4af37;
color: #f5f5f5;
}
}
/* Tab content */
.tab {
display: none;
padding: 4px 8px;
overflow-y: auto;
flex: 1 1 auto;
&.active {
display: block;
}
}
/* Item list in details tab */
.item-list {
list-style: none;
margin: 0;
padding: 0;
li.item {
display: flex;
align-items: center;
margin-bottom: 2px;
padding: 2px 4px;
min-height: 24px;
border-bottom: none;
&.flexrow {
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
}
}
}
/* Labels */
.generic-label {
display: inline-block;
white-space: nowrap;
font-weight: 700;
color: #464331c4;
font-size: 0.9rem;
font-family: "CentaurMT", serif;
margin: 0;
padding: 0 4px 0 0;
}
/* Field labels */
.item-field-label-short {
flex: 0 0 60px;
max-width: 60px;
}
.item-field-label-medium {
flex: 0 0 100px;
max-width: 100px;
}
.item-field-label-long {
flex: 1;
min-width: 150px;
}
.item-field-label-long1 {
flex: 1;
min-width: 200px;
}
.item-field-label-long2 {
flex: 1;
min-width: 250px;
max-width: 250px;
}
.item-field-label-long3 {
flex: 1;
min-width: 350px;
max-width: 350px;
}
.numeric-input {
text-align: center;
width: 60px;
}
/* Editor fields */
.editor {
height: 300px;
border: 1px solid #999;
background: rgba(255, 255, 255, 0.9);
.editor-content {
height: 100%;
padding: 0.5rem;
color: #000;
}
}
}
+6
View File
@@ -0,0 +1,6 @@
// Main LESS file for Mournblade system
// Importing base styles and component-specific styles
@import "simple-converted";
@import "item-styles";
@import "actor-styles";
File diff suppressed because it is too large Load Diff
@@ -1,18 +1,18 @@
/** /**
* Extend the basic ActorSheet with some very simple modifications * Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet} * @extends {ActorSheetV2}
*/ */
import { MournbladeUtility } from "./mournblade-utility.js"; import { MournbladeUtility } from "./mournblade-utility.js";
import { MournbladeRollDialog } from "./mournblade-roll-dialog.js"; import { MournbladeRollDialog } from "./mournblade-roll-dialog.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class MournbladeActorSheet extends ActorSheet { export class MournbladeActorSheet extends foundry.applications.sheets.ActorSheetV2 {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-mournblade", "sheet", "actor"], classes: ["fvtt-mournblade", "sheet", "actor"],
template: "systems/fvtt-mournblade/templates/actor-sheet.html", template: "systems/fvtt-mournblade/templates/actor-sheet.html",
width: 640, width: 640,
@@ -25,7 +25,7 @@ export class MournbladeActorSheet extends ActorSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
async getData() { async getData() {
const objectData = duplicate(this.object) const objectData = foundry.utils.duplicate(this.object)
let actorData = objectData let actorData = objectData
let formData = { let formData = {
@@ -40,22 +40,30 @@ export class MournbladeActorSheet extends ActorSheet {
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
limited: this.object.limited, limited: this.object.limited,
skills: this.actor.getSkills(), skills: this.actor.getSkills(),
armes: duplicate(this.actor.getWeapons()), armes: foundry.utils.duplicate(this.actor.getWeapons()),
protections: duplicate(this.actor.getArmors()), protections: foundry.utils.duplicate(this.actor.getArmors()),
dons: duplicate(this.actor.getDons()), dons: foundry.utils.duplicate(this.actor.getDons()),
pactes: foundry.utils.duplicate(this.actor.getPactes()),
alignement: this.actor.getAlignement(), alignement: this.actor.getAlignement(),
aspect: this.actor.getAspect(), aspect: this.actor.getAspect(),
marge: this.actor.getMarge(), marge: this.actor.getMarge(),
tendances:duplicate(this.actor.getTendances()), tendances:foundry.utils.duplicate(this.actor.getTendances()),
runes:duplicate(this.actor.getRunes()), runes:foundry.utils.duplicate(this.actor.getRunes()),
traitsChaotiques:duplicate(this.actor.getTraitsChaotiques()), traitsChaotiques:foundry.utils.duplicate(this.actor.getTraitsChaotiques()),
origine: duplicate(this.actor.getOrigine() || {}), traitsEspeces: foundry.utils.duplicate(this.actor.getTraitsEspeces()),
heritage: duplicate(this.actor.getHeritage() || {}), origine: foundry.utils.duplicate(this.actor.getOrigine() || {}),
metier: duplicate(this.actor.getMetier() || {}), heritage: foundry.utils.duplicate(this.actor.getHeritage() || {}),
metier: foundry.utils.duplicate(this.actor.getMetier() || {}),
combat: this.actor.getCombatValues(), combat: this.actor.getCombatValues(),
equipements: duplicate(this.actor.getEquipments()), equipements: foundry.utils.duplicate(this.actor.getEquipments()),
monnaies: duplicate(this.actor.getMonnaies()), modifiers: foundry.utils.duplicate(this.actor.getModifiers()),
description: await TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}), monnaies: foundry.utils.duplicate(this.actor.getMonnaies()),
runeEffects: foundry.utils.duplicate(this.actor.getRuneEffects()),
config: game.system.mournblade.config,
protectionTotal: this.actor.getProtectionTotal(),
santeMalus: this.actor.getStatusMalus(),
ameMalus: this.actor.getAmeMalus(),
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.description, {async: true}),
options: this.options, options: this.options,
owner: this.document.isOwner, owner: this.document.isOwner,
editScore: this.options.editScore, editScore: this.options.editScore,
@@ -128,6 +136,21 @@ export class MournbladeActorSheet extends ActorSheet {
let armeId = li.data("item-id") let armeId = li.data("item-id")
this.actor.rollArmeOffensif(armeId) this.actor.rollArmeOffensif(armeId)
}) })
html.find('.roll-assomer').click((event) => {
this.actor.rollAssomer()
})
html.find('.roll-fuir').click((event) => {
this.actor.rollFuir()
})
html.find('.roll-immobiliser').click((event) => {
this.actor.rollImmobiliser()
})
html.find('.roll-arme-special').click((event) => {
const li = $(event.currentTarget).parents(".item")
let armeId = li.data("item-id")
this.actor.rollArmeSpecial(armeId)
})
html.find('.roll-arme-degats').click((event) => { html.find('.roll-arme-degats').click((event) => {
const li = $(event.currentTarget).parents(".item") const li = $(event.currentTarget).parents(".item")
let armeId = li.data("item-id") let armeId = li.data("item-id")
@@ -142,6 +165,15 @@ export class MournbladeActorSheet extends ActorSheet {
const itemType = $(event.currentTarget).data("type") const itemType = $(event.currentTarget).data("type")
this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true }) this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
}) })
html.find('.sante-modify').click((event) => {
const santeType = $(event.currentTarget).data("type")
const value = $(event.currentTarget).data("value")
this.actor.incDecSante(santeType, value, false)
})
html.find('.ame-modify').click((event) => {
const value = $(event.currentTarget).data("value")
this.actor.incDecAme(value)
})
html.find('.lock-unlock-sheet').click((event) => { html.find('.lock-unlock-sheet').click((event) => {
@@ -0,0 +1,25 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { MournbladeActorSheet } from "./mournblade-actor-sheet.js";
import { MournbladeUtility } from "./mournblade-utility.js";
/* -------------------------------------------- */
export class MournbladeCreatureSheet extends MournbladeActorSheet {
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-mournblade", "sheet", "actor"],
template: "systems/fvtt-mournblade/templates/creature-sheet.html",
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
editScore: false
})
}
}
@@ -0,0 +1,158 @@
import { MournbladeUtility } from "../mournblade-utility.js"
/**
* Dialogue de jet de dé pour Mournblade - Version DialogV2
*/
export class MournbladeRollDialog {
/**
* Create and display the roll dialog
* @param {MournbladeActor} actor - The actor making the roll
* @param {Object} rollData - Data for the roll
* @returns {Promise<MournbladeRollDialog>}
*/
static async create(actor, rollData) {
// Préparer le contexte pour le template
const context = {
...rollData,
difficulte: String(rollData.difficulte || 0),
img: actor.img,
name: actor.name,
config: game.system.mournblade.config,
}
// Si attrKey est "tochoose", préparer la liste des attributs sélectionnables
if (rollData.attrKey === "tochoose") {
context.selectableAttributes = actor.system.attributs
}
// Rendre le template en HTML
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-mournblade/templates/roll-dialog-v2.hbs",
context
)
// Utiliser DialogV2.wait avec le HTML rendu
return foundry.applications.api.DialogV2.wait({
window: { title: "Test de Capacité", icon: "fa-solid fa-dice-d20" },
classes: ["mournblade-roll-dialog"],
position: { width: 500 },
modal: false,
content,
buttons: [
{
action: "rolld10",
label: "Lancer 1d10",
icon: "fa-solid fa-dice-d10",
default: true,
callback: (event, button, dialog) => {
this._updateRollDataFromForm(rollData, button.form.elements, actor)
rollData.mainDice = "1d10"
MournbladeUtility.rollMournblade(rollData)
}
},
{
action: "rolld20",
label: "Lancer 1d20",
icon: "fa-solid fa-dice-d20",
callback: (event, button, dialog) => {
this._updateRollDataFromForm(rollData, button.form.elements, actor)
rollData.mainDice = "1d20"
MournbladeUtility.rollMournblade(rollData)
}
},
],
rejectClose: false,
})
}
/**
* Mettre à jour rollData avec les valeurs du formulaire
* @param {Object} rollData - L'objet rollData à mettre à jour
* @param {HTMLFormControlsCollection} formElements - Les éléments du formulaire
* @param {MournbladeActor} actor - L'acteur pour récupérer les attributs
* @private
*/
static _updateRollDataFromForm(rollData, formElements, actor) {
// Attributs
if (formElements.attrKey) {
rollData.attrKey = formElements.attrKey.value
if (rollData.attrKey !== "tochoose" && actor) {
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
rollData.actionImg = "systems/fvtt-mournblade/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
}
}
// Modificateurs de base
if (formElements.difficulte) {
rollData.difficulte = Number(formElements.difficulte.value)
}
if (formElements.modificateur) {
rollData.modificateur = Number(formElements.modificateur.value)
}
// Runes
if (formElements.runemode) {
rollData.runemode = String(formElements.runemode.value)
}
if (formElements.runeame) {
rollData.runeame = Number(formElements.runeame.value)
}
// Combat mêlée
if (formElements.typeAttaque) {
rollData.typeAttaque = String(formElements.typeAttaque.value)
}
if (formElements.isMonte !== undefined) {
rollData.isMonte = formElements.isMonte.checked
}
// Combat distance
if (formElements.visee !== undefined) {
rollData.visee = formElements.visee.checked
}
if (formElements.cibleconsciente !== undefined) {
rollData.cibleconsciente = formElements.cibleconsciente.checked
}
if (formElements.ciblecourt !== undefined) {
rollData.ciblecourt = formElements.ciblecourt.checked
}
if (formElements.typeCouvert) {
rollData.typeCouvert = String(formElements.typeCouvert.value)
}
// Désavantages
if (!rollData.desavantages) rollData.desavantages = {}
if (formElements.cibleausol !== undefined) {
rollData.desavantages.cibleausol = formElements.cibleausol.checked
}
if (formElements.cibledesarmee !== undefined) {
rollData.desavantages.cibledesarmee = formElements.cibledesarmee.checked
}
if (formElements.ciblerestreint !== undefined) {
rollData.desavantages.ciblerestreint = formElements.ciblerestreint.checked
}
if (formElements.cibleimmobilisée !== undefined) {
rollData.desavantages.cibleimmobilisée = formElements.cibleimmobilisée.checked
}
if (formElements.ciblesurplomb !== undefined) {
rollData.desavantages.ciblesurplomb = formElements.ciblesurplomb.checked
}
// Double D20
if (formElements.doubleD20 !== undefined) {
rollData.doubleD20 = formElements.doubleD20.checked
}
// Modifiers
if (rollData.modifiers) {
rollData.modifiers.forEach((modifier, idx) => {
const checkbox = formElements[`apply-modifier-${idx}`]
if (checkbox) {
modifier.system.apply = checkbox.checked
}
})
}
}
}
+29
View File
@@ -0,0 +1,29 @@
/**
* Index des applications AppV2 pour Mournblade
* Ce fichier centralise tous les exports des applications
*/
// Applications de feuilles d'acteurs
export { default as MournbladePersonnageSheet } from './mournblade-personnage-sheet.mjs';
export { default as MournbladeCreatureSheet } from './mournblade-creature-sheet.mjs';
export { default as MournbladePnjSheet } from './mournblade-pnj-sheet.mjs';
// Applications de feuilles d'items
export { default as MournbladeArmeSheet } from './mournblade-arme-sheet.mjs';
export { default as MournbladeBouclierSheet } from './mournblade-bouclier-sheet.mjs';
export { default as MournbladeCapaciteSheet } from './mournblade-capacite-sheet.mjs';
export { default as MournbladeCompetenceSheet } from './mournblade-competence-sheet.mjs';
export { default as MournbladeDonSheet } from './mournblade-don-sheet.mjs';
export { default as MournbladeEquipementSheet } from './mournblade-equipement-sheet.mjs';
export { default as MournbladeHeritageSheet } from './mournblade-heritage-sheet.mjs';
export { default as MournbladeMetierSheet } from './mournblade-metier-sheet.mjs';
export { default as MournbladeModifierSheet } from './mournblade-modifier-sheet.mjs';
export { default as MournbladeMonnaieSheet } from './mournblade-monnaie-sheet.mjs';
export { default as MournbladeOrigineSheet } from './mournblade-origine-sheet.mjs';
export { default as MournbladePacteSheet } from './mournblade-pacte-sheet.mjs';
export { default as MournbladeProtectionSheet } from './mournblade-protection-sheet.mjs';
export { default as MournbladeRuneSheet } from './mournblade-rune-sheet.mjs';
export { default as MournbladeRuneEffectSheet } from './mournblade-runeeffect-sheet.mjs';
export { default as MournbladeTendanceSheet } from './mournblade-tendance-sheet.mjs';
export { default as MournbladeTraitChaotiqueSheet } from './mournblade-traitchaotique-sheet.mjs';
export { default as MournbladeTraitEspeceSheet } from './mournblade-traitespece-sheet.mjs';
@@ -0,0 +1,406 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
import { MournbladeUtility } from "../../mournblade-utility.js"
export default class MournbladeActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
/**
* Different sheet modes.
* @enum {number}
*/
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
this._sheetMode = this.constructor.SHEET_MODES.PLAY // Commencer en mode visualisation
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-mournblade", "sheet", "actor"],
position: {
width: 650,
height: 720,
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
window: {
resizable: true,
},
tabs: [
{
navSelector: 'nav[data-group="primary"]',
contentSelector: "section.sheet-body",
initial: "stats",
},
],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
actions: {
editImage: MournbladeActorSheet.#onEditImage,
toggleSheet: MournbladeActorSheet.#onToggleSheet,
editItem: MournbladeActorSheet.#onEditItem,
deleteItem: MournbladeActorSheet.#onDeleteItem,
createItem: MournbladeActorSheet.#onCreateItem,
equipItem: MournbladeActorSheet.#onEquipItem,
modifyQuantity: MournbladeActorSheet.#onModifyQuantity,
modifySante: MournbladeActorSheet.#onModifySante,
modifyAme: MournbladeActorSheet.#onModifyAme,
rollAttribut: MournbladeActorSheet.#onRollAttribut,
rollCompetence: MournbladeActorSheet.#onRollCompetence,
rollRune: MournbladeActorSheet.#onRollRune,
rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif,
rollArmeSpecial: MournbladeActorSheet.#onRollArmeSpecial,
rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats,
rollAssommer: MournbladeActorSheet.#onRollAssommer,
rollImmobiliser: MournbladeActorSheet.#onRollImmobiliser,
rollFuir: MournbladeActorSheet.#onRollFuir,
},
}
/**
* Is the sheet currently in 'Play' mode?
* @type {boolean}
*/
get isPlayMode() {
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
}
/**
* Is the sheet currently in 'Edit' mode?
* @type {boolean}
*/
get isEditMode() {
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
}
/**
* Tab groups state
* @type {object}
*/
tabGroups = { primary: "stats" }
/** @override */
async _prepareContext() {
const actor = this.document
const context = {
actor: actor,
system: actor.system,
source: actor.toObject(),
fields: actor.schema.fields,
systemFields: actor.system.schema.fields,
isEditable: this.isEditable,
isEditMode: this.isEditMode,
isPlayMode: this.isPlayMode,
isGM: game.user.isGM,
config: game.system.mournblade.config,
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }),
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
// Handle edit-item-data changes
this.element.querySelectorAll('.edit-item-data').forEach(element => {
element.addEventListener('change', async (event) => {
const target = event.currentTarget
const itemElement = target.closest('[data-item-id]')
if (!itemElement) return
const itemId = itemElement.dataset.itemId
const itemType = itemElement.dataset.itemType
const itemField = target.dataset.itemField
const dataType = target.dataset.dtype
const value = target.value
await this.document.editItemField(itemId, itemType, itemField, dataType, value)
})
})
// Activate tab navigation manually
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
// Activate the current tab
const activeTab = this.tabGroups[group] || "stats"
nav.querySelectorAll('[data-tab]').forEach(link => {
const tab = link.dataset.tab
link.classList.toggle('active', tab === activeTab)
link.addEventListener('click', (event) => {
event.preventDefault()
this.tabGroups[group] = tab
this.render()
})
})
// Show/hide tab content
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
}
// #region Drag-and-Drop Workflow
/**
* Create drag-and-drop workflow handlers for this Application
*/
#createDragDropHandlers() {
return []
}
// #region Actions
/** @override */
static ACTIONS = {
editImage: MournbladeActorSheet.#onEditImage,
toggleSheet: MournbladeActorSheet.#onToggleSheet,
editItem: MournbladeActorSheet.#onEditItem,
deleteItem: MournbladeActorSheet.#onDeleteItem,
createItem: MournbladeActorSheet.#onCreateItem,
equipItem: MournbladeActorSheet.#onEquipItem,
modifyQuantity: MournbladeActorSheet.#onModifyQuantity,
rollAttribut: MournbladeActorSheet.#onRollAttribut,
rollCompetence: MournbladeActorSheet.#onRollCompetence,
rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif,
rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats,
rollAssommer: MournbladeActorSheet.#onRollAssommer,
rollImmobiliser: MournbladeActorSheet.#onRollImmobiliser,
rollFuir: MournbladeActorSheet.#onRollFuir,
}
/**
* Handle editing the actor image
* @param {Event} event - The triggering event
*/
static async #onEditImage(event) {
event.preventDefault()
const sheet = this
const filePicker = new FilePicker({
type: "image",
current: sheet.document.img,
callback: (path) => {
sheet.document.update({ img: path })
},
})
filePicker.browse()
}
/**
* Handle toggling the sheet mode
* @param {Event} event - The triggering event
*/
static async #onToggleSheet(event) {
event.preventDefault()
const sheet = this
sheet._sheetMode = sheet._sheetMode === sheet.constructor.SHEET_MODES.PLAY ? sheet.constructor.SHEET_MODES.EDIT : sheet.constructor.SHEET_MODES.PLAY
sheet.render()
}
/**
* Handle editing an item
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onEditItem(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
const item = this.actor.items.get(itemId)
if (item) item.sheet.render(true)
}
/**
* Handle deleting an item
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onDeleteItem(event, target) {
const li = target.closest(".item")
await MournbladeUtility.confirmDelete(this, li)
}
/**
* Handle creating an item
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onCreateItem(event, target) {
const itemType = target.dataset.type
await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
}
/**
* Handle equipping an item
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onEquipItem(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
const item = this.actor.items.get(itemId)
if (item) {
await item.update({ "system.equipped": !item.system.equipped })
}
}
/**
* Handle modifying the quantity of an item
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onModifyQuantity(event, target) {
const li = target.closest('[data-item-id]')
const itemId = li?.dataset.itemId
const value = Number.parseInt(target.dataset.quantiteValue)
const item = this.document.items.get(itemId)
if (item) {
const newQuantity = Math.max(0, (item.system.quantite || 0) + value)
await item.update({ "system.quantite": newQuantity })
}
}
/**
* Handle modifying santé (health) values
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onModifySante(event, target) {
const type = target.dataset.type
const value = Number.parseInt(target.dataset.value)
const actor = this.document
const currentValue = actor.system.sante[type] || 0
const newValue = Math.max(0, currentValue + value)
await actor.update({ [`system.sante.${type}`]: newValue })
}
/**
* Handle modifying âme (soul) value
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
static async #onModifyAme(event, target) {
const value = Number.parseInt(target.dataset.value)
const actor = this.document
const currentValue = actor.system.ame.value || 0
const newValue = Math.max(0, currentValue + value)
await actor.update({ "system.ame.value": newValue })
}
/**
* Handle rolling an attribut
* @param {Event} event - The triggering event
*/
static async #onRollAttribut(event, target) {
event.preventDefault()
const sheet = this
const attrKey = target.dataset.attrKey
const actor = sheet.document
await actor.rollAttribut(attrKey)
}
/**
* Handle rolling a competence
* @param {Event} event - The triggering event
*/
static async #onRollCompetence(event, target) {
event.preventDefault()
const sheet = this
const attrKey = target.dataset.attrKey
const li = target.closest('[data-item-id]')
const compId = li?.dataset.itemId
const actor = sheet.document
await actor.rollCompetence(attrKey, compId)
}
/**
* Handle rolling a rune
* @param {Event} event - The triggering event
*/
static async #onRollRune(event, target) {
event.preventDefault()
const sheet = this
const li = target.closest('[data-item-id]')
const runeId = li?.dataset.itemId
const actor = sheet.document
await actor.rollRune(runeId)
}
/**
* Handle rolling an arme offensif
* @param {Event} event - The triggering event
*/
static async #onRollArmeOffensif(event, target) {
event.preventDefault()
const sheet = this
const armeId = target.dataset.armeId
const actor = sheet.document
await actor.rollArmeOffensif(armeId)
}
/**
* Handle rolling an arme degats
* @param {Event} event - The triggering event
*/
static async #onRollArmeDegats(event, target) {
event.preventDefault()
const sheet = this
const armeId = target.dataset.armeId
const actor = sheet.document
await actor.rollArmeDegats(armeId)
}
/**
* Handle rolling an arme special
* @param {Event} event - The triggering event
*/
static async #onRollArmeSpecial(event, target) {
event.preventDefault()
const sheet = this
const armeId = target.dataset.armeId
const actor = sheet.document
await actor.rollArmeSpecial(armeId)
}
/**
* Handle rolling an assommer
* @param {Event} event - The triggering event
*/
static async #onRollAssommer(event, target) {
event.preventDefault()
const sheet = this
const actor = sheet.document
await actor.rollAssomer()
}
/**
* Handle rolling an immobiliser
* @param {Event} event - The triggering event
*/
static async #onRollImmobiliser(event, target) {
event.preventDefault()
const sheet = this
const actor = sheet.document
await actor.rollImmobiliser()
}
/**
* Handle rolling a fuir
* @param {Event} event - The triggering event
*/
static async #onRollFuir(event, target) {
event.preventDefault()
const sheet = this
const actor = sheet.document
await actor.rollFuir()
}
}
@@ -0,0 +1,142 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class MournbladeItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-mournblade", "item"],
position: {
width: 620,
height: 600,
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
tabs: [
{
navSelector: 'nav[data-group="primary"]',
contentSelector: "section.sheet-body",
initial: "description",
},
],
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
actions: {
editImage: MournbladeItemSheet.#onEditImage,
postItem: MournbladeItemSheet.#onPostItem,
},
}
/**
* Tab groups state
* @type {object}
*/
tabGroups = { primary: "description" }
/** @override */
async _prepareContext() {
const context = {
fields: this.document.schema.fields,
systemFields: this.document.system.schema.fields,
item: this.document,
system: this.document.system,
source: this.document.toObject(),
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }),
isEditMode: true,
isEditable: this.isEditable,
isGM: game.user.isGM,
config: game.system.mournblade.config,
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
// Activate tab navigation manually
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
// Activate the current tab
const activeTab = this.tabGroups[group] || "description"
nav.querySelectorAll('[data-tab]').forEach(link => {
const tab = link.dataset.tab
link.classList.toggle('active', tab === activeTab)
link.addEventListener('click', (event) => {
event.preventDefault()
this.tabGroups[group] = tab
this.render()
})
})
// Show/hide tab content
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
}
// #region Drag-and-Drop Workflow
/**
* Create drag-and-drop workflow handlers for this Application
*/
#createDragDropHandlers() {
return []
}
// #region Actions
/**
* Handle editing the item image
* @param {Event} event - The triggering event
*/
static async #onEditImage(event) {
event.preventDefault()
const filePicker = new FilePicker({
type: "image",
current: this.document.img,
callback: (path) => {
this.document.update({ img: path })
},
})
filePicker.browse()
}
/**
* Handle posting the item to chat
* @param {Event} event - The triggering event
*/
static async #onPostItem(event) {
event.preventDefault()
let chatData = foundry.utils.duplicate(this.document)
if (this.document.actor) {
chatData.actor = { id: this.document.actor.id }
}
// Don't post any image for the item if the default image is used
if (chatData.img.includes("/blank.png")) {
chatData.img = null
}
// JSON object for easy creation
chatData.jsondata = JSON.stringify({
compendium: "postedItem",
payload: chatData,
})
const html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-mournblade/templates/post-item.hbs', chatData)
const chatOptions = {
user: game.user.id,
content: html,
}
ChatMessage.create(chatOptions)
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeArmeSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["arme"],
position: {
width: 620,
},
window: {
contentClasses: ["arme-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-arme-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeBouclierSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["bouclier"],
position: {
width: 620,
},
window: {
contentClasses: ["bouclier-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-bouclier-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeCapaciteSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["capacite"],
position: {
width: 620,
},
window: {
contentClasses: ["capacite-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-capacite-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeCompetenceSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["competence"],
position: {
width: 620,
},
window: {
contentClasses: ["competence-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-competence-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,43 @@
import MournbladeActorSheet from "./base-actor-sheet.mjs"
export default class MournbladeCreatureSheet extends MournbladeActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
window: {
...super.DEFAULT_OPTIONS.window,
title: "SHEETS.Actor.creature",
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-mournblade/templates/creature-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "stats",
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
// Add creature-specific data
context.skills = actor.getSkills()
context.armes = foundry.utils.duplicate(actor.getWeapons())
context.protections = foundry.utils.duplicate(actor.getArmors())
context.runes = foundry.utils.duplicate(actor.getRunes())
context.combat = actor.getCombatValues()
context.equipements = foundry.utils.duplicate(actor.getEquipments())
context.protectionTotal = actor.getProtectionTotal()
context.santeMalus = actor.getStatusMalus()
context.ameMalus = actor.getAmeMalus()
return context
}
}
@@ -0,0 +1,50 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeDonSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["don"],
position: {
width: 620,
},
window: {
contentClasses: ["don-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-don-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
context.enrichedSacrifice = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.sacrifice, { async: true })
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeEquipementSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["equipement"],
position: {
width: 620,
},
window: {
contentClasses: ["equipement-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-equipement-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeHeritageSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["heritage"],
position: {
width: 620,
},
window: {
contentClasses: ["heritage-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-heritage-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeMetierSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["metier"],
position: {
width: 620,
},
window: {
contentClasses: ["metier-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-metier-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeModifierSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["modifier"],
position: {
width: 620,
},
window: {
contentClasses: ["modifier-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-modifier-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeMonnaieSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["monnaie"],
position: {
width: 620,
},
window: {
contentClasses: ["monnaie-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-monnaie-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeOrigineSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["origine"],
position: {
width: 620,
},
window: {
contentClasses: ["origine-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-origine-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladePacteSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["pacte"],
position: {
width: 620,
},
window: {
contentClasses: ["pacte-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-pacte-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,57 @@
import MournbladeActorSheet from "./base-actor-sheet.mjs"
export default class MournbladePersonnageSheet extends MournbladeActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
window: {
...super.DEFAULT_OPTIONS.window,
title: "SHEETS.Actor.personnage",
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-mournblade/templates/actor-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "principal",
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
// Add personnage-specific data
context.skills = actor.getSkills()
context.armes = foundry.utils.duplicate(actor.getWeapons())
context.protections = foundry.utils.duplicate(actor.getArmors())
context.dons = foundry.utils.duplicate(actor.getDons())
context.pactes = foundry.utils.duplicate(actor.getPactes())
context.alignement = actor.getAlignement()
context.aspect = actor.getAspect()
context.marge = actor.getMarge()
context.tendances = foundry.utils.duplicate(actor.getTendances())
context.runes = foundry.utils.duplicate(actor.getRunes())
context.traitsChaotiques = foundry.utils.duplicate(actor.getTraitsChaotiques())
context.traitsEspeces = foundry.utils.duplicate(actor.getTraitsEspeces())
context.origine = foundry.utils.duplicate(actor.getOrigine() || {})
context.heritage = foundry.utils.duplicate(actor.getHeritage() || {})
context.metier = foundry.utils.duplicate(actor.getMetier() || {})
context.combat = actor.getCombatValues()
context.equipements = foundry.utils.duplicate(actor.getEquipments())
context.modifiers = foundry.utils.duplicate(actor.getModifiers())
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
context.runeEffects = foundry.utils.duplicate(actor.getRuneEffects())
context.protectionTotal = actor.getProtectionTotal()
context.santeMalus = actor.getStatusMalus()
context.ameMalus = actor.getAmeMalus()
return context
}
}
@@ -0,0 +1,47 @@
/**
* Application de feuille de PNJ pour Mournblade
*/
import MournbladeActorSheet from "./base-actor-sheet.mjs"
export default class MournbladePnjSheet extends MournbladeActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
window: {
...super.DEFAULT_OPTIONS.window,
title: "SHEETS.Actor.pnj",
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-mournblade/templates/actor-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "principal",
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
// Add pnj-specific data
context.skills = actor.getSkills()
context.armes = foundry.utils.duplicate(actor.getWeapons())
context.protections = foundry.utils.duplicate(actor.getArmors())
context.dons = foundry.utils.duplicate(actor.getDons())
context.pactes = foundry.utils.duplicate(actor.getPactes())
context.combat = actor.getCombatValues()
context.equipements = foundry.utils.duplicate(actor.getEquipments())
context.protectionTotal = actor.getProtectionTotal()
context.santeMalus = actor.getStatusMalus()
context.ameMalus = actor.getAmeMalus()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeProtectionSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["protection"],
position: {
width: 620,
},
window: {
contentClasses: ["protection-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-protection-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeRuneSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["rune"],
position: {
width: 620,
},
window: {
contentClasses: ["rune-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-rune-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeRuneEffectSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["runeeffect"],
position: {
width: 620,
},
window: {
contentClasses: ["runeeffect-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-runeeffect-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeTendanceSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["tendance"],
position: {
width: 620,
},
window: {
contentClasses: ["tendance-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-tendance-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeTraitChaotiqueSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["traitchaotique"],
position: {
width: 620,
},
window: {
contentClasses: ["traitchaotique-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-traitchaotique-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -0,0 +1,49 @@
import MournbladeItemSheet from "./base-item-sheet.mjs"
export default class MournbladeTraitEspeceSheet extends MournbladeItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["traitespece"],
position: {
width: 620,
},
window: {
contentClasses: ["traitespece-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-mournblade/templates/item-traitespece-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
+27
View File
@@ -0,0 +1,27 @@
/**
* Data model pour les armes
*/
export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
typearme: new fields.StringField({ initial: "" }),
isdefense: new fields.BooleanField({ initial: false }),
bonusmaniementoff: new fields.NumberField({ initial: 0, integer: true }),
bonusmaniementdef: new fields.NumberField({ initial: 0, integer: true }),
ignorearmure: new fields.BooleanField({ initial: false }),
nbressources: new fields.NumberField({ initial: 0, integer: true }),
degats: new fields.StringField({ initial: "" }),
nonletaux: new fields.BooleanField({ initial: false }),
deuxmains: new fields.BooleanField({ initial: false }),
courte: new fields.NumberField({ initial: 0, integer: true }),
moyenne: new fields.NumberField({ initial: 0, integer: true }),
longue: new fields.NumberField({ initial: 0, integer: true }),
tr: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
prix: new fields.NumberField({ initial: 0, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model de base pour les items Mournblade
*/
export default class BaseItemDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Data model pour les boucliers
*/
export default class BouclierDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
bonusdefense: new fields.NumberField({ initial: 0, integer: true }),
degats: new fields.StringField({ initial: "" }),
nonletaux: new fields.BooleanField({ initial: false }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
prix: new fields.NumberField({ initial: 0, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model pour les capacités
*/
export default class CapaciteDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+32
View File
@@ -0,0 +1,32 @@
/**
* Data model pour les compétences
*/
export default class CompetenceDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
niveau: new fields.NumberField({ initial: 0, integer: true }),
attribut1: new fields.StringField({ initial: "" }),
attribut2: new fields.StringField({ initial: "" }),
attribut3: new fields.StringField({ initial: "" }),
doublebonus: new fields.BooleanField({ initial: false }),
predilections: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({ initial: "" }),
used: new fields.BooleanField({ initial: false })
}),
{ initial: [] }
)
};
}
static migrateData(source) {
if (Array.isArray(source.predilections)) {
source.predilections = source.predilections.map(pred =>
typeof pred === "string" ? { name: pred, used: false } : pred
);
}
return super.migrateData(source);
}
}
+103
View File
@@ -0,0 +1,103 @@
/**
* Data model pour les créatures
*/
export default class CreatureDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
// Template biodata
biodata: new fields.SchemaField({
name: new fields.StringField({ initial: "" }),
age: new fields.NumberField({ initial: 0, integer: true }),
alignement: new fields.StringField({ initial: "" }),
poids: new fields.StringField({ initial: "" }),
taille: new fields.StringField({ initial: "" }),
cheveux: new fields.StringField({ initial: "" }),
sexe: new fields.StringField({ initial: "" }),
yeux: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" }),
amemultiplier: new fields.NumberField({ initial: 2, integer: true }),
ignoreamemalus: new fields.BooleanField({ initial: false }),
ignoresantemalus: new fields.BooleanField({ initial: false }),
notes: new fields.HTMLField({ initial: "" }),
gmnotes: new fields.HTMLField({ initial: "" })
}),
// Template core
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
attributs: new fields.SchemaField({
adr: new fields.SchemaField({
label: new fields.StringField({ initial: "Adresse" }),
labelnorm: new fields.StringField({ initial: "adresse" }),
abbrev: new fields.StringField({ initial: "adr" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pui: new fields.SchemaField({
label: new fields.StringField({ initial: "Puissance" }),
labelnorm: new fields.StringField({ initial: "puissance" }),
abbrev: new fields.StringField({ initial: "pui" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
cla: new fields.SchemaField({
label: new fields.StringField({ initial: "Clairvoyance" }),
labelnorm: new fields.StringField({ initial: "clairvoyance" }),
abbrev: new fields.StringField({ initial: "cla" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pre: new fields.SchemaField({
label: new fields.StringField({ initial: "Présence" }),
labelnorm: new fields.StringField({ initial: "presence" }),
abbrev: new fields.StringField({ initial: "pre" }),
value: new fields.NumberField({ initial: 0, integer: true })
}),
tre: new fields.SchemaField({
label: new fields.StringField({ initial: "Trempe" }),
labelnorm: new fields.StringField({ initial: "trempe" }),
abbrev: new fields.StringField({ initial: "tre" }),
value: new fields.NumberField({ initial: 0, integer: true })
})
}),
bonneaventure: new fields.SchemaField({
base: new fields.NumberField({ initial: 0, integer: true }),
actuelle: new fields.NumberField({ initial: 0, integer: true })
}),
experience: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
eclat: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
sante: new fields.SchemaField({
base: new fields.NumberField({ initial: 0, integer: true }),
bonus: new fields.NumberField({ initial: 0, integer: true }),
nonletaux: new fields.NumberField({ initial: 0, integer: true }),
letaux: new fields.NumberField({ initial: 0, integer: true }),
malusmanuel: new fields.NumberField({ initial: 0, integer: true }),
sequelles: new fields.StringField({ initial: "" })
}),
ame: new fields.SchemaField({
fullmax: new fields.NumberField({ initial: 0, integer: true }),
currentmax: new fields.NumberField({ initial: 0, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true }),
traumatismes: new fields.StringField({ initial: "" })
}),
combat: new fields.SchemaField({
initbonus: new fields.NumberField({ initial: 0, integer: true }),
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
monte: new fields.BooleanField({ initial: false })
}),
balance: new fields.SchemaField({
loi: new fields.NumberField({ initial: 0, integer: true }),
chaos: new fields.NumberField({ initial: 0, integer: true }),
aspect: new fields.NumberField({ initial: 0, integer: true }),
marge: new fields.NumberField({ initial: 0, integer: true }),
pointschaos: new fields.NumberField({ initial: 0, integer: true }),
pointsloi: new fields.NumberField({ initial: 0, integer: true })
}),
ressources: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
})
};
}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* Data model pour les dons
*/
export default class DonDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
allegeance: new fields.StringField({ initial: "" }),
prerequis: new fields.StringField({ initial: "" }),
sacrifice: new fields.HTMLField({ initial: "" })
};
}
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Data model pour les équipements
*/
export default class EquipementDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
prix: new fields.NumberField({ initial: 0, integer: true })
};
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model pour les héritages
*/
export default class HeritageDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+28
View File
@@ -0,0 +1,28 @@
/**
* Index des DataModels pour Mournblade
* Ce fichier centralise tous les exports des modèles de données
*/
// Modèles d'items
export { default as ArmeDataModel } from './arme.mjs';
export { default as BouclierDataModel } from './bouclier.mjs';
export { default as CapaciteDataModel } from './capacite.mjs';
export { default as CompetenceDataModel } from './competence.mjs';
export { default as DonDataModel } from './don.mjs';
export { default as EquipementDataModel } from './equipement.mjs';
export { default as HeritageDataModel } from './heritage.mjs';
export { default as MetierDataModel } from './metier.mjs';
export { default as ModifierDataModel } from './modifier.mjs';
export { default as MonnaieDataModel } from './monnaie.mjs';
export { default as OrigineDataModel } from './origine.mjs';
export { default as PacteDataModel } from './pacte.mjs';
export { default as ProtectionDataModel } from './protection.mjs';
export { default as RuneDataModel } from './rune.mjs';
export { default as RuneEffectDataModel } from './runeeffect.mjs';
export { default as TendanceDataModel } from './tendance.mjs';
export { default as TraitChaotiqueDataModel } from './traitchaotique.mjs';
export { default as TraitEspeceDataModel } from './traitespece.mjs';
// Modèles d'acteurs
export { default as PersonnageDataModel } from './personnage.mjs';
export { default as CreatureDataModel } from './creature.mjs';
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model pour les métiers
*/
export default class MetierDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+18
View File
@@ -0,0 +1,18 @@
/**
* Data model pour les modificateurs
*/
export default class ModifierDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
modifiertype: new fields.StringField({ initial: "roll" }),
value: new fields.NumberField({ initial: 0, integer: true }),
attribut: new fields.StringField({ initial: "aucun" }),
competence: new fields.StringField({ initial: "aucun" }),
permanent: new fields.BooleanField({ initial: true }),
once: new fields.BooleanField({ initial: false }),
duree: new fields.StringField({ initial: "" })
};
}
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Data model pour les monnaies
*/
export default class MonnaieDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
quantite: new fields.NumberField({ initial: 0, integer: true }),
unite: new fields.StringField({ initial: "" })
};
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model pour les origines
*/
export default class OrigineDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+12
View File
@@ -0,0 +1,12 @@
/**
* Data model pour les pactes
*/
export default class PacteDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
allegeance: new fields.StringField({ initial: "" })
};
}
}
+100
View File
@@ -0,0 +1,100 @@
/**
* Data model pour les personnages
*/
export default class PersonnageDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
// Template biodata
biodata: new fields.SchemaField({
name: new fields.StringField({ initial: "" }),
age: new fields.NumberField({ initial: 0, integer: true }),
alignement: new fields.StringField({ initial: "" }),
poids: new fields.StringField({ initial: "" }),
taille: new fields.StringField({ initial: "" }),
cheveux: new fields.StringField({ initial: "" }),
sexe: new fields.StringField({ initial: "" }),
yeux: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" }),
amemultiplier: new fields.NumberField({ initial: 2, integer: true }),
ignoreamemalus: new fields.BooleanField({ initial: false }),
ignoresantemalus: new fields.BooleanField({ initial: false }),
notes: new fields.HTMLField({ initial: "" }),
gmnotes: new fields.HTMLField({ initial: "" })
}),
// Template core
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
attributs: new fields.SchemaField({
adr: new fields.SchemaField({
label: new fields.StringField({ initial: "Adresse" }),
labelnorm: new fields.StringField({ initial: "adresse" }),
abbrev: new fields.StringField({ initial: "adr" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pui: new fields.SchemaField({
label: new fields.StringField({ initial: "Puissance" }),
labelnorm: new fields.StringField({ initial: "puissance" }),
abbrev: new fields.StringField({ initial: "pui" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
cla: new fields.SchemaField({
label: new fields.StringField({ initial: "Clairvoyance" }),
labelnorm: new fields.StringField({ initial: "clairvoyance" }),
abbrev: new fields.StringField({ initial: "cla" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pre: new fields.SchemaField({
label: new fields.StringField({ initial: "Présence" }),
labelnorm: new fields.StringField({ initial: "presence" }),
abbrev: new fields.StringField({ initial: "pre" }),
value: new fields.NumberField({ initial: 0, integer: true })
}),
tre: new fields.SchemaField({
label: new fields.StringField({ initial: "Trempe" }),
labelnorm: new fields.StringField({ initial: "trempe" }),
abbrev: new fields.StringField({ initial: "tre" }),
value: new fields.NumberField({ initial: 0, integer: true })
})
}),
bonneaventure: new fields.SchemaField({
base: new fields.NumberField({ initial: 0, integer: true }),
actuelle: new fields.NumberField({ initial: 0, integer: true })
}),
experience: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
eclat: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
sante: new fields.SchemaField({
base: new fields.NumberField({ initial: 0, integer: true }),
bonus: new fields.NumberField({ initial: 0, integer: true }),
nonletaux: new fields.NumberField({ initial: 0, integer: true }),
letaux: new fields.NumberField({ initial: 0, integer: true }),
malusmanuel: new fields.NumberField({ initial: 0, integer: true }),
sequelles: new fields.StringField({ initial: "" })
}),
ame: new fields.SchemaField({
fullmax: new fields.NumberField({ initial: 0, integer: true }),
currentmax: new fields.NumberField({ initial: 0, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true }),
traumatismes: new fields.StringField({ initial: "" })
}),
combat: new fields.SchemaField({
initbonus: new fields.NumberField({ initial: 0, integer: true }),
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
monte: new fields.BooleanField({ initial: false })
}),
balance: new fields.SchemaField({
loi: new fields.NumberField({ initial: 0, integer: true }),
chaos: new fields.NumberField({ initial: 0, integer: true }),
aspect: new fields.NumberField({ initial: 0, integer: true }),
marge: new fields.NumberField({ initial: 0, integer: true }),
pointschaos: new fields.NumberField({ initial: 0, integer: true }),
pointsloi: new fields.NumberField({ initial: 0, integer: true })
})
};
}
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Data model pour les protections
*/
export default class ProtectionDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
typeprotection: new fields.StringField({ initial: "" }),
protection: new fields.NumberField({ initial: 0, integer: true }),
degats: new fields.StringField({ initial: "" }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
prix: new fields.NumberField({ initial: 0, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}
+15
View File
@@ -0,0 +1,15 @@
/**
* Data model pour les runes
*/
export default class RuneDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
formule: new fields.StringField({ initial: "" }),
seuil: new fields.NumberField({ initial: 0, integer: true }),
prononcee: new fields.StringField({ initial: "" }),
tracee: new fields.StringField({ initial: "" })
};
}
}
+15
View File
@@ -0,0 +1,15 @@
/**
* Data model pour les effets de runes
*/
export default class RuneEffectDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
rune: new fields.StringField({ initial: "" }),
mode: new fields.StringField({ initial: "" }),
duree: new fields.StringField({ initial: "" }),
pointame: new fields.NumberField({ initial: 0, integer: true })
};
}
}
+12
View File
@@ -0,0 +1,12 @@
/**
* Data model pour les tendances
*/
export default class TendanceDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
allegeance: new fields.StringField({ initial: "" })
};
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model pour les traits chaotiques
*/
export default class TraitChaotiqueDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Data model pour les traits d'espèce
*/
export default class TraitEspeceDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
+323 -71
View File
@@ -1,6 +1,6 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
import { MournbladeUtility } from "./mournblade-utility.js"; import { MournbladeUtility } from "./mournblade-utility.js";
import { MournbladeRollDialog } from "./mournblade-roll-dialog.js"; import { MournbladeRollDialog } from "./applications/mournblade-roll-dialog.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10] const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10]
@@ -12,6 +12,34 @@ const __vitesseBonus = [-2, -2, -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6
* @extends {Actor} * @extends {Actor}
*/ */
export class MournbladeActor extends Actor { export class MournbladeActor extends Actor {
prepareData() {
super.prepareData();
// Calculate derived attributes
const data = this.system;
if (this.type == 'personnage') {
// Compute base health and max soul from attributes (derived, no DB write needed)
data.sante.base = data.sante.bonus + (data.attributs.pui.value + data.attributs.tre.value) * 2 + 5;
data.ame.fullmax = (data.attributs.cla.value + data.attributs.tre.value) * data.biodata.amemultiplier + 5;
}
// Calculate total health
data.sante.total = data.sante.base + data.sante.bonus;
// Calculate current health
data.sante.current = data.sante.total - data.sante.nonletaux - data.sante.letaux;
// Calculate total ame
data.ame.total = data.ame.fullmax;
// Calculate balance
data.balance.total = data.balance.loi + data.balance.chaos;
}
prepareDerivedData() {
super.prepareDerivedData();
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
@@ -31,7 +59,7 @@ export class MournbladeActor extends Actor {
if (data instanceof Array) { if (data instanceof Array) {
return super.create(data, options); return super.create(data, options);
} }
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic // If the created actor has items (only applicable to foundry.utils.duplicated actors) bypass the new actor creation logic
if (data.items) { if (data.items) {
let actor = super.create(data, options); let actor = super.create(data, options);
return actor; return actor;
@@ -41,47 +69,84 @@ export class MournbladeActor extends Actor {
const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills") const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills")
data.items = skills.map(i => i.toObject()) data.items = skills.map(i => i.toObject())
} }
if (data.type == 'pnj') { if (data.type == 'creature') {
const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills-creatures")
data.items = skills.map(i => i.toObject())
data.items.push({ name: "Arme naturelle 1", type: 'arme', img: "systems/fvtt-mournblade/assets/icons/arme.webp", system: { typearme: "contact", bonusmaniementoff: 0, seuildefense: 0, degats: "0" } })
data.items.push({ name: "Arme naturelle 2", type: 'arme', img: "systems/fvtt-mournblade/assets/icons/arme.webp", system: { typearme: "contact", bonusmaniementoff: 0, seuildefense: 0, degats: "0" } })
} }
return super.create(data, options); return super.create(data, options);
} }
/* -------------------------------------------- */
setModifier(name, type, value) {
this.createEmbeddedDocuments("Item", [{ type: "modifier", name: name, system: { modifiertype: type, value: value } }])
ui.notifications.info("Le modificateur " + name + " a été ajouté à " + this.name + ".")
}
/* -------------------------------------------- */ /* -------------------------------------------- */
prepareArme(arme) { prepareArme(arme) {
arme = duplicate(arme) arme = foundry.utils.duplicate(arme)
let combat = this.getCombatValues() let combat = this.getCombatValues()
if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") { if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") {
arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée")) arme.system.isMelee = true
let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée")
if (competence) {
arme.system.competence = foundry.utils.duplicate(competence)
arme.system.attrKey = "pui" arme.system.attrKey = "pui"
arme.system.totalDegats = arme.system.degats + "+" + combat.bonusDegatsTotal arme.system.totalDegats = arme.system.degats + "+" + combat.bonusDegatsTotal
arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + combat.attaqueModifier
if (arme.system.isdefense) { if (arme.system.isdefense) {
arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.bonusmaniementdef arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.bonusmaniementdef
} }
} else {
arme.system.competence = null
arme.system.totalOffensif = 0
arme.system.totalDegats = arme.system.degats
arme.system.totalDefensif = 0
}
} }
if (arme.system.typearme == "jet" || arme.system.typearme == "tir") { if (arme.system.typearme == "jet" || arme.system.typearme == "tir") {
arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance")) arme.system.isDistance = true
let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance")
if (competence) {
arme.system.competence = foundry.utils.duplicate(competence)
arme.system.attrKey = "adr" arme.system.attrKey = "adr"
arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + combat.attaqueModifier
arme.system.totalDegats = arme.system.degats arme.system.totalDegats = arme.system.degats
if (arme.system.isdefense) { if (arme.system.isdefense) {
arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.bonusmaniementdef arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.bonusmaniementdef
} }
} else {
arme.system.competence = null
arme.system.totalOffensif = 0
arme.system.totalDegats = arme.system.degats
arme.system.totalDefensif = 0
}
} }
return arme return arme
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
prepareBouclier(bouclier) { prepareBouclier(bouclier) {
bouclier = duplicate(bouclier) bouclier = foundry.utils.duplicate(bouclier)
let combat = this.getCombatValues() let combat = this.getCombatValues()
bouclier.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée")) let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée")
if (competence) {
bouclier.system.competence = foundry.utils.duplicate(competence)
bouclier.system.attrKey = "pui" bouclier.system.attrKey = "pui"
bouclier.system.totalDegats = bouclier.system.degats + "+" + combat.bonusDegatsTotal bouclier.system.totalDegats = bouclier.system.degats + "+" + combat.bonusDegatsTotal
bouclier.system.totalOffensif = this.system.attributs.pui.value + bouclier.system.competence.system.niveau bouclier.system.totalOffensif = this.system.attributs.pui.value + bouclier.system.competence.system.niveau
bouclier.system.isdefense = true bouclier.system.isdefense = true
bouclier.system.bonusmaniementoff = 0 bouclier.system.bonusmaniementoff = 0
bouclier.system.totalDefensif = combat.defenseTotal + bouclier.system.competence.system.niveau + bouclier.system.bonusdefense bouclier.system.totalDefensif = combat.defenseTotal + bouclier.system.competence.system.niveau + bouclier.system.bonusdefense
} else {
bouclier.system.competence = null
bouclier.system.totalOffensif = 0
bouclier.system.totalDegats = bouclier.system.degats
bouclier.system.totalDefensif = 0
}
return bouclier return bouclier
} }
@@ -101,14 +166,31 @@ export class MournbladeActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getItemSorted( types) { getModifiersForRoll() {
let items = this.items.filter(item => types.includes(item.type )) || [] let modifiers = []
for (let mod of this.items) {
if (mod.type == "modifier" && mod.system.modifiertype == "roll") {
let modObj = mod.toObject()
modObj.system.apply = true
modifiers.push(modObj)
}
}
MournbladeUtility.sortArrayObjectsByName(modifiers)
return modifiers
}
/* -------------------------------------------- */
getItemSorted(types) {
let items = this.items.filter(item => types.includes(item.type)) || []
MournbladeUtility.sortArrayObjectsByName(items) MournbladeUtility.sortArrayObjectsByName(items)
return items return items
} }
getDons() { getDons() {
return this.getItemSorted(["don"]) return this.getItemSorted(["don"])
} }
getPactes() {
return this.getItemSorted(["pacte"])
}
getTendances() { getTendances() {
return this.getItemSorted(["tendance"]) return this.getItemSorted(["tendance"])
} }
@@ -118,15 +200,24 @@ export class MournbladeActor extends Actor {
getEquipments() { getEquipments() {
return this.getItemSorted(["equipement"]) return this.getItemSorted(["equipement"])
} }
getModifiers() {
return this.getItemSorted(["modifier"])
}
getTraitsChaotiques() { getTraitsChaotiques() {
return this.getItemSorted(["traitchaotique"]) return this.getItemSorted(["traitchaotique"])
} }
getTraitsEspeces() {
return this.getItemSorted(["traitespece"])
}
getMonnaies() { getMonnaies() {
return this.getItemSorted(["monnaie"]) return this.getItemSorted(["monnaie"])
} }
getArmors() { getArmors() {
return this.getItemSorted(["protection"]) return this.getItemSorted(["protection"])
} }
getRuneEffects() {
return this.getItemSorted(["runeeffect"])
}
getOrigine() { getOrigine() {
return this.items.find(item => item.type == "origine") return this.items.find(item => item.type == "origine")
} }
@@ -140,7 +231,7 @@ export class MournbladeActor extends Actor {
getSkills() { getSkills() {
let comp = [] let comp = []
for (let item of this.items) { for (let item of this.items) {
item = duplicate(item) item = foundry.utils.duplicate(item)
if (item.type == "competence") { if (item.type == "competence") {
item.system.attribut1total = item.system.niveau + (this.system.attributs[item.system.attribut1]?.value || 0) item.system.attribut1total = item.system.niveau + (this.system.attributs[item.system.attribut1]?.value || 0)
item.system.attribut2total = item.system.niveau + (this.system.attributs[item.system.attribut2]?.value || 0) item.system.attribut2total = item.system.niveau + (this.system.attributs[item.system.attribut2]?.value || 0)
@@ -160,12 +251,23 @@ export class MournbladeActor extends Actor {
return comp return comp
} }
/* -------------------------------------------- */
getProtectionTotal() {
let protection = 0
for (let item of this.items) {
if (item.type == "protection" && item.system.equipped) {
protection += item.system.protection
}
}
return protection
}
/* -------------------------------------------- */ /* -------------------------------------------- */
getAspect() { getAspect() {
return (this.system.balance.loi > this.system.balance.chaos) ? this.system.balance.loi : this.system.balance.chaos return (this.system.balance.loi > this.system.balance.chaos) ? this.system.balance.loi : this.system.balance.chaos
} }
getMarge() { getMarge() {
return Math.abs( this.system.balance.loi - this.system.balance.chaos) return Math.abs(this.system.balance.loi - this.system.balance.chaos)
} }
getAlignement() { getAlignement() {
return (this.system.balance.loi > this.system.balance.chaos) ? "loyal" : "chaotique" return (this.system.balance.loi > this.system.balance.chaos) ? "loyal" : "chaotique"
@@ -183,7 +285,14 @@ export class MournbladeActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
getCombatValues() { getCombatValues() {
let defenserModifier = 0
this.items.filter(item => item.type == "modifier" && item.system.modifiertype == "defense").map(e => defenserModifier += e.system.value)
let attaqueModifier = 0
this.items.filter(item => item.type == "modifier" && item.system.modifiertype == "attaque").map(e => attaqueModifier += e.system.value)
let combat = { let combat = {
defenserModifier,
attaqueModifier,
initBase: this.system.attributs.adr.value, initBase: this.system.attributs.adr.value,
initTotal: this.system.attributs.adr.value + this.system.combat.initbonus, initTotal: this.system.attributs.adr.value + this.system.combat.initbonus,
bonusDegats: this.getBonusDegats(), bonusDegats: this.getBonusDegats(),
@@ -191,37 +300,11 @@ export class MournbladeActor extends Actor {
vitesseBase: this.getVitesseBase(), vitesseBase: this.getVitesseBase(),
vitesseTotal: this.getVitesseBase() + this.system.combat.vitessebonus, vitesseTotal: this.getVitesseBase() + this.system.combat.vitessebonus,
defenseBase: this.getDefenseBase(), defenseBase: this.getDefenseBase(),
defenseTotal: this.getDefenseBase() + this.system.combat.defensebonus defenseTotal: this.getDefenseBase() + this.system.combat.defensebonus + defenserModifier
} }
return combat return combat
} }
/* -------------------------------------------- */
prepareBaseData() {
}
/* -------------------------------------------- */
async prepareData() {
super.prepareData();
}
/* -------------------------------------------- */
prepareDerivedData() {
if (this.type == 'personnage') {
let newSante = this.system.sante.bonus + (this.system.attributs.pui.value + this.system.attributs.tre.value) * 2 + 5
if (this.system.sante.base != newSante) {
this.update({ 'system.sante.base': newSante })
}
let newAme = (this.system.attributs.cla.value + this.system.attributs.tre.value) * this.system.biodata.amemultiplier + 5
if (this.system.ame.fullmax != newAme) {
this.update({ 'system.ame.fullmax': newAme })
}
}
super.prepareDerivedData()
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_preUpdate(changed, options, user) { _preUpdate(changed, options, user) {
@@ -232,7 +315,7 @@ export class MournbladeActor extends Actor {
getItemById(id) { getItemById(id) {
let item = this.items.find(item => item.id == id); let item = this.items.find(item => item.id == id);
if (item) { if (item) {
item = duplicate(item) item = foundry.utils.duplicate(item)
} }
return item; return item;
} }
@@ -240,12 +323,49 @@ export class MournbladeActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async equipItem(itemId) { async equipItem(itemId) {
let item = this.items.find(item => item.id == itemId) let item = this.items.find(item => item.id == itemId)
if (item && item.system) { if (item?.system) {
let update = { _id: item.id, "system.equipped": !item.system.equipped } let update = { _id: item.id, "system.equipped": !item.system.equipped }
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
} }
} }
/* -------------------------------------------- */
getStatusMalus() {
if (this.system.biodata.ignoresantemalus) {
return 0
}
let malusL = 0
let malusNL = 0
if (this.system.sante.base - this.system.sante.letaux < 10) {
malusL = -2
}
if (this.system.sante.base - this.system.sante.letaux < 5) {
malusL = -5
}
if (this.system.sante.base - this.system.sante.nonletaux < 10) {
malusNL = -2
}
if (this.system.sante.base - this.system.sante.nonletaux < 5) {
malusNL = -5
}
return Math.min(malusL, malusNL)
}
/* -------------------------------------------- */
getAmeMalus() {
if (this.system.biodata.ignoreamemalus) {
return 0
}
let malusA = 0
if (this.system.ame.currentmax - this.system.ame.value < 10) {
malusA = -2
}
if (this.system.ame.currentmax - this.system.ame.value < 5) {
malusA = -5
}
return malusA
}
/* -------------------------------------------- */ /* -------------------------------------------- */
editItemField(itemId, itemType, itemField, dataType, value) { editItemField(itemId, itemType, itemField, dataType, value) {
let item = this.items.find(item => item.id == itemId) let item = this.items.find(item => item.id == itemId)
@@ -261,6 +381,44 @@ export class MournbladeActor extends Actor {
} }
} }
/* -------------------------------------------- */
incDecSante(type, value, applyArmure = true) {
value = Number(value)
if (value && applyArmure) {
let protection = this.getProtectionTotal()
value -= protection
value = Math.max(0, Number(value))
}
if (value) {
let newSante = foundry.utils.duplicate(this.system.sante)
newSante[type] += Number(value)
newSante[type] = Math.max(0, newSante[type])
if (newSante[type] > this.system.sante.base) {
value -= this.system.sante.base - newSante[type]
newSante[type] = this.system.sante.base
} else {
value = 0
}
newSante[type] = Math.min(newSante[type], newSante.base)
if (value && type == "nonletaux") {
newSante["letaux"] += value
}
this.update({ 'system.sante': newSante })
}
}
/* -------------------------------------------- */
incDecAme(value) {
value = Number(value)
if (value) {
let newAme = foundry.utils.duplicate(this.system.ame)
newAme.value += Number(value)
newAme.value = Math.max(0, newAme.value)
newAme.value = Math.min(newAme.value, newAme.currentmax)
this.update({ 'system.ame': newAme })
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
getBonneAventure() { getBonneAventure() {
return this.system.bonneaventure.actuelle return this.system.bonneaventure.actuelle
@@ -291,13 +449,13 @@ export class MournbladeActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
subPointsAme(runeMode, value) { subPointsAme(runeMode, value) {
let ame = duplicate(this.system.ame) let ame = foundry.utils.duplicate(this.system.ame)
if(runeMode == "prononcer") { if (runeMode == "prononcer") {
ame.value -= value ame.value += value
} else { } else {
ame.currentmax -= value ame.currentmax -= value
} }
this.update( {'system.ame': ame}) this.update({ 'system.ame': ame })
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -318,6 +476,9 @@ export class MournbladeActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
getBonusDegats() { getBonusDegats() {
if (this.type == "creature") {
return 0
}
return __degatsBonus[this.system.attributs.pui.value] return __degatsBonus[this.system.attributs.pui.value]
} }
@@ -334,13 +495,13 @@ export class MournbladeActor extends Actor {
getSubActors() { getSubActors() {
let subActors = []; let subActors = [];
for (let id of this.system.subactors) { for (let id of this.system.subactors) {
subActors.push(duplicate(game.actors.get(id))); subActors.push(foundry.utils.duplicate(game.actors.get(id)));
} }
return subActors; return subActors;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async addSubActor(subActorId) { async addSubActor(subActorId) {
let subActors = duplicate(this.system.subactors); let subActors = foundry.utils.duplicate(this.system.subactors);
subActors.push(subActorId); subActors.push(subActorId);
await this.update({ 'system.subactors': subActors }); await this.update({ 'system.subactors': subActors });
} }
@@ -371,34 +532,55 @@ export class MournbladeActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async setPredilectionUsed(compId, predIdx) { async setPredilectionUsed(compId, predIdx) {
let comp = this.items.get(compId) let comp = this.items.get(compId)
let pred = duplicate(comp.system.predilections) let pred = foundry.utils.duplicate(comp.system.predilections)
pred[predIdx].used = true pred[predIdx].used = true
await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }]) await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }])
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getInitiativeScore( ) { getInitiativeScore() {
return Number(this.system.attributs.adr.value) + Number(this.system.combat.initbonus) return Number(this.system.attributs.adr.value) + Number(this.system.combat.initbonus)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getBestDefenseValue() { getBestDefenseValue() {
let defenseList = this.items.filter(item => (item.type =="arme" || item.type == "bouclier") && item.system.equipped) let defenseList = this.items.filter(item => (item.type == "arme" || item.type == "bouclier") && item.system.equipped)
let maxDef = 0 let maxDef = 0
let bestArme let bestArme
for(let arme of defenseList) { for (let arme of defenseList) {
if (arme.type == "arme" && arme.system.isdefense) { if (arme.type == "arme" && arme.system.isdefense) {
arme = this.prepareArme(arme) arme = this.prepareArme(arme)
} }
if (arme.type == "bouclier" ) { if (arme.type == "bouclier") {
arme = this.prepareBouclier(arme) arme = this.prepareBouclier(arme)
} }
if ( arme.system.totalDefensif > maxDef) { if (arme.system.totalDefensif > maxDef) {
maxDef = arme.system.totalDefensif maxDef = arme.system.totalDefensif
bestArme = duplicate(arme) bestArme = foundry.utils.duplicate(arme)
} }
} }
return bestArme return bestArme
} }
/* -------------------------------------------- */
depenseRessources(arme) {
if (arme.system.nbressources && Number(arme.system.nbressources) > 0) {
if (this.type == "creature") {
let ressources = foundry.utils.duplicate(this.system.ressources)
if (Number(ressources.value) >= Number(arme.system.nbressources)) {
ressources.value -= arme.system.nbressources
this.update({ 'system.ressources': ressources })
ChatMessage.create({
content: "L'utilisation de la capacité/arme a dépensé " + arme.system.nbressources + " ressources.",
whisper: game.user.id,
user: game.user.id
});
} else {
ui.notifications.warn("Points de ressources insuffisants.")
}
} else {
ui.notifications.warn("Les ressources ne sont pas disponibles pour les personnages.")
}
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
getCommonRollData(attrKey = undefined, compId = undefined, compName = undefined) { getCommonRollData(attrKey = undefined, compId = undefined, compName = undefined) {
@@ -411,20 +593,29 @@ export class MournbladeActor extends Actor {
rollData.canEclatDoubleD20 = this.canEclatDoubleD20() rollData.canEclatDoubleD20 = this.canEclatDoubleD20()
rollData.doubleD20 = false rollData.doubleD20 = false
rollData.attributs = MournbladeUtility.getAttributs() rollData.attributs = MournbladeUtility.getAttributs()
rollData.selectDifficulte = true
rollData.malusSante = this.getStatusMalus() + this.system.sante.malusmanuel
rollData.malusAme = this.getAmeMalus()
rollData.modifiers = this.getModifiersForRoll()
rollData.desavantages = {}
rollData.isMonte = this.system.combat.monte
if (rollData.isMonte) {
rollData.config.attaques["chargecavalerie"] = "Charge de cavalerie"
}
if (attrKey) { if (attrKey) {
rollData.attrKey = attrKey rollData.attrKey = attrKey
if (attrKey != "tochoose") { if (attrKey != "tochoose") {
rollData.actionImg = "systems/fvtt-mournblade/assets/icons/" + this.system.attributs[attrKey].labelnorm + ".webp" rollData.actionImg = "systems/fvtt-mournblade/assets/icons/" + this.system.attributs[attrKey].labelnorm + ".webp"
rollData.attr = duplicate(this.system.attributs[attrKey]) rollData.attr = foundry.utils.duplicate(this.system.attributs[attrKey])
} }
} }
if (compId) { if (compId) {
rollData.competence = duplicate(this.items.get(compId) || {}) rollData.competence = foundry.utils.duplicate(this.items.get(compId) || {})
rollData.actionImg = rollData.competence?.img rollData.actionImg = rollData.competence?.img
} }
if (compName) { if (compName) {
rollData.competence = duplicate(this.items.find( item => item.name.toLowerCase() == compName.toLowerCase()) || {}) rollData.competence = foundry.utils.duplicate(this.items.find(item => item.name.toLowerCase() == compName.toLowerCase()) || {})
rollData.actionImg = rollData.competence?.img rollData.actionImg = rollData.competence?.img
} }
return rollData return rollData
@@ -433,33 +624,30 @@ export class MournbladeActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollAttribut(attrKey) { async rollAttribut(attrKey) {
let rollData = this.getCommonRollData(attrKey) let rollData = this.getCommonRollData(attrKey)
let rollDialog = await MournbladeRollDialog.create(this, rollData) await MournbladeRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollCompetence(attrKey, compId) { async rollCompetence(attrKey, compId) {
let rollData = this.getCommonRollData(attrKey, compId) let rollData = this.getCommonRollData(attrKey, compId)
console.log("RollDatra", rollData) console.log("RollDatra", rollData)
let rollDialog = await MournbladeRollDialog.create(this, rollData) await MournbladeRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollRune(runeId) { async rollRune(runeId) {
let comp = this.items.find(comp => comp.type == "competence" && comp.name.toLowerCase() == "savoir : runes") let comp = this.items.find(comp => comp.type == "competence" && comp.name.toLowerCase() == "savoir : runes")
if ( !comp) { if (!comp) {
ui.notifications.warn("La compétence Savoirs : Runes n'a pas été trouvée, abandon.") ui.notifications.warn("La compétence Savoirs : Runes n'a pas été trouvée, abandon.")
return return
} }
let rollData = this.getCommonRollData("cla", undefined, "Savoir : Runes") let rollData = this.getCommonRollData("cla", undefined, "Savoir : Runes")
rollData.rune = duplicate(this.items.get(runeId) || {}) rollData.rune = foundry.utils.duplicate(this.items.get(runeId) || {})
rollData.difficulte = rollData.rune?.system?.seuil || 0 rollData.difficulte = rollData.rune?.system?.seuil || 0
rollData.runemode = "prononcer" rollData.runemode = "prononcer"
rollData.runeame = 1 rollData.runeame = 1
console.log("runeData", rollData) console.log("runeData", rollData)
let rollDialog = await MournbladeRollDialog.create(this, rollData) await MournbladeRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -473,9 +661,71 @@ export class MournbladeActor extends Actor {
} }
let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id) let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id)
rollData.arme = arme rollData.arme = arme
rollData.typeAttaque = "assaut"
rollData.typeCouvert = "aucun"
rollData.hasDesavantageBonus = true
rollData.visee = false
rollData.ciblecourt = false
rollData.cibleconsciente = false
// Do not display difficulte if defense weapon or distance
if (rollData.armeDefense || rollData.arme.system.isDistance) {
rollData.selectDifficulte = false
rollData.difficulte = (rollData.arme.system.isDistance) ? 0 : rollData.difficulte
}
console.log("ARME!", rollData) console.log("ARME!", rollData)
let rollDialog = await MournbladeRollDialog.create(this, rollData) this.depenseRessources(arme)
rollDialog.render(true) await MournbladeRollDialog.create(this, rollData)
}
/* -------------------------------------------- */
async rollAssomer() {
let rollData = this.getCommonRollData("adr", undefined, "Filouterie")
rollData.typeAttaque = "assomer"
rollData.typeCouvert = "aucun"
rollData.hasDesavantageBonus = true
if (rollData.defender) {
rollData.selectDifficulte = false
rollData.difficulte = rollData.defender.system.attributs.tre.value * 2
}
console.log("Assomer!", rollData)
await MournbladeRollDialog.create(this, rollData)
}
/* -------------------------------------------- */
async rollFuir() {
let rollData = this.getCommonRollData("adr", undefined, "Mouvements")
rollData.typeAttaque = "fuir"
rollData.typeCouvert = "aucun"
rollData.hasDesavantageBonus = true
if (rollData.defender) {
rollData.selectDifficulte = false
let comp = rollData.defender.items.find(it => it.type == "competence" && it.name.toLowerCase() == "mouvements")
rollData.difficulte = rollData.defender.system.attributs.adr.value + ((comp) ? comp.system.niveau : rollData.defender.system.attributs.adr.value)
}
console.log("Fuir!", rollData)
await MournbladeRollDialog.create(this, rollData)
}
/* -------------------------------------------- */
async rollImmobiliser() {
let rollData = this.getCommonRollData("pui", undefined, "Mêlée")
rollData.typeAttaque = "immobiliser"
rollData.typeCouvert = "aucun"
rollData.hasDesavantageBonus = true
if (rollData.defender) {
rollData.selectDifficulte = false
rollData.difficulte = rollData.defenderCombatValues.defenseTotal
}
console.log("Immobiliser!", rollData)
await MournbladeRollDialog.create(this, rollData)
}
/* -------------------------------------------- */
async rollArmeSpecial(armeId) {
let arme = this.items.get(armeId)
if (arme) {
MournbladeUtility.createChatWithRollMode("GM", {
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-mournblade/templates/chat-display-description.hbs`, arme)
}, arme)
this.depenseRessources(arme)
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -487,9 +737,11 @@ export class MournbladeActor extends Actor {
if (arme.type == "bouclier") { if (arme.type == "bouclier") {
arme = this.prepareBouclier(arme) arme = this.prepareBouclier(arme)
} }
let roll = new Roll(arme.system.totalDegats).roll({ async: false }) //Unused rollData.degatsFormula = arme.system.totalDegats
let roll = await new Roll(arme.system.totalDegats).evaluate()
await MournbladeUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode")); await MournbladeUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode"));
let rollData = { let rollData = {
degatsFormula: arme.system.totalDegats,
arme: arme, arme: arme,
finalResult: roll.total, finalResult: roll.total,
alias: this.name, alias: this.name,
@@ -498,7 +750,7 @@ export class MournbladeActor extends Actor {
actionImg: arme.img, actionImg: arme.img,
} }
MournbladeUtility.createChatWithRollMode(rollData.alias, { MournbladeUtility.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-mournblade/templates/chat-degats-result.html`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-mournblade/templates/chat-degats-result-v2.hbs`, rollData)
}) })
} }
+1 -1
View File
@@ -10,7 +10,7 @@ export class MournbladeCombat extends Combat {
const c = this.combatants.get(ids[cId]); const c = this.combatants.get(ids[cId]);
let id = c._id || c.id; let id = c._id || c.id;
let initBonus = c.actor ? c.actor.getInitiativeScore() : 0 let initBonus = c.actor ? c.actor.getInitiativeScore() : 0
let roll = new Roll("1d10 + "+initBonus).roll({ async: false}) let roll = await new Roll("1d10 + "+initBonus).evaluate()
await MournbladeUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode")) await MournbladeUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode"))
//console.log("Init bonus", initBonus, roll.total) //console.log("Init bonus", initBonus, roll.total)
await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: roll.total } ]); await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: roll.total } ]);
+4 -5
View File
@@ -89,7 +89,7 @@ export class MournbladeCommands {
if (command && command.func) { if (command && command.func) {
const result = command.func(content, msg, params); const result = command.func(content, msg, params);
if (result == false) { if (result == false) {
RdDCommands._chatAnswer(msg, command.descr); MournbladeCommands._chatAnswer(msg, command.descr);
} }
return true; return true;
} }
@@ -98,8 +98,8 @@ export class MournbladeCommands {
/* -------------------------------------------- */ /* -------------------------------------------- */
async createChar(msg) { async createChar(msg) {
game.system.Mournblade.creator = new MournbladeActorCreate(); game.system.mournblade.creator = new MournbladeActorCreate();
game.system.Mournblade.creator.start(); game.system.mournblade.creator.start();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -116,8 +116,7 @@ export class MournbladeCommands {
rollData.mode = "generic" rollData.mode = "generic"
rollData.title = `Dice Pool Roll`; rollData.title = `Dice Pool Roll`;
let rollDialog = await MournbladeRollDialog.create( this, rollData); await MournbladeRollDialog.create( this, rollData);
rollDialog.render( true );
} }
} }
+81
View File
@@ -0,0 +1,81 @@
export class MournbladeConfig {
static getConfig() {
let MOURNBLADE_CONFIG = {
attaques: {
assaut: game.i18n.localize("MNBL.assaut"),
precise: game.i18n.localize("MNBL.preciseattack"),
feinte: game.i18n.localize("MNBL.feint"),
coupbas: game.i18n.localize("MNBL.dirtyattack"),
charger: game.i18n.localize("MNBL.charge"),
contenir: game.i18n.localize("MNBL.contain"),
desarmer: game.i18n.localize("MNBL.disarm")
},
couverts: {
aucun: { name: game.i18n.localize("MNBL.none"), value: 0 },
rondache: { name: game.i18n.localize("MNBL.lightcover"), value: -2 },
pavois: { name: game.i18n.localize("MNBL.mediumcover"), value: -5 },
complet: { name: game.i18n.localize("MNBL.heavycover"), value: -10 },
},
modifierTypes: {
aucun: { name: game.i18n.localize("MNBL.none"), value: 0 },
roll: { name: game.i18n.localize("MNBL.roll"), value: 0 },
degats: { name: game.i18n.localize("MNBL.damage"), value: 0 },
defense: { name: game.i18n.localize("MNBL.defensecapacity"), value: 0 },
attaque: { name: game.i18n.localize("MNBL.attackcapacity"), value: 0 },
},
listeNiveau: {
},
listeNiveauCreature: {
},
listePortees: {
"10": game.i18n.localize("MNBL.lessthanshort"),
"15": game.i18n.localize("MNBL.shortmore"),
"20": game.i18n.localize("MNBL.mediummore"),
"25": game.i18n.localize("MNBL.longmore")
},
modificateurOptions: {},
pointsAmeOptions: {},
difficulteOptions: {
"0": game.i18n.localize("MNBL.noneunknwon"),
"5": game.i18n.localize("MNBL.easy"),
"10": game.i18n.localize("MNBL.medium"),
"15": game.i18n.localize("MNBL.hard"),
"20": game.i18n.localize("MNBL.hazardous"),
"25": game.i18n.localize("MNBL.insane"),
"30": game.i18n.localize("MNBL.puremadness")
},
attributs: {
adr: game.i18n.localize("Adresse"), pui: game.i18n.localize("Puissance"),
cla: game.i18n.localize("Clairvoyance"), pre: game.i18n.localize("Présence"), tre: game.i18n.localize("Trempe")
},
lancementRuneOptions: {
prononcer: game.i18n.localize("MNBL.pronouncerune"),
inscrire: game.i18n.localize("MNBL.tracerune")
},
effetRuneOptions: {
prononcee: game.i18n.localize("MNBL.pronounced"),
inscrite: game.i18n.localize("MNBL.traced")
},
typeArmeOptions: {
contact: game.i18n.localize("MNBL.meleeweapon"),
contactjet: game.i18n.localize("MNBL.meleethrowweapon"),
jet: game.i18n.localize("MNBL.throwweapon"),
tir: game.i18n.localize("MNBL.shootweapon"),
special: game.i18n.localize("MNBL.specialweapon")
},
allegeanceOptions: {
tous: game.i18n.localize("MNBL.all"),
chaos: game.i18n.localize("MNBL.chaos"),
loi: game.i18n.localize("MNBL.law"),
betes: game.i18n.localize("MNBL.beastslords"),
elementaires: game.i18n.localize("MNBL.elementslords")
}
}
return MOURNBLADE_CONFIG;
}
};
+19 -18
View File
@@ -4,14 +4,14 @@ import { MournbladeUtility } from "./mournblade-utility.js";
* Extend the basic ItemSheet with some very simple modifications * Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet} * @extends {ItemSheet}
*/ */
export class MournbladeItemSheet extends ItemSheet { export class MournbladeItemSheet extends foundry.appv1.sheets.ItemSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-mournblade", "sheet", "item"], classes: ["fvtt-mournblade", "sheet", "item"],
template: "systems/fvtt-mournblade/templates/item-sheet.html", template: "systems/fvtt-mournblade/templates/item-sheet.hbs",
dragDrop: [{ dragSelector: null, dropSelector: null }], dragDrop: [{ dragSelector: null, dropSelector: null }],
width: 620, width: 620,
height: 550 height: 550
@@ -48,7 +48,7 @@ export class MournbladeItemSheet extends ItemSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
async getData() { async getData() {
const objectData = duplicate(this.object) const objectData = foundry.utils.duplicate(this.object)
let itemData = objectData let itemData = objectData
let formData = { let formData = {
title: this.title, title: this.title,
@@ -63,13 +63,14 @@ export class MournbladeItemSheet extends ItemSheet {
limited: this.object.limited, limited: this.object.limited,
options: this.options, options: this.options,
owner: this.document.isOwner, owner: this.document.isOwner,
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}), description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, {async: true}),
config: game.system.mournblade.config,
mr: (this.object.type == 'specialisation'), mr: (this.object.type == 'specialisation'),
isGM: game.user.isGM isGM: game.user.isGM
} }
if ( objectData.type == "don") { if ( objectData.type == "don") {
formData.sacrifice = await TextEditor.enrichHTML(this.object.system.sacrifice, {async: true}) formData.sacrifice = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.sacrifice, {async: true})
} }
//this.options.editable = !(this.object.origin == "embeddedItem"); //this.options.editable = !(this.object.origin == "embeddedItem");
console.log("ITEM DATA", formData, this); console.log("ITEM DATA", formData, this);
@@ -90,7 +91,7 @@ export class MournbladeItemSheet extends ItemSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
postItem() { postItem() {
let chatData = duplicate(MournbladeUtility.data(this.item)); let chatData = foundry.utils.duplicate(MournbladeUtility.data(this.item));
if (this.actor) { if (this.actor) {
chatData.actor = { id: this.actor.id }; chatData.actor = { id: this.actor.id };
} }
@@ -105,7 +106,7 @@ export class MournbladeItemSheet extends ItemSheet {
payload: chatData, payload: chatData,
}); });
renderTemplate('systems/fvtt-Mournblade-rpg/templates/post-item.html', chatData).then(html => { foundry.applications.handlebars.renderTemplate('systems/fvtt-mournblade/templates/post-item.hbs', chatData).then(html => {
let chatOptions = MournbladeUtility.chatDataSetup(html); let chatOptions = MournbladeUtility.chatDataSetup(html);
ChatMessage.create(chatOptions) ChatMessage.create(chatOptions)
}); });
@@ -133,28 +134,28 @@ export class MournbladeItemSheet extends ItemSheet {
html.find('.edit-prediction').change(ev => { html.find('.edit-prediction').change(ev => {
const li = $(ev.currentTarget).parents(".prediction-item") const li = $(ev.currentTarget).parents(".prediction-item")
let index = li.data("prediction-index") let index = li.data("prediction-index")
let pred = duplicate(this.object.system.predilections) let pred = foundry.utils.duplicate(this.object.system.predilections)
pred[index].name = ev.currentTarget.value pred[index].name = ev.currentTarget.value
this.object.update( { 'data.predilections': pred }) this.object.update( { 'system.predilections': pred })
}) })
html.find('.delete-prediction').click(ev => { html.find('.delete-prediction').click(ev => {
const li = $(ev.currentTarget).parents(".prediction-item") const li = $(ev.currentTarget).parents(".prediction-item")
let index = li.data("prediction-index") let index = li.data("prediction-index")
let pred = duplicate(this.object.system.predilections) let pred = foundry.utils.duplicate(this.object.system.predilections)
pred.splice(index,1) pred.splice(index,1)
this.object.update( { 'data.predilections': pred }) this.object.update( { 'system.predilections': pred })
}) })
html.find('.use-prediction').change(ev => { html.find('.use-prediction').change(ev => {
const li = $(ev.currentTarget).parents(".prediction-item") const li = $(ev.currentTarget).parents(".prediction-item")
let index = li.data("prediction-index") let index = li.data("prediction-index")
let pred = duplicate(this.object.system.predilections) let pred = foundry.utils.duplicate(this.object.system.predilections)
pred[index].used = ev.currentTarget.checked pred[index].used = ev.currentTarget.checked
this.object.update( { 'data.predilections': pred }) this.object.update( { 'system.predilections': pred })
}) })
html.find('#add-predilection').click(ev => { html.find('#add-predilection').click(ev => {
let pred = duplicate(this.object.system.predilections) let pred = foundry.utils.duplicate(this.object.system.predilections)
pred.push( { name: "Nouvelle prédilection", used: false }) pred.push( { name: "Nouvelle prédilection", id: randomID(), used: false })
this.object.update( { 'data.predilections': pred }) this.object.update( { 'system.predilections': pred })
}) })
// Update Inventory Item // Update Inventory Item
html.find('.item-delete').click(ev => { html.find('.item-delete').click(ev => {
@@ -168,7 +169,7 @@ export class MournbladeItemSheet extends ItemSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
get template() { get template() {
let type = this.item.type; let type = this.item.type;
return `systems/fvtt-mournblade/templates/item-${type}-sheet.html`; return `systems/fvtt-mournblade/templates/item-${type}-sheet.hbs`;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
+2
View File
@@ -11,8 +11,10 @@ export const defaultItemImg = {
predilection: "systems/fvtt-mournblade/assets/icons/predilection.webp", predilection: "systems/fvtt-mournblade/assets/icons/predilection.webp",
protection: "systems/fvtt-mournblade/assets/icons/protection.webp", protection: "systems/fvtt-mournblade/assets/icons/protection.webp",
rune: "systems/fvtt-mournblade/assets/icons/rune.webp", rune: "systems/fvtt-mournblade/assets/icons/rune.webp",
runeeffect: "systems/fvtt-mournblade/assets/icons/rune.webp",
tendance: "systems/fvtt-mournblade/assets/icons/tendance.webp", tendance: "systems/fvtt-mournblade/assets/icons/tendance.webp",
traitchaotique: "systems/fvtt-mournblade/assets/icons/traitchaotique.webp", traitchaotique: "systems/fvtt-mournblade/assets/icons/traitchaotique.webp",
traitespece: "systems/fvtt-mournblade/assets/icons/capacite.webp"
} }
/** /**
+98 -41
View File
@@ -10,11 +10,19 @@
// Import Modules // Import Modules
import { MournbladeActor } from "./mournblade-actor.js"; import { MournbladeActor } from "./mournblade-actor.js";
import { MournbladeItemSheet } from "./mournblade-item-sheet.js"; import { MournbladeItemSheet } from "./mournblade-item-sheet.js";
import { MournbladeActorSheet } from "./mournblade-actor-sheet.js"; // import { MournbladeActorSheet } from "./mournblade-actor-sheet.js";
//import { MournbladeNPCSheet } from "./mournblade-npc-sheet.js"; // import { MournbladeCreatureSheet } from "./mournblade-creature-sheet.js";
import { MournbladeUtility } from "./mournblade-utility.js"; import { MournbladeUtility } from "./mournblade-utility.js";
import { MournbladeCombat } from "./mournblade-combat.js"; import { MournbladeCombat } from "./mournblade-combat.js";
import { MournbladeItem } from "./mournblade-item.js"; import { MournbladeItem } from "./mournblade-item.js";
import { MournbladeConfig } from "./mournblade-config.js";
// Import DataModels
import * as models from "./models/index.mjs";
// Import AppV2 Item Sheets
import * as sheets from "./applications/sheets/_module.mjs";
import { MournbladeRollDialog } from "./mournblade-roll-dialog.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Foundry VTT Initialization */ /* Foundry VTT Initialization */
@@ -36,7 +44,7 @@ Hooks.once("init", async function () {
}; };
/* -------------------------------------------- */ /* -------------------------------------------- */
game.socket.on("system.fvtt-mournblade-rpg", data => { game.socket.on("system.fvtt-mournblade", data => {
MournbladeUtility.onSocketMesssage(data); MournbladeUtility.onSocketMesssage(data);
}); });
@@ -44,58 +52,90 @@ Hooks.once("init", async function () {
// Define custom Entity classes // Define custom Entity classes
CONFIG.Combat.documentClass = MournbladeCombat CONFIG.Combat.documentClass = MournbladeCombat
CONFIG.Actor.documentClass = MournbladeActor CONFIG.Actor.documentClass = MournbladeActor
CONFIG.Actor.dataModels = {
personnage: models.PersonnageDataModel,
creature: models.CreatureDataModel,
}
CONFIG.Item.documentClass = MournbladeItem CONFIG.Item.documentClass = MournbladeItem
game.system.mournblade = { } CONFIG.Item.dataModels = {
arme: models.ArmeDataModel,
bouclier: models.BouclierDataModel,
capacite: models.CapaciteDataModel,
competence: models.CompetenceDataModel,
don: models.DonDataModel,
equipement: models.EquipementDataModel,
heritage: models.HeritageDataModel,
metier: models.MetierDataModel,
modifier: models.ModifierDataModel,
monnaie: models.MonnaieDataModel,
origine: models.OrigineDataModel,
pacte: models.PacteDataModel,
protection: models.ProtectionDataModel,
rune: models.RuneDataModel,
runeeffect: models.RuneEffectDataModel,
tendance: models.TendanceDataModel,
traitchaotique: models.TraitChaotiqueDataModel,
traitespece: models.TraitEspeceDataModel
}
game.system.mournblade = {
config : MournbladeConfig.getConfig(),
models,
sheets,
MournbladeRollDialog
}
/* -------------------------------------------- */ /* -------------------------------------------- */
// Register sheet application classes // Register sheet application classes
Actors.unregisterSheet("core", ActorSheet); foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
Actors.registerSheet("fvtt-mournblade", MournbladeActorSheet, { types: ["personnage"], makeDefault: true }) foundry.documents.collections.Actors.registerSheet("fvtt-mournblade", sheets.MournbladePersonnageSheet, { types: ["personnage"], makeDefault: true });
//Actors.registerSheet("fvtt-mournblade", MournbladeNPCSheet, { types: ["npc"], makeDefault: false }); foundry.documents.collections.Actors.registerSheet("fvtt-mournblade", sheets.MournbladeCreatureSheet, { types: ["creature"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet); foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
Items.registerSheet("fvtt-mournblade", MournbladeItemSheet, { makeDefault: true }) foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeArmeSheet, { types: ["arme"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeBouclierSheet, { types: ["bouclier"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeCapaciteSheet, { types: ["capacite"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeCompetenceSheet, { types: ["competence"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeDonSheet, { types: ["don"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeEquipementSheet, { types: ["equipement"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeHeritageSheet, { types: ["heritage"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeMetierSheet, { types: ["metier"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeModifierSheet, { types: ["modifier"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeMonnaieSheet, { types: ["monnaie"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeOrigineSheet, { types: ["origine"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladePacteSheet, { types: ["pacte"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeProtectionSheet, { types: ["protection"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeRuneSheet, { types: ["rune"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeRuneEffectSheet, { types: ["runeeffect"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeTendanceSheet, { types: ["tendance"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeTraitChaotiqueSheet, { types: ["traitchaotique"], makeDefault: true });
foundry.documents.collections.Items.registerSheet("fvtt-mournblade", sheets.MournbladeTraitEspeceSheet, { types: ["traitespece"], makeDefault: true });
MournbladeUtility.init(); MournbladeUtility.init();
}); });
/* -------------------------------------------- */ /* -------------------------------------------- */
function welcomeMessage() { async function welcomeMessage() {
const templateData = {};
const html = await foundry.applications.handlebars.renderTemplate("systems/fvtt-mournblade/templates/chat-welcome-message.hbs", templateData);
ChatMessage.create({ ChatMessage.create({
user: game.user.id, user: game.user.id,
whisper: [game.user.id], whisper: [game.user.id],
content: `<div id="welcome-message-Mournblade"><span class="rdd-roll-part"> content: html
<strong>Bienvenue dans les Jeunes Royaumes de Mournblade !</strong> });
<p>Les livres de Mournblade sont nécessaires pour jouer : https://www.titam-france.fr</p>
<p>Mournblade est jeu de rôle publié par Titam France/Sombres projets, tout les droits leur appartiennent.</p>
<p>Système développé par LeRatierBretonnien, support sur le <a href="https://discord.gg/pPSDNJk">Discord FR de Foundry</a>.</p>
` });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
// Register world usage statistics async function importDefaultScene() {
function registerUsageCount( registerKey ) { let exists = game.scenes.find(j => j.name == "Accueil");
if ( game.user.isGM ) { if (!exists) {
game.settings.register(registerKey, "world-key", { const scenes = await MournbladeUtility.loadCompendium("fvtt-mournblade.scenes")
name: "Unique world key", let newDocuments = scenes.filter(i => i.name == "Accueil");
scope: "world", await game.scenes.documentClass.create(newDocuments);
config: false, game.scenes.find(i => i.name == "Accueil").activate();
default: "",
type: String
});
let worldKey = game.settings.get(registerKey, "world-key")
if ( worldKey == undefined || worldKey == "" ) {
worldKey = randomID(32)
game.settings.set(registerKey, "world-key", worldKey )
}
// Simple API counter
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
//$.ajaxSetup({
//headers: { 'Access-Control-Allow-Origin': '*' }
//})
$.ajax(regURL)
} }
} }
@@ -104,17 +144,35 @@ function registerUsageCount( registerKey ) {
/* -------------------------------------------- */ /* -------------------------------------------- */
Hooks.once("ready", function () { Hooks.once("ready", function () {
game.system.mournblade = {
config : MournbladeConfig.getConfig(),
}
MournbladeUtility.ready(); MournbladeUtility.ready();
// User warning // User warning
if (!game.user.isGM && game.user.character == undefined) { if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Aucun personnage n'est relié au joueur !"); ui.notifications.info("Attention ! Aucun personnage n'est relié au joueur !");
ChatMessage.create({ ChatMessage.create({
content: "<b>ATTENTION</b> Le joueur " + game.user.name + " n'est relié à aucun personnage !", content: "<b>ATTENTION</b> Le joueur " + game.user.name + " n'est relié à aucun personnage !",
user: game.user._id user: game.user.id
});
}
if (!game.user.isGM && game.user.character && !game.user.character.prototypeToken.actorLink) {
ui.notifications.info("Le token de du joueur n'est pas connecté à l'acteur !");
ChatMessage.create({
content: "<b>ATTENTION</b> Le token du joueur " + game.user.name + " n'est pas connecté à l'acteur !",
user: game.user.id
}); });
} }
registerUsageCount('fvtt-mournblade') import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter=>{
console.log("ClassCounter loaded", moduleCounter)
moduleCounter.ClassCounter.registerUsageCount()
}).catch(err=>
console.log("No stats available, giving up.")
)
importDefaultScene();
welcomeMessage(); welcomeMessage();
}); });
@@ -131,4 +189,3 @@ Hooks.on("chatMessage", (html, content, msg) => {
} }
return true; return true;
}); });
+76 -35
View File
@@ -1,65 +1,73 @@
import { MournbladeUtility } from "./mournblade-utility.js"; import { MournbladeUtility } from "./mournblade-utility.js";
export class MournbladeRollDialog extends Dialog { export class MournbladeRollDialog extends Application {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async create(actor, rollData ) { static async create(actor, rollData ) {
return new MournbladeRollDialog(actor, rollData);
let options = { classes: ["MournbladeDialog"], width: 340, height: 'fit-content', 'z-index': 99999 };
let html = await renderTemplate('systems/fvtt-mournblade/templates/roll-dialog-generic.html', rollData);
return new MournbladeRollDialog(actor, rollData, html, options );
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(actor, rollData, html, options, close = undefined) { constructor(actor, rollData, options = {}) {
let conf = { super(options);
title: "Test de Capacité", this.actor = actor;
content: html, this.rollData = rollData;
buttons: {
rolld10: {
icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d10",
callback: () => { this.roll("1d10") }
},
rolld20: {
icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d20",
callback: () => { this.roll("1d20") }
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Annuler",
callback: () => { this.close() }
} },
close: close
} }
super(conf, options); static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-mournblade", "sheet", "item"],
template: "systems/fvtt-mournblade/templates/roll-dialog-generic.hbs",
width: 400,
height: "auto",
title: "Test de Capacité"
});
}
this.actor = actor getData() {
this.rollData = rollData const data = foundry.utils.duplicate(this.rollData);
if (!data.config) {
data.config = game.system.mournblade.config;
}
return data;
}
_onCancel() {
this.close();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
roll ( dice) { _onRoll(dice) {
this.rollData.mainDice = dice this.rollData.mainDice = dice;
MournbladeUtility.rollMournblade( this.rollData ) MournbladeUtility.rollMournblade(this.rollData);
this.close();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
activateListeners(html) { activateListeners(html) {
super.activateListeners(html); super.activateListeners(html);
var dialog = this; // Roll buttons
html.find(".rolld10").click(this._onRoll.bind(this, "1d10"));
html.find(".rolld20").click(this._onRoll.bind(this, "1d20"));
html.find(".cancel").click(this._onCancel.bind(this));
function onLoad() { function onLoad() {
} }
$(function () { onLoad(); }); $(function () { onLoad(); });
html.find('.apply-modifier').change(async (event) => {
let modifierIdx = $(event.currentTarget).data("modifier-idx")
let modifier = this.rollData.modifiers[modifierIdx]
modifier.system.apply = event.currentTarget.checked
})
html.find('#modificateur').change(async (event) => { html.find('#modificateur').change(async (event) => {
this.rollData.modificateur = Number(event.currentTarget.value) this.rollData.modificateur = Number(event.currentTarget.value)
}) })
html.find('#typeAttaque').change(async (event) => {
this.rollData.typeAttaque = String(event.currentTarget.value)
})
html.find('#difficulte').change(async (event) => { html.find('#difficulte').change(async (event) => {
this.rollData.difficulte = Number(event.currentTarget.value) this.rollData.difficulte = Number(event.currentTarget.value)
}) })
@@ -72,8 +80,41 @@ export class MournbladeRollDialog extends Dialog {
html.find('#runeame').change(async (event) => { html.find('#runeame').change(async (event) => {
this.rollData.runeame = Number(event.currentTarget.value) this.rollData.runeame = Number(event.currentTarget.value)
}) })
html.find('#isMonte').change(async (event) => {
this.rollData.desavantages.isMonte = event.currentTarget.checked
})
html.find('#cibleausol').change(async (event) => {
this.rollData.desavantages.cibleausol = event.currentTarget.checked
})
html.find('#cibledesarmee').change(async (event) => {
this.rollData.desavantages.cibledesarmee = event.currentTarget.checked
})
html.find('#ciblerestreint').change(async (event) => {
this.rollData.desavantages.ciblerestreint = event.currentTarget.checked
})
html.find('#cibleimmobilisée').change(async (event) => {
this.rollData.desavantages.cibleimmobilisée = event.currentTarget.checked
})
html.find('#ciblesurplomb').change(async (event) => {
this.rollData.desavantages.ciblesurplomb = event.currentTarget.checked
})
html.find('#doubleD20').change(async (event) => { html.find('#doubleD20').change(async (event) => {
this.rollData.doubleD20 = event.currentTarget.checked this.rollData.doubleD20 = event.currentTarget.checked
}) })
html.find('#visee').change(async (event) => {
this.rollData.visee = event.currentTarget.checked
})
html.find('#cibleconsciente').change(async (event) => {
this.rollData.cibleconsciente = event.currentTarget.checked
})
html.find('#ciblecourt').change(async (event) => {
this.rollData.ciblecourt = event.currentTarget.checked
})
html.find('#typeCouvert').change(async (event) => {
this.rollData.typeCouvert = String(event.currentTarget.value)
})
} }
} }
+294 -91
View File
@@ -8,15 +8,13 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async init() { static async init() {
Hooks.on('renderChatLog', (log, html, data) => MournbladeUtility.chatListeners(html)) Hooks.on('renderChatMessageHTML', (log, html, data) => MournbladeUtility.chatListeners(html))
Hooks.on("getChatLogEntryContext", (html, options) => MournbladeUtility.chatRollMenu(html, options)) Hooks.on("getChatMessageContextOptions", (html, options) => MournbladeUtility.chatRollMenu(html, options))
Hooks.on('renderChatMessageHTML', (message, html, data) => MournbladeUtility.chatMessageHandler(message, html, data))
Hooks.on("getCombatTrackerEntryContext", (html, options) => { Hooks.on("getCombatTrackerEntryContext", (html, options) => {
MournbladeUtility.pushInitiativeOptions(html, options); MournbladeUtility.pushInitiativeOptions(html, options);
}) })
Hooks.on("dropCanvasData", (canvas, data) => {
MournbladeUtility.dropItemOnToken(canvas, data)
});
this.rollDataStore = {} this.rollDataStore = {}
this.defenderStore = {} this.defenderStore = {}
@@ -58,15 +56,6 @@ export class MournbladeUtility {
return actor return actor
} }
/* -------------------------------------------- */
static getModificateurOptions() {
let opt = []
for (let i = -15; i <= 15; i++) {
opt.push(`<option value="${i}">${i}</option>`)
}
return opt.concat("\n")
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static sortArrayObjectsByName(myArray) { static sortArrayObjectsByName(myArray) {
myArray.sort((a, b) => { myArray.sort((a, b) => {
@@ -74,18 +63,9 @@ export class MournbladeUtility {
}) })
} }
/* -------------------------------------------- */
static getPointAmeOptions() {
let opt = []
for (let i = 1; i <= 20; i++) {
opt.push(`<option value="${i}">${i}</option>`)
}
return opt.concat("\n")
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static getAttributs() { static getAttributs() {
return { adr: "Adresse", pui: "Puissance", cla: "Clairvoyance", pre: "Présence", tre: "Trempe" } return game.system.mournblade.config.attributs
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static pushInitiativeOptions(html, options) { static pushInitiativeOptions(html, options) {
@@ -100,6 +80,11 @@ export class MournbladeUtility {
static async ready() { static async ready() {
const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills") const skills = await MournbladeUtility.loadCompendium("fvtt-mournblade.skills")
this.skills = skills.map(i => i.toObject()) this.skills = skills.map(i => i.toObject())
game.system.mournblade.config.listeNiveauSkill = MournbladeUtility.createDirectOptionList(0, 10)
game.system.mournblade.config.listeNiveauCreature = MournbladeUtility.createDirectOptionList(0, 35)
game.system.mournblade.config.modificateurOptions = MournbladeUtility.createArrayOptionList(-15, 15)
game.system.mournblade.config.pointsAmeOptions = MournbladeUtility.createDirectOptionList(0, 20)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -118,30 +103,72 @@ export class MournbladeUtility {
static getOptionsStatusList() { static getOptionsStatusList() {
return this.optionsStatusList; return this.optionsStatusList;
} }
/* -------------------------------------------- */
static getPredilection(comp, predIdx) {
let pred = foundry.utils.duplicate(comp.system.predilections)
return foundry.utils.duplicate(pred[predIdx] || { name: "Error!" })
}
/* -------------------------------------------- */
static async chatMessageHandler(message, html, data) {
const chatCard = $(html).find('.action-section')
if (chatCard.length > 0) {
// If the user is the message author or the actor owner, proceed
const actor = game.actors.get(data.message.speaker.actor)
// DEBUG : console.log("FOUND 1!!! ", actor, data.message)
if (actor?.isOwner || game.user.isGM) {
return
}
chatCard.hide()
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async chatListeners(html) { static async chatListeners(html) {
html.on("click", '.predilection-reroll', async event => { $(html).on("click", '.predilection-reroll', async event => {
let predIdx = $(event.currentTarget).data("predilection-index") let predIdx = $(event.currentTarget).data("predilection-index")
let messageId = MournbladeUtility.findChatMessageId(event.currentTarget) let messageId = MournbladeUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId) let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
await actor.setPredilectionUsed(rollData.competence._id, predIdx) await actor.setPredilectionUsed(rollData.competence._id, predIdx)
rollData.competence = duplicate(actor.getCompetence(rollData.competence._id)) rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
rollData.predilectionUsed = MournbladeUtility.getPredilection(rollData.competence, predIdx)
await MournbladeUtility.rollMournblade(rollData) await MournbladeUtility.rollMournblade(rollData)
}) })
$(html).on("click", '.arme-roll-degats', async event => {
let messageId = MournbladeUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "mournblade-roll")
MournbladeUtility.rollDegatsFromAttaque(rollData)
})
$(html).on("click", '.arme-apply-degats', async event => {
let messageId = MournbladeUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "mournblade-roll")
if (game.user.isGM) {
MournbladeUtility.applyDegatsFromAttaque(rollData)
} else {
game.socket.emit("system.fvtt-mournblade", { name: "msg_apply_damage", data: { rollData: rollData } })
}
})
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async preloadHandlebarsTemplates() { static async preloadHandlebarsTemplates() {
const templatePaths = [ const templatePaths = [
'systems/fvtt-mournblade/templates/editor-notes-gm.html', 'systems/fvtt-mournblade/templates/editor-notes-gm.hbs',
'systems/fvtt-mournblade/templates/partial-item-description.html', 'systems/fvtt-mournblade/templates/partial-item-description.hbs',
'systems/fvtt-mournblade/templates/partial-list-niveau.html' 'systems/fvtt-mournblade/templates/partial-item-header.hbs',
'systems/fvtt-mournblade/templates/partial-item-nav.hbs'
] ]
return loadTemplates(templatePaths); return foundry.applications.handlebars.loadTemplates(templatePaths);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -181,12 +208,10 @@ export class MournbladeUtility {
} }
return options; return options;
} }
static createArrayOptionList(min, max) {
/* -------------------------------------------- */ let options = [];
static buildListOptions(min, max) {
let options = ""
for (let i = min; i <= max; i++) { for (let i = min; i <= max; i++) {
options += `<option value="${i}">${i}</option>` options.push({ key: `${i}`, label: `${i}` });
} }
return options; return options;
} }
@@ -201,22 +226,6 @@ export class MournbladeUtility {
return undefined; return undefined;
} }
/* -------------------------------------------- */
static updateRollData(rollData) {
let id = rollData.rollId;
let oldRollData = this.rollDataStore[id] || {};
let newRollData = mergeObject(oldRollData, rollData);
this.rollDataStore[id] = newRollData;
}
/* -------------------------------------------- */
static saveRollData(rollData) {
game.socket.emit("system.fvtt-mournblade", {
name: "msg_update_roll", data: rollData
}); // Notify all other clients of the roll
this.updateRollData(rollData);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static getRollData(id) { static getRollData(id) {
return this.rollDataStore[id]; return this.rollDataStore[id];
@@ -224,11 +233,10 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static onSocketMesssage(msg) { static onSocketMesssage(msg) {
if (msg.name == "msg_update_defense_state") { if (msg.name == "msg_apply_damage") {
this.updateDefenseState(msg.data.defenderId, msg.data.rollId); if (game.user.isGM) {
this.applyDegatsFromAttaque(msg.data.rollData);
} }
if (msg.name == "msg_update_roll") {
this.updateRollData(msg.data);
} }
} }
@@ -286,19 +294,26 @@ export class MournbladeUtility {
if (diceValue % 2 == 1) { if (diceValue % 2 == 1) {
//console.log("PAIR/IMP2", diceValue) //console.log("PAIR/IMP2", diceValue)
rollData.finalResult -= rollData.roll.terms[0].results[0].result // Substract value rollData.finalResult -= rollData.roll.terms[0].results[0].result // Substract value
rollData.isImpair = true
if (diceValue == 1 || diceValue == 11) { if (diceValue == 1 || diceValue == 11) {
rollData.isDramatique = true rollData.isDramatique = true
rollData.isSuccess = false rollData.isSuccess = false
} }
} }
} }
this.computeQualityResult(rollData)
}
/* -------------------------------------------- */
static computeQualityResult(rollData) {
//console.log("Result : ", rollData) //console.log("Result : ", rollData)
if (rollData.difficulte > 0 && !rollData.isDramatique) { if (rollData.difficulte > 0 && !rollData.isDramatique) {
rollData.isSuccess = (rollData.finalResult >= rollData.difficulte) rollData.isSuccess = (rollData.finalResult >= rollData.difficulte)
rollData.isHeroique = ((rollData.finalResult - rollData.difficulte) >= 10) rollData.isHeroique = ((rollData.finalResult - rollData.difficulte) >= 10)
rollData.isDramatique = ((rollData.finalResult - rollData.difficulte) <= -10) rollData.isDramatique = ((rollData.finalResult - rollData.difficulte) <= -10)
rollData.isPureSuccess = (rollData.isSuccess && !rollData.isHeroique)
} }
rollData.isEchec = !rollData.isSuccess
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -310,7 +325,7 @@ export class MournbladeUtility {
} }
if (!rollData.attr) { if (!rollData.attr) {
rollData.actionImg = "systems/fvtt-mournblade/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp" rollData.actionImg = "systems/fvtt-mournblade/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
rollData.attr = duplicate(actor.system.attributs[rollData.attrKey]) rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
} }
rollData.diceFormula = rollData.mainDice rollData.diceFormula = rollData.mainDice
@@ -322,17 +337,59 @@ export class MournbladeUtility {
} }
//console.log("BEFORE COMP", rollData) //console.log("BEFORE COMP", rollData)
if (rollData.competence) { if (rollData.competence) {
rollData.predilections = duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || []) rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections)
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0 let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
rollData.diceFormula += `+${rollData.attr.value}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}` rollData.diceFormula += `+${rollData.attr.value}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
} else { } else {
rollData.diceFormula += `+${rollData.attr.value}*2+${rollData.modificateur}` rollData.diceFormula += `+${rollData.attr.value}*2+${rollData.modificateur}`
} }
rollData.diceFormula += `+${rollData.malusSante}+${rollData.malusAme}`
if (rollData.arme && rollData.arme.type == "arme") { if (rollData.arme?.type == "arme") {
rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}` rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
} }
// Apply modifiers
for (let modifier of rollData.modifiers) {
if (modifier.system.modifiertype == "roll" && modifier.system.apply) {
rollData.diceFormula += `+${modifier.system.value}`
}
}
// Apply desavantages
let desavantagesBonus = 0
for (let desavantage in rollData.desavantages) {
if (rollData.desavantages[desavantage]) {
desavantagesBonus += 5
}
}
desavantagesBonus = Math.min(15, desavantagesBonus)
rollData.diceFormula += `+${desavantagesBonus}`
// Monté ?
if (rollData.isMonte) {
rollData.diceFormula += "+5"
}
// Specific modifier for distance
if (rollData.arme?.system?.isDistance) {
if (rollData.visee) {
rollData.diceFormula += "+5"
}
if (rollData.cibleconsciente && rollData.defender) {
rollData.diceFormula += `-${rollData.defender.system.attributs.adr.value}`
}
if (rollData.ciblecourt) {
if (rollData.difficulte <= 15) { // Portée courte ou moins
rollData.diceFormula += `-5`
} else {
rollData.diceFormula += `-10`
}
}
if (rollData.typeCouvert && rollData.typeCouvert != "aucun" && rollData.config.couverts[rollData.typeCouvert]) {
rollData.diceFormula += `+${rollData.config.couverts[rollData.typeCouvert].value}`
}
}
if (rollData.rune) { if (rollData.rune) {
rollData.runeduree = Math.ceil((rollData.runeame + 3) / 3) rollData.runeduree = Math.ceil((rollData.runeame + 3) / 3)
if (rollData.runemode == "inscrire") { if (rollData.runemode == "inscrire") {
@@ -343,15 +400,16 @@ export class MournbladeUtility {
} }
} }
let myRoll = new Roll(rollData.diceFormula).roll({ async: false }) let myRoll = await new Roll(rollData.diceFormula).evaluate();
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
rollData.roll = duplicate(myRoll) rollData.roll = foundry.utils.duplicate(myRoll)
rollData.diceResult = myRoll.terms[0].results[0].result rollData.diceResult = myRoll.terms[0].results[0].result
console.log(">>>> ", myRoll) console.log(">>>> ", myRoll)
rollData.finalResult = myRoll.total rollData.finalResult = myRoll.total
this.computeResult(rollData) this.computeResult(rollData)
// Application immédiate selon type de jet
if (rollData.rune) { if (rollData.rune) {
let subAme = rollData.runeame let subAme = rollData.runeame
if (rollData.isEchec && !rollData.isDramatique) { if (rollData.isEchec && !rollData.isDramatique) {
@@ -359,34 +417,176 @@ export class MournbladeUtility {
} }
actor.subPointsAme(rollData.runemode, subAme) actor.subPointsAme(rollData.runemode, subAme)
} }
if (rollData.typeAttaque == "assomer" && rollData.defenderTokenId && rollData.isPureSuccess) {
let defender = game.canvas.tokens.get(rollData?.defenderTokenId)?.actor
defender.setModifier("Assomer : Prochaine action", "roll", -5)
}
if (rollData.typeAttaque == "fuir" && rollData.difficulte > 0 && !rollData.isSuccess) {
actor.setModifier("Fuite échouée : -5 en défense ce round et suivant", "defense", -5)
}
if (rollData.typeAttaque == "immobiliser" && rollData.difficulte > 0 && rollData.isPureSuccess) {
actor.setModifier("Immobilisation en cours : -5 pour prochaine action", "roll", -5)
}
if (rollData.typeAttaque == "chargecavalerie") {
actor.setModifier("Charge de Cavalerie : -5 défense pour le tour", "defense", -5)
}
this.createChatWithRollMode(rollData.alias, { this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-mournblade/templates/chat-generic-result.html`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-mournblade/templates/chat-generic-result-v2.hbs`, rollData)
}, rollData) }, rollData)
} }
/* -------------------------------------------- */
static async rollDegatsFromAttaque(rollData) {
let maximize = false
let degatsMessage = "Degats normaux"
if (rollData.arme?.system?.isMelee) {
rollData.degatsFormula = rollData.arme.system.totalDegats
if (rollData.isHeroique) { // Deux fois les dés de dégats
degatsMessage = "Dégats doublés"
}
if (rollData.typeAttaque == "assomer") {
rollData.degatsFormula = false
}
if (rollData.typeAttaque == "charger") {
rollData.degatsFormula += "+2"
}
if (rollData.typeAttaque == "chargecavalerie") {
rollData.degatsFormula += "+5"
}
if (rollData.typeAttaque == "precise") {
degatsMessage = "Degats normaux"
if (rollData.isHeroique) { // Degats max
maximize = true
degatsMessage = "Dégats maximaux, ignore l'armure du défenseur";
rollData.ignoreDefenseArmor = true
}
}
if (rollData.typeAttaque == "feinte") {
degatsMessage = "Pas de dégats, mais bonus pour prochaine attaque"
rollData.degatsFormula = false
rollData.nextBonus = 5
if (rollData.isHeroique) { // Bonus pour prochaine action
rollData.nextBonus = 10
rollData.nextBonusDegats = 10
}
}
if (rollData.typeAttaque == "coupbas") {
degatsMessage = "Pas de dégats, mais malus pour prochaine action complexe du défenseur"
rollData.degatsFormula = false
rollData.nextMalus = 5
if (rollData.isHeroique) { // Malus pour prochaine action
rollData.nextMalus = 15
}
}
if (rollData.typeAttaque == "contenir") {
degatsMessage = "Pas de dégats, mais l'adversaire ne peut pas vous attaquer pour le reste du tour"
rollData.degatsFormula = false
if (rollData.isHeroique) { // Malus pour prochaine action
degatsMessage = "Pas de dégats, mais tout les adversaires avec une défense inférieure ou égale à " + rollData.finalResult - 10 +
" ne peuvent pas vous attaquer pour le reste du tour"
}
}
if (rollData.typeAttaque == "desarmer") {
degatsMessage = "Pas de dégats, mais l'adversaire reçoit un malus de -5 pour sa prochaine action"
rollData.degatsFormula = false
if (rollData.isHeroique) { // Malus pour prochaine action
rollData.defenderDesarme = true
degatsMessage = "Pas de dégats, mais l'arme de votre adversaire est arrachée de ses mains"
}
}
} else { // Armes à distance
rollData.degatsFormula = rollData.arme.system.totalDegats
}
// Perform the roll, show the dice
rollData.finalResult = 0
rollData.degatsMessage = degatsMessage
if (rollData.degatsFormula) {
console.log("Degats formula", rollData.degatsFormula)
// Twice!maximize
if (rollData.isHeroique && !maximize) {
rollData.degatsFormula += "+" + rollData.degatsFormula
}
// Latest modifiers
for (let mod of rollData.modifiers) {
if (mod.system.modifiertype == "degats") {
rollData.degatsFormula += `+${mod.system.value}`
}
}
let degatsRoll = await new Roll(rollData.degatsFormula).evaluate({ maximize: maximize })
await this.showDiceSoNice(degatsRoll, game.settings.get("core", "rollMode"))
rollData.degatsRoll = foundry.utils.duplicate(degatsRoll)
rollData.finalResult = degatsRoll.total
}
this.createChatWithRollMode(rollData.alias, {
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-mournblade/templates/chat-degats-result-v2.hbs`, rollData)
}, rollData)
}
/* -------------------------------------------- */
static applyDegatsFromAttaque(rollData) {
let defender = game.canvas.tokens.get(rollData?.defenderTokenId)?.actor
if (defender && rollData.arme) {
let actor = MournbladeUtility.getActorFromRollData(rollData)
if (rollData.typeAttaque == "desarmer" && !rollData.isHeroique) {
defender.setModifier("Malus suite à désarmement", "roll", -5)
}
if (rollData.typeAttaque == "charger") {
actor.setModifier("Défense suite à charge", "roll", -5)
}
if (rollData.nextBonus) {
actor.setModifier("Prochaine attaque", "roll", rollData.nextBonus)
if (rollData.nextDegatsBonus) {
actor.setModifier("Prochaine attaque", "degats", rollData.nextDegatsBonus)
}
}
if (rollData.nextMalus) {
defender.setModifier("Prochaine action complexe", "roll", -rollData.nextMalus)
}
if (rollData.defenderDesarme) {
ui.notifications.info("L'arme de " + defender.name + " est arrachée de ses mains (à gérer manuellement)")
}
let degats = rollData.finalResult
let type = (rollData.arme.system.nonletaux) ? "nonletaux" : "letaux"
if (rollData.arme.system.ignorearmure) {
rollData.ignoreDefenseArmor = true
}
defender.incDecSante(type, +degats, rollData.ignoreDefenseArmor)
ui.notifications.info(defender.name + "a subi " + degats + " points de santé " + type + ".")
} else {
ui.notifications.warn("Pas de cible sélectionnée ou pas d'arme de défense équipée.")
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async bonusRollMournblade(rollData) { static async bonusRollMournblade(rollData) {
rollData.bonusFormula = rollData.addedBonus rollData.bonusFormula = rollData.addedBonus
let bonusRoll = new Roll(rollData.bonusFormula).roll({ async: false }) let bonusRoll = await new Roll(rollData.bonusFormula).evaluate()
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode")); await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
rollData.bonusRoll = duplicate(bonusRoll) rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
rollData.finalResult += rollData.bonusRoll.total rollData.finalResult += rollData.bonusRoll.total
this.computeResult(rollData) this.computeQualityResult(rollData)
this.createChatWithRollMode(rollData.alias, { this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-mournblade/templates/chat-generic-result.html`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-mournblade/templates/chat-generic-result-v2.hbs`, rollData)
}, rollData) }, rollData)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getUsers(filter) { static getUsers(filter) {
return game.users.filter(filter).map(user => user.data._id); return game.users.filter(filter).map(user => user._id);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -406,11 +606,11 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static blindMessageToGM(chatOptions) { static blindMessageToGM(chatOptions) {
let chatGM = duplicate(chatOptions); let chatGM = foundry.utils.duplicate(chatOptions);
chatGM.whisper = this.getUsers(user => user.isGM); chatGM.whisper = this.getUsers(user => user.isGM);
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content; chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
console.log("blindMessageToGM", chatGM); console.log("blindMessageToGM", chatGM);
game.socket.emit("system.fvtt-weapons-of-the-gods", { msg: "msg_gm_chat_message", data: chatGM }); game.socket.emit("system.fvtt-mournblade", { msg: "msg_gm_chat_message", data: chatGM });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -459,7 +659,8 @@ export class MournbladeUtility {
chatOptions.whisper = this.getWhisperRecipients(rollMode, name); chatOptions.whisper = this.getWhisperRecipients(rollMode, name);
break; break;
} }
chatOptions.alias = chatOptions.alias || name chatOptions.alias = chatOptions.alias || name;
chatOptions.speaker = ChatMessage.getSpeaker();
let msg = await ChatMessage.create(chatOptions) let msg = await ChatMessage.create(chatOptions)
console.log("=======>", rollData) console.log("=======>", rollData)
msg.setFlag("world", "mournblade-roll", rollData) msg.setFlag("world", "mournblade-roll", rollData)
@@ -468,12 +669,11 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getBasicRollData() { static getBasicRollData() {
let rollData = { let rollData = {
rollId: randomID(16), rollId: foundry.utils.randomID(16),
rollMode: game.settings.get("core", "rollMode"), rollMode: game.settings.get("core", "rollMode"),
modificateursOptions: this.getModificateurOptions(),
pointAmeOptions: this.getPointAmeOptions(),
difficulte: 0, difficulte: 0,
modificateur: 0, modificateur: 0,
config: foundry.utils.duplicate(game.system.mournblade.config),
} }
MournbladeUtility.updateWithTarget(rollData) MournbladeUtility.updateWithTarget(rollData)
return rollData return rollData
@@ -484,7 +684,10 @@ export class MournbladeUtility {
let target = MournbladeUtility.getTarget() let target = MournbladeUtility.getTarget()
if (target) { if (target) {
rollData.defenderTokenId = target.id rollData.defenderTokenId = target.id
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor let defender = game.canvas.tokens.get(rollData.defenderTokenId)?.actor
rollData.defenderCombatValues = defender.getCombatValues()
rollData.defender = defender.toObject() // Simpler
rollData.defenderDefense = defender.getBestDefenseValue()
rollData.armeDefense = defender.getBestDefenseValue() rollData.armeDefense = defender.getBestDefenseValue()
if (rollData.armeDefense) { if (rollData.armeDefense) {
rollData.difficulte = rollData.armeDefense.system.totalDefensif rollData.difficulte = rollData.armeDefense.system.totalDefensif
@@ -501,7 +704,7 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static applyBonneAventureRoll(li, changed, addedBonus) { static applyBonneAventureRoll(li, changed, addedBonus) {
let msgId = li.data("message-id") let msgId = $(li).data("message-id")
let msg = game.messages.get(msgId) let msg = game.messages.get(msgId)
if (msg) { if (msg) {
let rollData = msg.getFlag("world", "mournblade-roll") let rollData = msg.getFlag("world", "mournblade-roll")
@@ -520,7 +723,7 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static applyEclatRoll(li, changed, addedBonus) { static applyEclatRoll(li, changed, addedBonus) {
let msgId = li.data("message-id") let msgId = $(li).data("message-id")
let msg = game.messages.get(msgId) let msg = game.messages.get(msgId)
if (msg) { if (msg) {
let rollData = msg.getFlag("world", "mournblade-roll") let rollData = msg.getFlag("world", "mournblade-roll")
@@ -537,51 +740,51 @@ export class MournbladeUtility {
static chatRollMenu(html, options) { static chatRollMenu(html, options) {
let canApply = li => canvas.tokens.controlled.length && li.find(".mournblade-roll").length let canApply = li => canvas.tokens.controlled.length && li.find(".mournblade-roll").length
let canApplyBALoyal = function (li) { let canApplyBALoyal = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getBonneAventure() > 0 && actor.getAlignement() == "loyal") return (!rollData.isReroll && actor.getBonneAventure() > 0 && actor.getAlignement() == "loyal")
} }
let canApplyPELoyal = function (li) { let canApplyPELoyal = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getEclat() > 0 && actor.getAlignement() == "loyal") return (!rollData.isReroll && actor.getEclat() > 0 && actor.getAlignement() == "loyal")
} }
let canApplyBAChaotique = function (li) { let canApplyBAChaotique = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getBonneAventure() > 0 && actor.getAlignement() == "chaotique") return (!rollData.isReroll && actor.getBonneAventure() > 0 && actor.getAlignement() == "chaotique")
} }
let canApplyBAChaotique3 = function (li) { let canApplyBAChaotique3 = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getBonneAventure() > 2 && actor.getAlignement() == "chaotique") return (!rollData.isReroll && actor.getBonneAventure() > 2 && actor.getAlignement() == "chaotique")
} }
let canApplyPEChaotique = function (li) { let canApplyPEChaotique = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getEclat() > 0 && actor.getAlignement() == "chaotique") return (!rollData.isReroll && actor.getEclat() > 0 && actor.getAlignement() == "chaotique")
} }
let hasPredilection = function (li) { let hasPredilection = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
if (rollData.competence) { if (rollData.competence) {
let nbPred = rollData.competence.data.predilections.filter(pred => !pred.used).length let nbPred = rollData.competence.system.predilections.filter(pred => !pred.used).length
return (!rollData.isReroll && rollData.competence && nbPred > 0) return (!rollData.isReroll && rollData.competence && nbPred > 0)
} }
return false return false
} }
let canCompetenceDouble = function (li) { let canCompetenceDouble = function (li) {
let message = game.messages.get(li.attr("data-message-id")) let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "mournblade-roll") let rollData = message.getFlag("world", "mournblade-roll")
let actor = MournbladeUtility.getActorFromRollData(rollData) let actor = MournbladeUtility.getActorFromRollData(rollData)
if (rollData.competence) { if (rollData.competence) {
return rollData.competence.data.doublebonus return rollData.competence.system.doublebonus
} }
return false return false
} }
@@ -646,25 +849,25 @@ export class MournbladeUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async confirmDelete(actorSheet, li) { static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id"); let itemId = li.dataset?.itemId || li.data("item-id");
let msgTxt = "<p>Are you sure to remove this Item ?"; let msgTxt = "<p>Voulez vous supprimer cet item ?";
let buttons = { let buttons = {
delete: { delete: {
icon: '<i class="fas fa-check"></i>', icon: '<i class="fas fa-check"></i>',
label: "Yes, remove it", label: "Oui !",
callback: () => { callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]); actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
li.slideUp(200, () => actorSheet.render(false)); actorSheet.render(false);
} }
}, },
cancel: { cancel: {
icon: '<i class="fas fa-times"></i>', icon: '<i class="fas fa-times"></i>',
label: "Cancel" label: "Non !"
} }
} }
msgTxt += "</p>"; msgTxt += "</p>";
let d = new Dialog({ let d = new Dialog({
title: "Confirm removal", title: "Confirmer la suppression",
content: msgTxt, content: msgTxt,
buttons: buttons, buttons: buttons,
default: "cancel" default: "cancel"
+4968
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
{
"name": "fvtt-mournblade",
"version": "1.0.0",
"description": "Mournblade RPG for FoundryVTT - French",
"scripts": {
"build": "gulp build",
"watch": "gulp watch"
},
"author": "Uberwald/LeRatierBretonnien",
"license": "SEE LICENSE IN LICENCE.txt",
"devDependencies": {
"gulp": "^4.0.2",
"gulp-less": "^5.0.0",
"gulp-sourcemaps": "^3.0.0"
}
}
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000316
+11 -8
View File
@@ -1,8 +1,11 @@
2023/12/19-22:15:44.879816 7f3323fff6c0 Recovering log #12 2026/04/01-22:37:22.642283 7fcfbdbfc6c0 Delete type=3 #1
2023/12/19-22:15:44.890587 7f3323fff6c0 Delete type=3 #10 2026/04/01-22:38:41.418728 7fcfbd3fb6c0 Level-0 table #319: started
2023/12/19-22:15:44.890683 7f3323fff6c0 Delete type=0 #12 2026/04/01-22:38:41.418758 7fcfbd3fb6c0 Level-0 table #319: 0 bytes OK
2023/12/19-22:15:56.534936 7f33223ff6c0 Level-0 table #17: started 2026/04/01-22:38:41.455694 7fcfbd3fb6c0 Delete type=0 #317
2023/12/19-22:15:56.535009 7f33223ff6c0 Level-0 table #17: 0 bytes OK 2026/04/01-22:38:41.627139 7fcfbd3fb6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at '!items!wv5EiePmPTpqFutt' @ 239 : 1
2023/12/19-22:15:56.541696 7f33223ff6c0 Delete type=0 #15 2026/04/01-22:38:41.627156 7fcfbd3fb6c0 Compacting 1@0 + 0@1 files
2023/12/19-22:15:56.548995 7f33223ff6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end) 2026/04/01-22:38:41.644764 7fcfbd3fb6c0 Generated table #320@0: 48 keys, 14422 bytes
2023/12/19-22:15:56.555645 7f33223ff6c0 Manual compaction at level-1 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end) 2026/04/01-22:38:41.644788 7fcfbd3fb6c0 Compacted 1@0 + 0@1 files => 14422 bytes
2026/04/01-22:38:41.682337 7fcfbd3fb6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/01-22:38:41.682460 7fcfbd3fb6c0 Delete type=2 #235
2026/04/01-22:38:41.682635 7fcfbd3fb6c0 Manual compaction at level-0 from '!items!wv5EiePmPTpqFutt' @ 239 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
+4 -8
View File
@@ -1,8 +1,4 @@
2023/12/19-21:45:05.580403 7f3323fff6c0 Recovering log #8 2026/04/01-22:37:22.463555 7fcfbdbfc6c0 Log #314: 0 ops saved to Table #315 OK
2023/12/19-21:45:05.635099 7f3323fff6c0 Delete type=3 #6 2026/04/01-22:37:22.463668 7fcfbdbfc6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/armes/000314.log: OK
2023/12/19-21:45:05.635255 7f3323fff6c0 Delete type=0 #8 2026/04/01-22:37:22.469250 7fcfbdbfc6c0 Table #235: 48 entries OK
2023/12/19-21:45:32.368329 7f33223ff6c0 Level-0 table #13: started 2026/04/01-22:37:22.504609 7fcfbdbfc6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/armes; recovered 1 files; 14422 bytes. Some data may have been lost. ****
2023/12/19-21:45:32.368364 7f33223ff6c0 Level-0 table #13: 0 bytes OK
2023/12/19-21:45:32.374930 7f33223ff6c0 Delete type=0 #11
2023/12/19-21:45:32.386871 7f33223ff6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
2023/12/19-21:45:32.393727 7f33223ff6c0 Manual compaction at level-1 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000315
+11 -8
View File
@@ -1,8 +1,11 @@
2023/12/19-22:15:44.922363 7f3322ffd6c0 Recovering log #12 2026/04/01-22:37:23.261595 7fcfbf3ff6c0 Delete type=3 #1
2023/12/19-22:15:44.933242 7f3322ffd6c0 Delete type=3 #10 2026/04/01-22:38:41.719103 7fcfbd3fb6c0 Level-0 table #318: started
2023/12/19-22:15:44.933334 7f3322ffd6c0 Delete type=0 #12 2026/04/01-22:38:41.719126 7fcfbd3fb6c0 Level-0 table #318: 0 bytes OK
2023/12/19-22:15:56.541869 7f33223ff6c0 Level-0 table #17: started 2026/04/01-22:38:41.756352 7fcfbd3fb6c0 Delete type=0 #316
2023/12/19-22:15:56.541915 7f33223ff6c0 Level-0 table #17: 0 bytes OK 2026/04/01-22:38:41.891691 7fcfbd3fb6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at '!items!zzz9JrtWjELdoAfK' @ 120 : 1
2023/12/19-22:15:56.548800 7f33223ff6c0 Delete type=0 #15 2026/04/01-22:38:41.891728 7fcfbd3fb6c0 Compacting 1@0 + 0@1 files
2023/12/19-22:15:56.555609 7f33223ff6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end) 2026/04/01-22:38:41.908679 7fcfbd3fb6c0 Generated table #319@0: 30 keys, 15890 bytes
2023/12/19-22:15:56.555674 7f33223ff6c0 Manual compaction at level-1 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end) 2026/04/01-22:38:41.908714 7fcfbd3fb6c0 Compacted 1@0 + 0@1 files => 15890 bytes
2026/04/01-22:38:41.945467 7fcfbd3fb6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/01-22:38:41.945642 7fcfbd3fb6c0 Delete type=2 #234
2026/04/01-22:38:42.051657 7fcfbd3fb6c0 Manual compaction at level-0 from '!items!zzz9JrtWjELdoAfK' @ 120 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
+4 -8
View File
@@ -1,8 +1,4 @@
2023/12/19-21:45:05.751995 7f33237fe6c0 Recovering log #8 2026/04/01-22:37:23.088994 7fcfbf3ff6c0 Log #313: 0 ops saved to Table #314 OK
2023/12/19-21:45:05.802361 7f33237fe6c0 Delete type=3 #6 2026/04/01-22:37:23.089110 7fcfbf3ff6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/dons/000313.log: OK
2023/12/19-21:45:05.802459 7f33237fe6c0 Delete type=0 #8 2026/04/01-22:37:23.093521 7fcfbf3ff6c0 Table #234: 30 entries OK
2023/12/19-21:45:32.401535 7f33223ff6c0 Level-0 table #13: started 2026/04/01-22:37:23.118194 7fcfbf3ff6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/dons; recovered 1 files; 15890 bytes. Some data may have been lost. ****
2023/12/19-21:45:32.401586 7f33223ff6c0 Level-0 table #13: 0 bytes OK
2023/12/19-21:45:32.408618 7f33223ff6c0 Delete type=0 #11
2023/12/19-21:45:32.416737 7f33223ff6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
2023/12/19-21:45:32.416779 7f33223ff6c0 Manual compaction at level-1 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000014 MANIFEST-000315
+11 -8
View File
@@ -1,8 +1,11 @@
2023/12/19-22:15:44.907560 7f33237fe6c0 Recovering log #12 2026/04/01-22:37:23.085637 7fcfbe3fd6c0 Delete type=3 #1
2023/12/19-22:15:44.919038 7f33237fe6c0 Delete type=3 #10 2026/04/01-22:38:41.682720 7fcfbd3fb6c0 Level-0 table #318: started
2023/12/19-22:15:44.919177 7f33237fe6c0 Delete type=0 #12 2026/04/01-22:38:41.682751 7fcfbd3fb6c0 Level-0 table #318: 0 bytes OK
2023/12/19-22:15:56.527537 7f33223ff6c0 Level-0 table #17: started 2026/04/01-22:38:41.718990 7fcfbd3fb6c0 Delete type=0 #316
2023/12/19-22:15:56.527579 7f33223ff6c0 Level-0 table #17: 0 bytes OK 2026/04/01-22:38:41.830653 7fcfbd3fb6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at '!items!y47dBO3Mf5Pn7tOd' @ 220 : 1
2023/12/19-22:15:56.534748 7f33223ff6c0 Delete type=0 #15 2026/04/01-22:38:41.830662 7fcfbd3fb6c0 Compacting 1@0 + 0@1 files
2023/12/19-22:15:56.548969 7f33223ff6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end) 2026/04/01-22:38:41.848348 7fcfbd3fb6c0 Generated table #319@0: 55 keys, 9727 bytes
2023/12/19-22:15:56.555629 7f33223ff6c0 Manual compaction at level-1 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end) 2026/04/01-22:38:41.848372 7fcfbd3fb6c0 Compacted 1@0 + 0@1 files => 9727 bytes
2026/04/01-22:38:41.886159 7fcfbd3fb6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2026/04/01-22:38:41.886277 7fcfbd3fb6c0 Delete type=2 #234
2026/04/01-22:38:42.051640 7fcfbd3fb6c0 Manual compaction at level-0 from '!items!y47dBO3Mf5Pn7tOd' @ 220 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
+4 -8
View File
@@ -1,8 +1,4 @@
2023/12/19-21:45:05.694845 7f3322ffd6c0 Recovering log #8 2026/04/01-22:37:22.833289 7fcfbe3fd6c0 Log #313: 0 ops saved to Table #314 OK
2023/12/19-21:45:05.749080 7f3322ffd6c0 Delete type=3 #6 2026/04/01-22:37:22.833392 7fcfbe3fd6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/equipement/000313.log: OK
2023/12/19-21:45:05.749173 7f3322ffd6c0 Delete type=0 #8 2026/04/01-22:37:22.856591 7fcfbe3fd6c0 Table #234: 55 entries OK
2023/12/19-21:45:32.393743 7f33223ff6c0 Level-0 table #13: started 2026/04/01-22:37:22.888677 7fcfbe3fd6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/equipement; recovered 1 files; 9727 bytes. Some data may have been lost. ****
2023/12/19-21:45:32.393776 7f33223ff6c0 Level-0 table #13: 0 bytes OK
2023/12/19-21:45:32.401345 7f33223ff6c0 Delete type=0 #11
2023/12/19-21:45:32.416703 7f33223ff6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
2023/12/19-21:45:32.416767 7f33223ff6c0 Manual compaction at level-1 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More