Esprit de la Loi + Automaton
This commit is contained in:
+57
-1
@@ -223,6 +223,9 @@
|
|||||||
"MNBL.potionHeroique": "Heroic",
|
"MNBL.potionHeroique": "Heroic",
|
||||||
"MNBL.potionInefficace": "Ineffective",
|
"MNBL.potionInefficace": "Ineffective",
|
||||||
"MNBL.potionPoison": "Poison",
|
"MNBL.potionPoison": "Poison",
|
||||||
|
"MNBL.potions": "Potions",
|
||||||
|
"MNBL.potionStatut": "Status",
|
||||||
|
"MNBL.potionDuree": "Duration",
|
||||||
"MNBL.preparePotion": "Prepare a Potion",
|
"MNBL.preparePotion": "Prepare a Potion",
|
||||||
"MNBL.capacites": "Capacities / Powers",
|
"MNBL.capacites": "Capacities / Powers",
|
||||||
"MNBL.capacite": "Capacity",
|
"MNBL.capacite": "Capacity",
|
||||||
@@ -230,6 +233,7 @@
|
|||||||
"MNBL.typeCapaciteElue": "Chosen Power",
|
"MNBL.typeCapaciteElue": "Chosen Power",
|
||||||
"MNBL.typeCapaciteElementaire": "Elemental Power",
|
"MNBL.typeCapaciteElementaire": "Elemental Power",
|
||||||
"MNBL.typeCapaciteDemoniaque": "Demonic Power",
|
"MNBL.typeCapaciteDemoniaque": "Demonic Power",
|
||||||
|
"MNBL.typeCapaciteAutomaton": "Automaton Ability",
|
||||||
"MNBL.typeCapaciteCreature": "Creature Power",
|
"MNBL.typeCapaciteCreature": "Creature Power",
|
||||||
"MNBL.creatureTypeCreature": "Creature",
|
"MNBL.creatureTypeCreature": "Creature",
|
||||||
"MNBL.creatureTypeDemon": "Demon",
|
"MNBL.creatureTypeDemon": "Demon",
|
||||||
@@ -240,6 +244,37 @@
|
|||||||
"MNBL.elementTypeEau": "Water",
|
"MNBL.elementTypeEau": "Water",
|
||||||
"MNBL.creatureType": "Creature Type",
|
"MNBL.creatureType": "Creature Type",
|
||||||
"MNBL.elementType": "Element",
|
"MNBL.elementType": "Element",
|
||||||
|
"MNBL.demonType": "Demon Type",
|
||||||
|
"MNBL.demonPuissance": "Power Level",
|
||||||
|
"MNBL.demonTypeNone": "— Undefined —",
|
||||||
|
"MNBL.demonTypeCombat": "Combat Demon",
|
||||||
|
"MNBL.demonTypeDesir": "Desire Demon",
|
||||||
|
"MNBL.demonTypeSavoir": "Knowledge Demon",
|
||||||
|
"MNBL.demonTypeProtection": "Protection Demon",
|
||||||
|
"MNBL.demonTypeVoyage": "Travel Demon",
|
||||||
|
"MNBL.demonPuissanceNone": "— Undefined —",
|
||||||
|
"MNBL.demonPuissanceMineur": "Minor",
|
||||||
|
"MNBL.demonPuissanceMedian": "Median",
|
||||||
|
"MNBL.demonPuissanceMajeur": "Major",
|
||||||
|
"MNBL.creatureTypeAutomaton": "Automaton",
|
||||||
|
"MNBL.automatonType": "Automaton Type",
|
||||||
|
"MNBL.automatonPuissance": "Power Level",
|
||||||
|
"MNBL.automatonTypeNone": "— Undefined —",
|
||||||
|
"MNBL.automatonTypeCombat": "Combat",
|
||||||
|
"MNBL.automatonTypeVoyage": "Travel",
|
||||||
|
"MNBL.automatonTypePerception": "Perception",
|
||||||
|
"MNBL.automatonTypeRestauration": "Restoration",
|
||||||
|
"MNBL.automatonTypeReparateur": "Repairer",
|
||||||
|
"MNBL.automatonPuissanceNone": "— Undefined —",
|
||||||
|
"MNBL.automatonPuissanceMineur": "Minor",
|
||||||
|
"MNBL.automatonPuissanceMedian": "Median",
|
||||||
|
"MNBL.automatonPuissanceMajeur": "Major",
|
||||||
|
"MNBL.automatonVoyageType": "Travel Type",
|
||||||
|
"MNBL.automatonVoyageTypeNone": "— Undefined —",
|
||||||
|
"MNBL.automatonVoyageTypeTerrestre": "Terrestrial",
|
||||||
|
"MNBL.automatonVoyageTypeAquatique": "Aquatic",
|
||||||
|
"MNBL.automatonVoyageTypeAerien": "Aerial",
|
||||||
|
"MNBL.automatonVoyageTypeExtradimensionnel": "Extra-dimensional",
|
||||||
"MNBL.invoquerElementaire": "Summon an Elemental",
|
"MNBL.invoquerElementaire": "Summon an Elemental",
|
||||||
"MNBL.bannirElementaire": "Banish",
|
"MNBL.bannirElementaire": "Banish",
|
||||||
"MNBL.invocationsActives": "Active Invocations",
|
"MNBL.invocationsActives": "Active Invocations",
|
||||||
@@ -262,5 +297,26 @@
|
|||||||
"MNBL.invocationConcentrationMineur": "1 round",
|
"MNBL.invocationConcentrationMineur": "1 round",
|
||||||
"MNBL.invocationConcentrationMedian": "1 minute",
|
"MNBL.invocationConcentrationMedian": "1 minute",
|
||||||
"MNBL.invocationConcentrationMajeur": "1 hour",
|
"MNBL.invocationConcentrationMajeur": "1 hour",
|
||||||
"MNBL.invocationAmeBloque": "Blocked Soul"
|
"MNBL.invocationAmeBloque": "Blocked Soul",
|
||||||
|
|
||||||
|
"MNBL.invoquerDemon": "Invoke a Demon",
|
||||||
|
"MNBL.invoquerEsprit": "Invoke a Law Spirit",
|
||||||
|
"MNBL.libererDemon": "Release",
|
||||||
|
"MNBL.invocationsDemons": "Active Demonic Invocations",
|
||||||
|
"MNBL.invocationDemonSeuil": "Difficulty Threshold",
|
||||||
|
"MNBL.invocationDemonAme": "Soul Spent",
|
||||||
|
"MNBL.invocationDemonCoercition": "Coercion",
|
||||||
|
"MNBL.invocationDemonLoiChaos": "Lore: Law & Chaos",
|
||||||
|
"MNBL.invocationDemonResultSucces": "The Demon is invoked!",
|
||||||
|
"MNBL.invocationDemonResultHeroique": "Heroic success! More favorable agreement.",
|
||||||
|
"MNBL.invocationDemonResultEchec": "Invocation failed. You lose half the Soul points spent.",
|
||||||
|
"MNBL.invocationDemonResultDramatique": "Dramatic failure! All Soul points are lost.",
|
||||||
|
|
||||||
|
"MNBL.enchantementLoi": "Law Enchantment",
|
||||||
|
"MNBL.enchanter": "Enchant",
|
||||||
|
"MNBL.enchantementActif": "Enchanted",
|
||||||
|
"MNBL.enchantementBonus": "Bonus",
|
||||||
|
"MNBL.enchantementAntiChaos": "Anti-Chaos",
|
||||||
|
"MNBL.enchantementStandard": "Standard (skill bonus)",
|
||||||
|
"MNBL.enchantementTypeAntiChaos": "Anti-Chaos (luminous aura)"
|
||||||
}
|
}
|
||||||
+59
-2
@@ -22,7 +22,8 @@
|
|||||||
"runeeffect": "Effet de Rune",
|
"runeeffect": "Effet de Rune",
|
||||||
"bouclier": "Bouclier",
|
"bouclier": "Bouclier",
|
||||||
"modifier": "Modificateur",
|
"modifier": "Modificateur",
|
||||||
"traitespece": "Trait d'Espèce"
|
"traitespece": "Trait d'Espèce",
|
||||||
|
"potion": "Potion"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Adresse": "Adresse",
|
"Adresse": "Adresse",
|
||||||
@@ -232,6 +233,9 @@
|
|||||||
"MNBL.potionHeroique": "Héroïque",
|
"MNBL.potionHeroique": "Héroïque",
|
||||||
"MNBL.potionInefficace": "Inefficace",
|
"MNBL.potionInefficace": "Inefficace",
|
||||||
"MNBL.potionPoison": "Poison",
|
"MNBL.potionPoison": "Poison",
|
||||||
|
"MNBL.potions": "Potions",
|
||||||
|
"MNBL.potionStatut": "Statut",
|
||||||
|
"MNBL.potionDuree": "Durée",
|
||||||
"MNBL.preparePotion": "Préparer une Potion",
|
"MNBL.preparePotion": "Préparer une Potion",
|
||||||
"MNBL.capacites": "Capacités / Pouvoirs",
|
"MNBL.capacites": "Capacités / Pouvoirs",
|
||||||
"MNBL.capacite": "Capacité",
|
"MNBL.capacite": "Capacité",
|
||||||
@@ -239,6 +243,7 @@
|
|||||||
"MNBL.typeCapaciteElue": "Pouvoir Élue",
|
"MNBL.typeCapaciteElue": "Pouvoir Élue",
|
||||||
"MNBL.typeCapaciteElementaire": "Pouvoir Élémentaire",
|
"MNBL.typeCapaciteElementaire": "Pouvoir Élémentaire",
|
||||||
"MNBL.typeCapaciteDemoniaque": "Pouvoir Démoniaque",
|
"MNBL.typeCapaciteDemoniaque": "Pouvoir Démoniaque",
|
||||||
|
"MNBL.typeCapaciteAutomaton": "Capacité Automaton",
|
||||||
"MNBL.typeCapaciteCreature": "Pouvoir Créature",
|
"MNBL.typeCapaciteCreature": "Pouvoir Créature",
|
||||||
"MNBL.creatureTypeCreature": "Créature",
|
"MNBL.creatureTypeCreature": "Créature",
|
||||||
"MNBL.creatureTypeDemon": "Démon",
|
"MNBL.creatureTypeDemon": "Démon",
|
||||||
@@ -249,6 +254,37 @@
|
|||||||
"MNBL.elementTypeEau": "Eau",
|
"MNBL.elementTypeEau": "Eau",
|
||||||
"MNBL.creatureType": "Type de créature",
|
"MNBL.creatureType": "Type de créature",
|
||||||
"MNBL.elementType": "Élément",
|
"MNBL.elementType": "Élément",
|
||||||
|
"MNBL.demonType": "Type de Démon",
|
||||||
|
"MNBL.demonPuissance": "Puissance",
|
||||||
|
"MNBL.demonTypeNone": "— Non défini —",
|
||||||
|
"MNBL.demonTypeCombat": "Démon de Combat",
|
||||||
|
"MNBL.demonTypeDesir": "Démon du Désir",
|
||||||
|
"MNBL.demonTypeSavoir": "Démon du Savoir",
|
||||||
|
"MNBL.demonTypeProtection": "Démon de Protection",
|
||||||
|
"MNBL.demonTypeVoyage": "Démon du Voyage",
|
||||||
|
"MNBL.demonPuissanceNone": "— Non défini —",
|
||||||
|
"MNBL.demonPuissanceMineur": "Mineur",
|
||||||
|
"MNBL.demonPuissanceMedian": "Médian",
|
||||||
|
"MNBL.demonPuissanceMajeur": "Majeur",
|
||||||
|
"MNBL.creatureTypeAutomaton": "Automaton",
|
||||||
|
"MNBL.automatonType": "Type d'Automaton",
|
||||||
|
"MNBL.automatonPuissance": "Puissance",
|
||||||
|
"MNBL.automatonTypeNone": "— Non défini —",
|
||||||
|
"MNBL.automatonTypeCombat": "Combat",
|
||||||
|
"MNBL.automatonTypeVoyage": "Voyage",
|
||||||
|
"MNBL.automatonTypePerception": "Perception",
|
||||||
|
"MNBL.automatonTypeRestauration": "Restauration",
|
||||||
|
"MNBL.automatonTypeReparateur": "Réparateur",
|
||||||
|
"MNBL.automatonPuissanceNone": "— Non défini —",
|
||||||
|
"MNBL.automatonPuissanceMineur": "Mineur",
|
||||||
|
"MNBL.automatonPuissanceMedian": "Médian",
|
||||||
|
"MNBL.automatonPuissanceMajeur": "Majeur",
|
||||||
|
"MNBL.automatonVoyageType": "Type de voyage",
|
||||||
|
"MNBL.automatonVoyageTypeNone": "— Non défini —",
|
||||||
|
"MNBL.automatonVoyageTypeTerrestre": "Terrestre",
|
||||||
|
"MNBL.automatonVoyageTypeAquatique": "Aquatique",
|
||||||
|
"MNBL.automatonVoyageTypeAerien": "Aérien",
|
||||||
|
"MNBL.automatonVoyageTypeExtradimensionnel": "Extra-dimensionnel",
|
||||||
"MNBL.invoquerElementaire": "Invoquer un Élémentaire",
|
"MNBL.invoquerElementaire": "Invoquer un Élémentaire",
|
||||||
"MNBL.bannirElementaire": "Bannir",
|
"MNBL.bannirElementaire": "Bannir",
|
||||||
"MNBL.invocationsActives": "Invocations actives",
|
"MNBL.invocationsActives": "Invocations actives",
|
||||||
@@ -271,5 +307,26 @@
|
|||||||
"MNBL.invocationConcentrationMineur": "1 tour",
|
"MNBL.invocationConcentrationMineur": "1 tour",
|
||||||
"MNBL.invocationConcentrationMedian": "1 minute",
|
"MNBL.invocationConcentrationMedian": "1 minute",
|
||||||
"MNBL.invocationConcentrationMajeur": "1 heure",
|
"MNBL.invocationConcentrationMajeur": "1 heure",
|
||||||
"MNBL.invocationAmeBloque": "Âme bloquée"
|
"MNBL.invocationAmeBloque": "Âme bloquée",
|
||||||
|
|
||||||
|
"MNBL.invoquerDemon": "Invoquer un Démon",
|
||||||
|
"MNBL.invoquerEsprit": "Invoquer un Esprit de la Loi",
|
||||||
|
"MNBL.libererDemon": "Libérer",
|
||||||
|
"MNBL.invocationsDemons": "Invocations démoniaques actives",
|
||||||
|
"MNBL.invocationDemonSeuil": "Seuil de difficulté",
|
||||||
|
"MNBL.invocationDemonAme": "Âme dépensée",
|
||||||
|
"MNBL.invocationDemonCoercition": "Coercition",
|
||||||
|
"MNBL.invocationDemonLoiChaos": "Savoir : Loi & Chaos",
|
||||||
|
"MNBL.invocationDemonResultSucces": "Le Démon est invoqué !",
|
||||||
|
"MNBL.invocationDemonResultHeroique": "Réussite héroïque ! Accord plus favorable.",
|
||||||
|
"MNBL.invocationDemonResultEchec": "L'invocation a échoué. Vous perdez la moitié des points d'Âme dépensés.",
|
||||||
|
"MNBL.invocationDemonResultDramatique": "Échec dramatique ! Tous les points d'Âme sont perdus.",
|
||||||
|
|
||||||
|
"MNBL.enchantementLoi": "Enchantement de la Loi",
|
||||||
|
"MNBL.enchanter": "Enchanter",
|
||||||
|
"MNBL.enchantementActif": "Enchanté",
|
||||||
|
"MNBL.enchantementBonus": "Bonus",
|
||||||
|
"MNBL.enchantementAntiChaos": "Anti-Chaos",
|
||||||
|
"MNBL.enchantementStandard": "Standard (bonus de compétence)",
|
||||||
|
"MNBL.enchantementTypeAntiChaos": "Anti-Chaos (aura lumineuse)"
|
||||||
}
|
}
|
||||||
+33
-2
@@ -343,8 +343,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.item-controls-fixed {
|
.item-controls-fixed {
|
||||||
min-width: 3.2rem;
|
min-width: 5rem;
|
||||||
max-width: 3.2rem;
|
max-width: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Couleurs pour les labels et textes dans les onglets
|
// Couleurs pour les labels et textes dans les onglets
|
||||||
@@ -917,3 +917,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chat potion result — rune mini image and haut-parler styling
|
||||||
|
.mournblade-chat-result {
|
||||||
|
.detail-row {
|
||||||
|
.rune-mini-img {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 2px;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px solid rgba(139, 69, 19, 0.4);
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.potion-haut-parler {
|
||||||
|
font-style: italic;
|
||||||
|
color: #5a3a8a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post-item card: danger divider variant (for létal effects)
|
||||||
|
.mournblade-item-card {
|
||||||
|
.item-card-divider--danger {
|
||||||
|
color: #8b0000;
|
||||||
|
&::before, &::after {
|
||||||
|
background: linear-gradient(90deg, transparent, #8b0000, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -440,3 +440,41 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enchantement de la Loi */
|
||||||
|
.enchantement-section {
|
||||||
|
margin-top: 0.6rem;
|
||||||
|
padding: 0.5rem 0.6rem;
|
||||||
|
background: rgba(255, 215, 0, 0.05);
|
||||||
|
border: 1px solid rgba(255, 215, 0, 0.3);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.section-title-small {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin: 0 0 0.4rem 0;
|
||||||
|
color: #a07800;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
border-bottom: 1px solid rgba(255, 215, 0, 0.2);
|
||||||
|
padding-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enchant-badge {
|
||||||
|
display: inline-block;
|
||||||
|
background: gold;
|
||||||
|
color: #333;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.1rem 0.4rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enchant-none {
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.2rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2418,6 +2418,25 @@
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.next-steps {
|
||||||
|
background: rgba(200, 220, 255, 0.5);
|
||||||
|
border-left-color: #2255aa;
|
||||||
|
|
||||||
|
ol {
|
||||||
|
margin: 6px 0 0 0;
|
||||||
|
padding-left: 18px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #2255aa;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.damage-buttons {
|
.damage-buttons {
|
||||||
@@ -2916,6 +2935,219 @@
|
|||||||
accent-color: #c0392b;
|
accent-color: #c0392b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Invocation démon specific ----
|
||||||
|
|
||||||
|
.actor-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: linear-gradient(135deg, #1a0030 0%, #3a0060 100%);
|
||||||
|
border: 1px solid #6a008a;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.actor-portrait {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 2px solid #9900cc;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 1px 3px rgba(0,0,0,0.9);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.dialog-subtitle {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #ddb0ff;
|
||||||
|
i { margin-right: 4px; }
|
||||||
|
}
|
||||||
|
.actor-info { display: flex; flex-direction: column; gap: 2px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-warning-box {
|
||||||
|
background: rgba(180, 20, 20, 0.15);
|
||||||
|
border: 1px solid #8b0000;
|
||||||
|
border-left: 4px solid #c0392b;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 7px 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #6a0000;
|
||||||
|
i { margin-right: 6px; color: #c0392b; }
|
||||||
|
strong { color: #8b0000; }
|
||||||
|
div { margin-top: 3px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-comp-summary {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.comp-badge {
|
||||||
|
background: rgba(60, 20, 80, 0.12);
|
||||||
|
border: 1px solid rgba(120, 40, 160, 0.4);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: #3a0060;
|
||||||
|
|
||||||
|
&.comp-missing {
|
||||||
|
background: rgba(120, 0, 0, 0.08);
|
||||||
|
border-color: rgba(180, 0, 0, 0.3);
|
||||||
|
color: #800000;
|
||||||
|
em { font-style: italic; opacity: 0.7; }
|
||||||
|
}
|
||||||
|
strong { color: #2a004a; font-weight: bold; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-seuil-calculator {
|
||||||
|
background: rgba(60, 10, 80, 0.07);
|
||||||
|
border: 1px solid rgba(120, 40, 160, 0.35);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.invoc-section-title {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #3a0060;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
i { margin-right: 5px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-criteria-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
gap: 5px 10px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 0.83rem;
|
||||||
|
color: #3a1a0a;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select, input[type="number"] {
|
||||||
|
background: #3a1a08;
|
||||||
|
border: 1px solid #8b4513;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 4px 24px 4px 8px;
|
||||||
|
color: #f0e8d8;
|
||||||
|
font-family: CentaurMT, serif;
|
||||||
|
font-size: 0.83rem;
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 200px;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='%23ffd070' d='M5 7L0 0h10z'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 7px center;
|
||||||
|
background-size: 9px 6px;
|
||||||
|
option { background: #1a0e06; color: #f0e8d8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"] {
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
background-image: none;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-seuil-total-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
background: linear-gradient(135deg, #2a005a 0%, #4a0080 100%);
|
||||||
|
border: 1px solid #9900cc;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.invoc-seuil-label {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #ddb0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-seuil-total {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffd700;
|
||||||
|
text-shadow: 0 0 8px rgba(255, 200, 0, 0.6);
|
||||||
|
min-width: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-form-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.invoc-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-bottom: 1px solid rgba(139, 69, 19, 0.2);
|
||||||
|
|
||||||
|
label {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.87rem;
|
||||||
|
color: #3a1a0a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-value-highlight {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #6a0080;
|
||||||
|
min-width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoc-hint {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: #5a3a0a;
|
||||||
|
font-style: italic;
|
||||||
|
strong { color: #8b0000; font-weight: bold; }
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
background: #3a1a08;
|
||||||
|
border: 1px solid #8b4513;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 4px 24px 4px 8px;
|
||||||
|
color: #f0e8d8;
|
||||||
|
font-family: CentaurMT, serif;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='%23ffd070' d='M5 7L0 0h10z'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 7px center;
|
||||||
|
background-size: 9px 6px;
|
||||||
|
option { background: #1a0e06; color: #f0e8d8; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { MournbladeUtility } from "../mournblade-utility.js"
|
||||||
|
|
||||||
|
export default class MournbladeEnchantementDialog {
|
||||||
|
|
||||||
|
static _normalize(str) {
|
||||||
|
return (str ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/œ/g, "oe")
|
||||||
|
.replace(/æ/g, "ae")
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.replace(/[^a-z0-9]+/g, " ")
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
static _findCompetence(actor, ...keywords) {
|
||||||
|
const normKeys = keywords.map(k => MournbladeEnchantementDialog._normalize(k))
|
||||||
|
return actor.items.find(item => {
|
||||||
|
if (item.type !== "competence") return false
|
||||||
|
const norm = MournbladeEnchantementDialog._normalize(item.name)
|
||||||
|
return normKeys.every(k => norm.includes(k))
|
||||||
|
}) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(actor, item) {
|
||||||
|
const normalize = MournbladeEnchantementDialog._normalize.bind(MournbladeEnchantementDialog)
|
||||||
|
const findComp = (...kw) => MournbladeEnchantementDialog._findCompetence(actor, ...kw)
|
||||||
|
|
||||||
|
const ameDisponible = Math.max(0, (actor.system.ame.currentmax - actor.system.ame.value))
|
||||||
|
const aspect = actor.system.balance.aspect ?? 0
|
||||||
|
|
||||||
|
// Skill lookups
|
||||||
|
const savoirRunesComp = findComp("rune")
|
||||||
|
const hautParlerComp = findComp("haut", "parler")
|
||||||
|
const artisanatComp = findComp("savoir", "artisanat")
|
||||||
|
const claAttr = actor.system.attributs?.clairvoyance
|
||||||
|
|
||||||
|
// Prerequisite: Rune de la Loi in inventory
|
||||||
|
const hasRuneLoi = actor.items.some(i => {
|
||||||
|
if (i.type !== "rune") return false
|
||||||
|
return normalize(i.name).includes("loi")
|
||||||
|
})
|
||||||
|
|
||||||
|
const savoirRunesNiveau = savoirRunesComp ? (savoirRunesComp.system.niveau ?? 0) : null
|
||||||
|
const hautParlerNiveau = hautParlerComp ? (hautParlerComp.system.niveau ?? 0) : null
|
||||||
|
const artisanatNiveau = artisanatComp ? (artisanatComp.system.niveau ?? 0) : null
|
||||||
|
const claValeur = claAttr ? (claAttr.value ?? 0) : 0
|
||||||
|
|
||||||
|
// Limit: CLA + Savoir:Runes is capped by min(Haut-Parler, Artisanat) if those skills exist
|
||||||
|
const limiteur = (hautParlerNiveau !== null && artisanatNiveau !== null)
|
||||||
|
? Math.min(hautParlerNiveau, artisanatNiveau)
|
||||||
|
: (hautParlerNiveau ?? artisanatNiveau ?? null)
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
actorImg: actor.img,
|
||||||
|
actorName: actor.name,
|
||||||
|
itemImg: item.img,
|
||||||
|
itemName: item.name,
|
||||||
|
itemType: item.type,
|
||||||
|
itemId: item.id,
|
||||||
|
ameDisponible,
|
||||||
|
aspect,
|
||||||
|
hasRuneLoi,
|
||||||
|
savoirRunesNiveau,
|
||||||
|
hautParlerNiveau,
|
||||||
|
artisanatNiveau,
|
||||||
|
claValeur,
|
||||||
|
limiteur,
|
||||||
|
modOptions: Array.from({ length: 21 }, (_, i) => i - 10),
|
||||||
|
enchantementActif: item.system.enchantementLoi?.actif ?? false,
|
||||||
|
enchantementBonus: item.system.enchantementLoi?.bonus ?? 0,
|
||||||
|
enchantementAntiChaos: item.system.enchantementLoi?.antiChaos ?? false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const prerequisOk = hasRuneLoi
|
||||||
|
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-mournblade/templates/dialog-enchantement.hbs",
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
Hooks.once("renderDialogV2", (_app, html) => {
|
||||||
|
const form = html.querySelector ? html : html[0]
|
||||||
|
MournbladeEnchantementDialog._attachListeners(form, ameDisponible, claValeur, savoirRunesNiveau, limiteur, prerequisOk)
|
||||||
|
})
|
||||||
|
|
||||||
|
return foundry.applications.api.DialogV2.wait({
|
||||||
|
window: { title: `Enchanter : ${item.name}`, icon: "fa-solid fa-star" },
|
||||||
|
classes: ["mournblade-roll-dialog"],
|
||||||
|
position: { width: 520 },
|
||||||
|
modal: false,
|
||||||
|
content,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "enchanter",
|
||||||
|
label: "Enchanter",
|
||||||
|
icon: "fa-solid fa-star",
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
const elems = button.form.elements
|
||||||
|
const ptsAme = parseInt(elems["ptsAme"]?.value ?? 5) || 5
|
||||||
|
const antiChaos = elems["antiChaos"]?.value === "true"
|
||||||
|
const modificateur = parseInt(elems["modificateur"]?.value ?? 0) || 0
|
||||||
|
MournbladeUtility.rollEnchantement({
|
||||||
|
actor,
|
||||||
|
item,
|
||||||
|
ptsAme,
|
||||||
|
antiChaos,
|
||||||
|
modificateur,
|
||||||
|
savoirRunesComp,
|
||||||
|
hautParlerComp,
|
||||||
|
artisanatComp,
|
||||||
|
claValeur,
|
||||||
|
limiteur,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rejectClose: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static _attachListeners(html, ameDisponible, claValeur, savoirRunesNiveau, limiteur, prerequisOk = true) {
|
||||||
|
const enchanterBtn = html.querySelector('button[data-action="enchanter"]')
|
||||||
|
if (enchanterBtn) enchanterBtn.disabled = !prerequisOk
|
||||||
|
|
||||||
|
const diffEl = html.querySelector('#enchant-difficulte')
|
||||||
|
const bonusEl = html.querySelector('#enchant-bonus-preview')
|
||||||
|
const warnAmeEl = html.querySelector('#enchant-ame-warn')
|
||||||
|
const warnLimitEl = html.querySelector('#enchant-limit-warn')
|
||||||
|
const totalEl = html.querySelector('#enchant-total-dice')
|
||||||
|
|
||||||
|
const recalculate = () => {
|
||||||
|
const ptsAme = parseInt(html.querySelector('[name="ptsAme"]')?.value ?? 5) || 0
|
||||||
|
const difficulte = ptsAme
|
||||||
|
const bonus = Math.floor(ptsAme / 5)
|
||||||
|
const savoir = savoirRunesNiveau ?? 0
|
||||||
|
const basePool = claValeur + savoir
|
||||||
|
const effectivePool = limiteur !== null ? Math.min(basePool, limiteur) : basePool
|
||||||
|
|
||||||
|
if (diffEl) diffEl.textContent = difficulte
|
||||||
|
if (bonusEl) bonusEl.textContent = `+${bonus}`
|
||||||
|
if (totalEl) totalEl.textContent = effectivePool
|
||||||
|
|
||||||
|
if (warnAmeEl) warnAmeEl.style.display = ptsAme > ameDisponible ? "" : "none"
|
||||||
|
if (warnLimitEl && limiteur !== null)
|
||||||
|
warnLimitEl.style.display = basePool > limiteur ? "" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
const ptsAmeEl = html.querySelector('[name="ptsAme"]')
|
||||||
|
if (ptsAmeEl) {
|
||||||
|
ptsAmeEl.addEventListener('input', recalculate)
|
||||||
|
ptsAmeEl.addEventListener('change', recalculate)
|
||||||
|
}
|
||||||
|
recalculate()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
import { MournbladeUtility } from "../mournblade-utility.js"
|
||||||
|
|
||||||
|
export default class MournbladeInvocationDemonDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a string for fuzzy matching: lowercase, remove diacritics,
|
||||||
|
* replace œ/æ ligatures, collapse whitespace and punctuation.
|
||||||
|
*/
|
||||||
|
static _normalize(str) {
|
||||||
|
return (str ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/œ/g, "oe")
|
||||||
|
.replace(/æ/g, "ae")
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[\u0300-\u036f]/g, "") // strip combining diacritics
|
||||||
|
.replace(/[^a-z0-9]+/g, " ") // replace any non-alnum with space
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first competence item whose name contains all of the given keywords.
|
||||||
|
* Keywords are normalized before comparison.
|
||||||
|
*/
|
||||||
|
static _findCompetence(actor, ...keywords) {
|
||||||
|
const normKeys = keywords.map(k => MournbladeInvocationDemonDialog._normalize(k))
|
||||||
|
return actor.items.find(item => {
|
||||||
|
if (item.type !== "competence") return false
|
||||||
|
const norm = MournbladeInvocationDemonDialog._normalize(item.name)
|
||||||
|
return normKeys.every(k => norm.includes(k))
|
||||||
|
}) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the actor has a capacite or don whose name contains all given keywords.
|
||||||
|
*/
|
||||||
|
static _hasCapacite(actor, ...keywords) {
|
||||||
|
const normKeys = keywords.map(k => MournbladeInvocationDemonDialog._normalize(k))
|
||||||
|
return actor.items.some(item => {
|
||||||
|
if (item.type !== "capacite" && item.type !== "don") return false
|
||||||
|
const norm = MournbladeInvocationDemonDialog._normalize(item.name)
|
||||||
|
return normKeys.every(k => norm.includes(k))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(actor, rollData) {
|
||||||
|
const ameDisponible = Math.max(0, (actor.system.ame.currentmax - actor.system.ame.value))
|
||||||
|
|
||||||
|
// Robust skill detection: partial keyword matching, diacritic-insensitive
|
||||||
|
const coercitionComp = MournbladeInvocationDemonDialog._findCompetence(actor, "coercition")
|
||||||
|
const hautParlerComp = MournbladeInvocationDemonDialog._findCompetence(actor, "haut", "parler")
|
||||||
|
const loiChaosComp = MournbladeInvocationDemonDialog._findCompetence(actor, "loi", "chaos")
|
||||||
|
|
||||||
|
// Check prerequisites — robust capacite/rune detection
|
||||||
|
const isChaotique = actor.system.balance.chaos > actor.system.balance.loi
|
||||||
|
const hasOeilSorcier = MournbladeInvocationDemonDialog._hasCapacite(actor, "oeil", "sorcier")
|
||||||
|
const hasRuneChaos = actor.items.some(i => {
|
||||||
|
if (i.type !== "rune") return false
|
||||||
|
return MournbladeInvocationDemonDialog._normalize(i.name).includes("chaos")
|
||||||
|
})
|
||||||
|
const prerequisOk = isChaotique && hasOeilSorcier && hasRuneChaos
|
||||||
|
|
||||||
|
// Auto-detect chaos link bonuses
|
||||||
|
const aspectGe8 = (actor.system.balance.aspect ?? 0) >= 8
|
||||||
|
// hasPacte: true if actor has a Pacte item whose associated deity matches a demon
|
||||||
|
// (simplified: just expose it as a selectable option — can't auto-detect)
|
||||||
|
const hasPacte = false
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
...rollData,
|
||||||
|
img: actor.img,
|
||||||
|
name: actor.name,
|
||||||
|
ameDisponible,
|
||||||
|
modOptions: Array.from({ length: 21 }, (_, i) => i - 10),
|
||||||
|
coercitionNiveau: coercitionComp ? coercitionComp.system.niveau : null,
|
||||||
|
hautParlerNiveau: hautParlerComp ? hautParlerComp.system.niveau : null,
|
||||||
|
loiChaosNiveau: loiChaosComp ? loiChaosComp.system.niveau : null,
|
||||||
|
isChaotique,
|
||||||
|
hasOeilSorcier,
|
||||||
|
hasRuneChaos,
|
||||||
|
prerequisOk,
|
||||||
|
aspectGe8,
|
||||||
|
hasPacte,
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-mournblade/templates/dialog-invocation-demon.hbs",
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
Hooks.once("renderDialogV2", (_app, html) => {
|
||||||
|
const form = html.querySelector ? html : html[0]
|
||||||
|
MournbladeInvocationDemonDialog._attachListeners(form, ameDisponible, prerequisOk)
|
||||||
|
})
|
||||||
|
|
||||||
|
return foundry.applications.api.DialogV2.wait({
|
||||||
|
window: { title: "Invoquer un Démon", icon: "fa-solid fa-skull" },
|
||||||
|
classes: ["mournblade-roll-dialog"],
|
||||||
|
position: { width: 560 },
|
||||||
|
modal: false,
|
||||||
|
content,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "invoquer",
|
||||||
|
label: "Invoquer",
|
||||||
|
icon: "fa-solid fa-skull",
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
MournbladeInvocationDemonDialog._updateRollData(rollData, button.form.elements, actor, {
|
||||||
|
coercitionComp, hautParlerComp, loiChaosComp
|
||||||
|
})
|
||||||
|
MournbladeUtility.rollInvocationDemon(rollData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rejectClose: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static _calculateSeuil(html) {
|
||||||
|
const get = (name) => parseInt(html.querySelector(`[name="${name}"]`)?.value ?? 0) || 0
|
||||||
|
const seuil = get("seuil_nature")
|
||||||
|
+ get("seuil_traits")
|
||||||
|
+ get("seuil_augmentation")
|
||||||
|
+ get("seuil_service")
|
||||||
|
+ get("seuil_duree")
|
||||||
|
+ get("seuil_marche")
|
||||||
|
+ get("seuil_chaos")
|
||||||
|
+ get("seuil_sacrifice")
|
||||||
|
return Math.max(1, seuil)
|
||||||
|
}
|
||||||
|
|
||||||
|
static _attachListeners(html, ameDisponible, prerequisOk = true) {
|
||||||
|
const invoquerBtn = html.querySelector('button[data-action="invoquer"]')
|
||||||
|
if (invoquerBtn) invoquerBtn.disabled = !prerequisOk
|
||||||
|
|
||||||
|
const coutEl = html.querySelector('#invoc-demon-cout')
|
||||||
|
const totalEl = html.querySelector('#invoc-demon-seuil-total')
|
||||||
|
const hiddenEl = html.querySelector('#invoc-demon-seuil-hidden')
|
||||||
|
const warnEl = html.querySelector('#invoc-demon-ame-warn')
|
||||||
|
|
||||||
|
const criteriaNames = [
|
||||||
|
"seuil_nature", "seuil_traits", "seuil_augmentation",
|
||||||
|
"seuil_service", "seuil_duree", "seuil_marche",
|
||||||
|
"seuil_chaos", "seuil_sacrifice"
|
||||||
|
]
|
||||||
|
|
||||||
|
const recalculate = () => {
|
||||||
|
const seuil = MournbladeInvocationDemonDialog._calculateSeuil(html)
|
||||||
|
if (totalEl) totalEl.textContent = seuil
|
||||||
|
if (hiddenEl) hiddenEl.value = seuil
|
||||||
|
if (coutEl) coutEl.textContent = seuil
|
||||||
|
if (warnEl) warnEl.style.display = seuil > ameDisponible ? "" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name of criteriaNames) {
|
||||||
|
const el = html.querySelector(`[name="${name}"]`)
|
||||||
|
if (el) el.addEventListener('change', recalculate)
|
||||||
|
if (el && el.type === 'number') el.addEventListener('input', recalculate)
|
||||||
|
}
|
||||||
|
|
||||||
|
recalculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
static _updateRollData(rollData, formElements, actor, { coercitionComp }) {
|
||||||
|
const seuil = parseInt(formElements['seuil']?.value ?? 5)
|
||||||
|
const modificateur = parseInt(formElements['modificateur']?.value ?? 0)
|
||||||
|
|
||||||
|
rollData.invocationSeuil = seuil
|
||||||
|
rollData.invocationSoulCost = seuil
|
||||||
|
rollData.difficulte = seuil
|
||||||
|
rollData.modificateur = modificateur
|
||||||
|
rollData.competence = coercitionComp ?? null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,33 @@ import { MournbladeUtility } from "../mournblade-utility.js"
|
|||||||
|
|
||||||
export default class MournbladeInvocationDialog {
|
export default class MournbladeInvocationDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a string for fuzzy matching: lowercase, remove diacritics,
|
||||||
|
* replace œ/æ ligatures, collapse whitespace and punctuation.
|
||||||
|
*/
|
||||||
|
static _normalize(str) {
|
||||||
|
return (str ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/œ/g, "oe")
|
||||||
|
.replace(/æ/g, "ae")
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.replace(/[^a-z0-9]+/g, " ")
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first competence item whose name contains all of the given keywords.
|
||||||
|
*/
|
||||||
|
static _findCompetence(actor, ...keywords) {
|
||||||
|
const normKeys = keywords.map(k => MournbladeInvocationDialog._normalize(k))
|
||||||
|
return actor.items.find(item => {
|
||||||
|
if (item.type !== "competence") return false
|
||||||
|
const norm = MournbladeInvocationDialog._normalize(item.name)
|
||||||
|
return normKeys.every(k => norm.includes(k))
|
||||||
|
}) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
static async create(actor, rollData) {
|
static async create(actor, rollData) {
|
||||||
const ameDisponible = Math.max(0, (actor.system.ame.currentmax - actor.system.ame.value))
|
const ameDisponible = Math.max(0, (actor.system.ame.currentmax - actor.system.ame.value))
|
||||||
const maxExtra = Math.max(0, ameDisponible - 15)
|
const maxExtra = Math.max(0, ameDisponible - 15)
|
||||||
@@ -14,8 +41,9 @@ export default class MournbladeInvocationDialog {
|
|||||||
allegeance: (p.system.allegeance || "").toLowerCase()
|
allegeance: (p.system.allegeance || "").toLowerCase()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const hautParlerComp = actor.items.find(c => c.type == "competence" && c.name.toLowerCase() == "savoir : haut-parler")
|
// Robust skill detection: partial keyword matching, diacritic-insensitive
|
||||||
const seigneursElemComp = actor.items.find(c => c.type == "competence" && c.name.toLowerCase() == "savoir : seigneurs élémentaires")
|
const hautParlerComp = MournbladeInvocationDialog._findCompetence(actor, "haut", "parler")
|
||||||
|
const seigneursElemComp = MournbladeInvocationDialog._findCompetence(actor, "seigneurs", "elementaires")
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
...rollData,
|
...rollData,
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import { MournbladeUtility } from "../mournblade-utility.js"
|
||||||
|
|
||||||
|
export default class MournbladeInvocationEspritDialog {
|
||||||
|
|
||||||
|
static _normalize(str) {
|
||||||
|
return (str ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/œ/g, "oe").replace(/æ/g, "ae")
|
||||||
|
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.replace(/[^a-z0-9]+/g, " ").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
static _findCompetence(actor, ...keywords) {
|
||||||
|
const normKeys = keywords.map(k => MournbladeInvocationEspritDialog._normalize(k))
|
||||||
|
return actor.items.find(item => {
|
||||||
|
if (item.type !== "competence") return false
|
||||||
|
const norm = MournbladeInvocationEspritDialog._normalize(item.name)
|
||||||
|
return normKeys.every(k => norm.includes(k))
|
||||||
|
}) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Seuil and soul cost per puissance */
|
||||||
|
static SEUILS = { mineur: 15, median: 20, majeur: 25 }
|
||||||
|
|
||||||
|
static async create(actor, rollData) {
|
||||||
|
const ameDisponible = Math.max(0, actor.system.ame.currentmax - actor.system.ame.value)
|
||||||
|
|
||||||
|
const persuasionComp = MournbladeInvocationEspritDialog._findCompetence(actor, "persuasion")
|
||||||
|
const hautParlerComp = MournbladeInvocationEspritDialog._findCompetence(actor, "haut", "parler")
|
||||||
|
const loiChaosComp = MournbladeInvocationEspritDialog._findCompetence(actor, "loi", "chaos")
|
||||||
|
|
||||||
|
const isLoyal = actor.system.balance.loi > actor.system.balance.chaos
|
||||||
|
const hasRuneLoi = actor.items.some(i =>
|
||||||
|
i.type === "rune" && MournbladeInvocationEspritDialog._normalize(i.name).includes("loi")
|
||||||
|
)
|
||||||
|
const prerequisOk = isLoyal && hasRuneLoi
|
||||||
|
|
||||||
|
const automatonTypes = {
|
||||||
|
combat: "Combat",
|
||||||
|
voyage: "Voyage",
|
||||||
|
perception: "Perception",
|
||||||
|
restauration:"Restauration",
|
||||||
|
reparateur: "Réparateur",
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
...rollData,
|
||||||
|
img: actor.img,
|
||||||
|
name: actor.name,
|
||||||
|
ameDisponible,
|
||||||
|
treValeur: actor.system.attributs?.tre?.value ?? 0,
|
||||||
|
persuasionNiveau: persuasionComp?.system?.niveau ?? null,
|
||||||
|
hautParlerNiveau: hautParlerComp?.system?.niveau ?? null,
|
||||||
|
loiChaosNiveau: loiChaosComp?.system?.niveau ?? null,
|
||||||
|
isLoyal,
|
||||||
|
hasRuneLoi,
|
||||||
|
prerequisOk,
|
||||||
|
automatonTypes,
|
||||||
|
seuils: MournbladeInvocationEspritDialog.SEUILS,
|
||||||
|
modOptions: Array.from({ length: 21 }, (_, i) => i - 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-mournblade/templates/dialog-invocation-esprit.hbs",
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
Hooks.once("renderDialogV2", (_app, html) => {
|
||||||
|
const form = html.querySelector ? html : html[0]
|
||||||
|
MournbladeInvocationEspritDialog._attachListeners(form, ameDisponible, prerequisOk)
|
||||||
|
})
|
||||||
|
|
||||||
|
return foundry.applications.api.DialogV2.wait({
|
||||||
|
window: { title: "Invoquer un Esprit de la Loi", icon: "fa-solid fa-star" },
|
||||||
|
classes: ["mournblade-roll-dialog"],
|
||||||
|
position: { width: 520 },
|
||||||
|
modal: false,
|
||||||
|
content,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "invoquer",
|
||||||
|
label: "Invoquer",
|
||||||
|
icon: "fa-solid fa-star",
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
MournbladeInvocationEspritDialog._updateRollData(rollData, button.form.elements, actor, {
|
||||||
|
persuasionComp, hautParlerComp, loiChaosComp
|
||||||
|
})
|
||||||
|
MournbladeUtility.rollInvocationEsprit(rollData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rejectClose: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static _attachListeners(html, ameDisponible, prerequisOk = true) {
|
||||||
|
const invoquerBtn = html.querySelector('button[data-action="invoquer"]')
|
||||||
|
if (invoquerBtn) invoquerBtn.disabled = !prerequisOk
|
||||||
|
|
||||||
|
const puissanceEl = html.querySelector('[name="puissance"]')
|
||||||
|
const seuilEl = html.querySelector('#esprit-seuil-total')
|
||||||
|
const coutEl = html.querySelector('#esprit-cout-ame')
|
||||||
|
const hiddenEl = html.querySelector('#esprit-seuil-hidden')
|
||||||
|
const warnEl = html.querySelector('#esprit-ame-warn')
|
||||||
|
const dureeEl = html.querySelector('#esprit-duree')
|
||||||
|
|
||||||
|
const DUREE = { mineur: "1 heure", median: "1 jour", majeur: "1 semaine" }
|
||||||
|
|
||||||
|
const recalculate = () => {
|
||||||
|
const puissance = puissanceEl?.value ?? "mineur"
|
||||||
|
const seuil = MournbladeInvocationEspritDialog.SEUILS[puissance] ?? 15
|
||||||
|
if (seuilEl) seuilEl.textContent = seuil
|
||||||
|
if (coutEl) coutEl.textContent = seuil
|
||||||
|
if (hiddenEl) hiddenEl.value = seuil
|
||||||
|
if (dureeEl) dureeEl.textContent = DUREE[puissance] ?? "1 heure"
|
||||||
|
if (warnEl) warnEl.style.display = seuil > ameDisponible ? "" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (puissanceEl) puissanceEl.addEventListener("change", recalculate)
|
||||||
|
recalculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
static _updateRollData(rollData, formElements, actor, { persuasionComp }) {
|
||||||
|
const seuil = parseInt(formElements["seuil"]?.value ?? 15)
|
||||||
|
const modificateur = parseInt(formElements["modificateur"]?.value ?? 0)
|
||||||
|
const automatonType = formElements["automatonType"]?.value ?? "combat"
|
||||||
|
const puissance = formElements["puissance"]?.value ?? "mineur"
|
||||||
|
|
||||||
|
rollData.invocationSeuil = seuil
|
||||||
|
rollData.invocationSoulCost = seuil
|
||||||
|
rollData.difficulte = seuil
|
||||||
|
rollData.modificateur = modificateur
|
||||||
|
rollData.competence = persuasionComp ?? null
|
||||||
|
rollData.automatonType = automatonType
|
||||||
|
rollData.automatonPuissance = puissance
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,6 +57,10 @@ export default class MournbladeActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
preparePotion: MournbladeActorSheet.#onPreparePotion,
|
preparePotion: MournbladeActorSheet.#onPreparePotion,
|
||||||
invoquerElementaire: MournbladeActorSheet.#onInvoquerElementaire,
|
invoquerElementaire: MournbladeActorSheet.#onInvoquerElementaire,
|
||||||
bannirElementaire: MournbladeActorSheet.#onBannirElementaire,
|
bannirElementaire: MournbladeActorSheet.#onBannirElementaire,
|
||||||
|
invoquerDemon: MournbladeActorSheet.#onInvoquerDemon,
|
||||||
|
libererDemon: MournbladeActorSheet.#onLibererDemon,
|
||||||
|
invoquerEsprit: MournbladeActorSheet.#onInvoquerEsprit,
|
||||||
|
enchanter: MournbladeActorSheet.#onEnchanter,
|
||||||
rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif,
|
rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif,
|
||||||
rollArmeSpecial: MournbladeActorSheet.#onRollArmeSpecial,
|
rollArmeSpecial: MournbladeActorSheet.#onRollArmeSpecial,
|
||||||
rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats,
|
rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats,
|
||||||
@@ -110,6 +114,21 @@ export default class MournbladeActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_preSyncPartState(partId, newElement, priorElement, state) {
|
||||||
|
super._preSyncPartState(partId, newElement, priorElement, state)
|
||||||
|
// Save scrollable tab positions for deferred restoration in _onRender.
|
||||||
|
// Tabs are hidden (display:none) at _syncPartState time, so scrollTop
|
||||||
|
// assignments have no effect. We re-apply them after making tabs visible.
|
||||||
|
const part = this.constructor.PARTS?.[partId]
|
||||||
|
if (part?.scrollable) {
|
||||||
|
this._pendingScrollRestores = part.scrollable.map(selector => {
|
||||||
|
const el = selector ? priorElement.querySelector(selector) : priorElement
|
||||||
|
return el ? { selector, scrollTop: el.scrollTop } : null
|
||||||
|
}).filter(Boolean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_onRender(context, options) {
|
_onRender(context, options) {
|
||||||
super._onRender(context, options)
|
super._onRender(context, options)
|
||||||
@@ -136,22 +155,37 @@ export default class MournbladeActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
const nav = this.element.querySelector('nav.tabs[data-group]')
|
const nav = this.element.querySelector('nav.tabs[data-group]')
|
||||||
if (nav) {
|
if (nav) {
|
||||||
const group = nav.dataset.group
|
const group = nav.dataset.group
|
||||||
// Activate the current tab
|
|
||||||
const activeTab = this.tabGroups[group] || "stats"
|
const activeTab = this.tabGroups[group] || "stats"
|
||||||
|
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
this.tabGroups[group] = tab
|
||||||
|
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||||
|
link.classList.toggle('active', link.dataset.tab === tab)
|
||||||
|
})
|
||||||
|
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
|
||||||
|
content.classList.toggle('active', content.dataset.tab === tab)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set initial state (makes active tab visible)
|
||||||
|
switchTab(activeTab)
|
||||||
|
|
||||||
|
// Restore scroll positions now that the active tab is visible
|
||||||
|
if (this._pendingScrollRestores?.length) {
|
||||||
|
for (const { selector, scrollTop } of this._pendingScrollRestores) {
|
||||||
|
const el = selector ? this.element.querySelector(selector) : this.element
|
||||||
|
if (el) el.scrollTop = scrollTop
|
||||||
|
}
|
||||||
|
this._pendingScrollRestores = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tab clicks: DOM-only, no re-render (preserves scroll positions)
|
||||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||||
const tab = link.dataset.tab
|
|
||||||
link.classList.toggle('active', tab === activeTab)
|
|
||||||
link.addEventListener('click', (event) => {
|
link.addEventListener('click', (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.tabGroups[group] = tab
|
switchTab(link.dataset.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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +417,40 @@ export default class MournbladeActorSheet extends HandlebarsApplicationMixin(fou
|
|||||||
await this.document.bannirElementaire(invocIndex)
|
await this.document.bannirElementaire(invocIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle invoking a demon
|
||||||
|
*/
|
||||||
|
static async #onInvoquerDemon(event, target) {
|
||||||
|
event.preventDefault()
|
||||||
|
await this.document.invoquerDemon()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle releasing a demon invocation
|
||||||
|
*/
|
||||||
|
static async #onLibererDemon(event, target) {
|
||||||
|
event.preventDefault()
|
||||||
|
const invocIndex = parseInt(target.dataset.invocIndex ?? "0")
|
||||||
|
await this.document.libererDemon(invocIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle invoking a Law Spirit into an Automaton
|
||||||
|
*/
|
||||||
|
static async #onInvoquerEsprit(event, target) {
|
||||||
|
event.preventDefault()
|
||||||
|
await this.document.invoquerEspritLoi()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle enchanting an item with Loi power
|
||||||
|
*/
|
||||||
|
static async #onEnchanter(event, target) {
|
||||||
|
event.preventDefault()
|
||||||
|
const itemId = target.dataset.itemId
|
||||||
|
await this.document.enchanter(itemId)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle rolling an arme offensif
|
* Handle rolling an arme offensif
|
||||||
* @param {Event} event - The triggering event
|
* @param {Event} event - The triggering event
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export default class MournbladeItemSheet extends HandlebarsApplicationMixin(foun
|
|||||||
actions: {
|
actions: {
|
||||||
editImage: MournbladeItemSheet.#onEditImage,
|
editImage: MournbladeItemSheet.#onEditImage,
|
||||||
postItem: MournbladeItemSheet.#onPostItem,
|
postItem: MournbladeItemSheet.#onPostItem,
|
||||||
|
enchanter: MournbladeItemSheet.#onEnchanter,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,17 +44,24 @@ export default class MournbladeItemSheet extends HandlebarsApplicationMixin(foun
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async _prepareContext() {
|
async _prepareContext() {
|
||||||
|
const item = this.document
|
||||||
|
const enchantableTypes = ["arme", "equipement", "protection", "bouclier"]
|
||||||
|
const actorIsLoyal = item.actor?.getAlignement?.() === "loyal"
|
||||||
|
const alreadyEnchanted = enchantableTypes.includes(item.type) && (item.system.enchantementLoi?.actif ?? false)
|
||||||
|
const canEnchant = enchantableTypes.includes(item.type) && !!item.actor && actorIsLoyal && !alreadyEnchanted
|
||||||
const context = {
|
const context = {
|
||||||
fields: this.document.schema.fields,
|
fields: item.schema.fields,
|
||||||
systemFields: this.document.system.schema.fields,
|
systemFields: item.system.schema.fields,
|
||||||
item: this.document,
|
item,
|
||||||
system: this.document.system,
|
system: item.system,
|
||||||
source: this.document.toObject(),
|
source: item.toObject(),
|
||||||
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }),
|
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.system.description, { async: true }),
|
||||||
isEditMode: true,
|
isEditMode: true,
|
||||||
isEditable: this.isEditable,
|
isEditable: this.isEditable,
|
||||||
isGM: game.user.isGM,
|
isGM: game.user.isGM,
|
||||||
config: game.system.mournblade.config,
|
config: game.system.mournblade.config,
|
||||||
|
canEnchant,
|
||||||
|
enchantementActif: alreadyEnchanted,
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
@@ -96,6 +104,19 @@ export default class MournbladeItemSheet extends HandlebarsApplicationMixin(foun
|
|||||||
|
|
||||||
// #region Actions
|
// #region Actions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle enchanting this item with Loi power
|
||||||
|
*/
|
||||||
|
static async #onEnchanter(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
const item = this.document
|
||||||
|
if (!item.actor) {
|
||||||
|
ui.notifications.warn("Cet objet doit être sur un personnage pour être enchanté.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await item.actor.enchanter(item.id)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle editing the item image
|
* Handle editing the item image
|
||||||
* @param {Event} event - The triggering event
|
* @param {Event} event - The triggering event
|
||||||
@@ -137,7 +158,8 @@ export default class MournbladeItemSheet extends HandlebarsApplicationMixin(foun
|
|||||||
protection: "Protection", equipement: "Équipement", heritage: "Héritage",
|
protection: "Protection", equipement: "Équipement", heritage: "Héritage",
|
||||||
metier: "Métier", capacite: "Capacité", tendance: "Tendance",
|
metier: "Métier", capacite: "Capacité", tendance: "Tendance",
|
||||||
traitchaotique: "Trait Chaotique", traitespece: "Trait d'Espèce",
|
traitchaotique: "Trait Chaotique", traitespece: "Trait d'Espèce",
|
||||||
origine: "Origine", modifier: "Modificateur", monnaie: "Monnaie"
|
origine: "Origine", modifier: "Modificateur", monnaie: "Monnaie",
|
||||||
|
potion: "Potion"
|
||||||
}
|
}
|
||||||
chatData.typeLabel = typeLabels[chatData.type] ?? chatData.type
|
chatData.typeLabel = typeLabels[chatData.type] ?? chatData.type
|
||||||
|
|
||||||
@@ -148,10 +170,30 @@ export default class MournbladeItemSheet extends HandlebarsApplicationMixin(foun
|
|||||||
pacte: "fa-scroll", protection: "fa-shield", equipement: "fa-box",
|
pacte: "fa-scroll", protection: "fa-shield", equipement: "fa-box",
|
||||||
heritage: "fa-dna", metier: "fa-hammer", capacite: "fa-bolt",
|
heritage: "fa-dna", metier: "fa-hammer", capacite: "fa-bolt",
|
||||||
tendance: "fa-yin-yang", traitchaotique: "fa-skull", traitespece: "fa-paw",
|
tendance: "fa-yin-yang", traitchaotique: "fa-skull", traitespece: "fa-paw",
|
||||||
origine: "fa-compass", modifier: "fa-sliders", monnaie: "fa-coins"
|
origine: "fa-compass", modifier: "fa-sliders", monnaie: "fa-coins",
|
||||||
|
potion: "fa-flask"
|
||||||
}
|
}
|
||||||
chatData.typeIcon = typeIcons[chatData.type] ?? "fa-cube"
|
chatData.typeIcon = typeIcons[chatData.type] ?? "fa-cube"
|
||||||
|
|
||||||
|
// Potion: add localized labels for statut and forme
|
||||||
|
if (chatData.type === "potion") {
|
||||||
|
const statutLabels = {
|
||||||
|
inconnue: game.i18n.localize("MNBL.potionInconnue"),
|
||||||
|
efficace: game.i18n.localize("MNBL.potionEfficace"),
|
||||||
|
heroique: game.i18n.localize("MNBL.potionHeroique"),
|
||||||
|
inefficace: game.i18n.localize("MNBL.potionInefficace"),
|
||||||
|
poison: game.i18n.localize("MNBL.potionPoison"),
|
||||||
|
}
|
||||||
|
const formeLabels = {
|
||||||
|
liquide: game.i18n.localize("MNBL.potionLiquide"),
|
||||||
|
onguent: game.i18n.localize("MNBL.potionOnguent"),
|
||||||
|
cachets: game.i18n.localize("MNBL.potionCachets"),
|
||||||
|
pilules: game.i18n.localize("MNBL.potionPilules"),
|
||||||
|
}
|
||||||
|
chatData.system.statutLabel = statutLabels[chatData.system.statut] ?? chatData.system.statut
|
||||||
|
chatData.system.formeLabel = formeLabels[chatData.system.forme] ?? chatData.system.forme
|
||||||
|
}
|
||||||
|
|
||||||
const html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-mournblade/templates/post-item.hbs', chatData)
|
const html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-mournblade/templates/post-item.hbs', chatData)
|
||||||
ChatMessage.create({ user: game.user.id, content: html })
|
ChatMessage.create({ user: game.user.id, content: html })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default class MournbladeCreatureSheet extends MournbladeActorSheet {
|
|||||||
static PARTS = {
|
static PARTS = {
|
||||||
sheet: {
|
sheet: {
|
||||||
template: "systems/fvtt-mournblade/templates/creature-sheet.hbs",
|
template: "systems/fvtt-mournblade/templates/creature-sheet.hbs",
|
||||||
|
scrollable: [".tab.competences", ".tab.equipement", ".tab.biodata"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default class MournbladePersonnageSheet extends MournbladeActorSheet {
|
|||||||
static PARTS = {
|
static PARTS = {
|
||||||
sheet: {
|
sheet: {
|
||||||
template: "systems/fvtt-mournblade/templates/actor-sheet.hbs",
|
template: "systems/fvtt-mournblade/templates/actor-sheet.hbs",
|
||||||
|
scrollable: [".tab.principal", ".tab.competences", ".tab.dons", ".tab.equipement"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ export default class MournbladePersonnageSheet extends MournbladeActorSheet {
|
|||||||
context.dons = foundry.utils.duplicate(actor.getDons())
|
context.dons = foundry.utils.duplicate(actor.getDons())
|
||||||
context.pactes = foundry.utils.duplicate(actor.getPactes())
|
context.pactes = foundry.utils.duplicate(actor.getPactes())
|
||||||
context.alignement = actor.getAlignement()
|
context.alignement = actor.getAlignement()
|
||||||
|
context.isChaotique = context.alignement === "chaotique"
|
||||||
|
context.isLoyal = context.alignement === "loyal"
|
||||||
context.aspect = actor.getAspect()
|
context.aspect = actor.getAspect()
|
||||||
context.marge = actor.getMarge()
|
context.marge = actor.getMarge()
|
||||||
context.tendances = foundry.utils.duplicate(actor.getTendances())
|
context.tendances = foundry.utils.duplicate(actor.getTendances())
|
||||||
@@ -46,6 +49,7 @@ export default class MournbladePersonnageSheet extends MournbladeActorSheet {
|
|||||||
context.metier = foundry.utils.duplicate(actor.getMetier() || {})
|
context.metier = foundry.utils.duplicate(actor.getMetier() || {})
|
||||||
context.combat = actor.getCombatValues()
|
context.combat = actor.getCombatValues()
|
||||||
context.equipements = foundry.utils.duplicate(actor.getEquipments())
|
context.equipements = foundry.utils.duplicate(actor.getEquipments())
|
||||||
|
context.potions = foundry.utils.duplicate(actor.getPotions())
|
||||||
context.modifiers = foundry.utils.duplicate(actor.getModifiers())
|
context.modifiers = foundry.utils.duplicate(actor.getModifiers())
|
||||||
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
|
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
|
||||||
context.runeEffects = foundry.utils.duplicate(actor.getRuneEffects())
|
context.runeEffects = foundry.utils.duplicate(actor.getRuneEffects())
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
|
|||||||
tr: new fields.NumberField({ initial: 0, integer: true }),
|
tr: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
prix: new fields.NumberField({ initial: 0, integer: true }),
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
equipped: new fields.BooleanField({ initial: false })
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
enchantementLoi: new fields.SchemaField({
|
||||||
|
actif: new fields.BooleanField({ initial: false }),
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
antiChaos: new fields.BooleanField({ initial: false }),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,12 @@ export default class BouclierDataModel extends foundry.abstract.TypeDataModel {
|
|||||||
nonletaux: new fields.BooleanField({ initial: false }),
|
nonletaux: new fields.BooleanField({ initial: false }),
|
||||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
prix: new fields.NumberField({ initial: 0, integer: true }),
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
equipped: new fields.BooleanField({ initial: false })
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
enchantementLoi: new fields.SchemaField({
|
||||||
|
actif: new fields.BooleanField({ initial: false }),
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
antiChaos: new fields.BooleanField({ initial: false }),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,11 @@ export default class CreatureDataModel extends foundry.abstract.TypeDataModel {
|
|||||||
alignement: new fields.StringField({ initial: "" }),
|
alignement: new fields.StringField({ initial: "" }),
|
||||||
creatureType: new fields.StringField({ initial: "creature" }),
|
creatureType: new fields.StringField({ initial: "creature" }),
|
||||||
elementType: new fields.StringField({ initial: "" }),
|
elementType: new fields.StringField({ initial: "" }),
|
||||||
|
demonType: new fields.StringField({ initial: "" }),
|
||||||
|
demonPuissance: new fields.StringField({ initial: "" }),
|
||||||
|
automatonType: new fields.StringField({ initial: "" }),
|
||||||
|
automatonPuissance: new fields.StringField({ initial: "" }),
|
||||||
|
automatonVoyageType: new fields.StringField({ initial: "" }),
|
||||||
poids: new fields.StringField({ initial: "" }),
|
poids: new fields.StringField({ initial: "" }),
|
||||||
taille: new fields.StringField({ initial: "" }),
|
taille: new fields.StringField({ initial: "" }),
|
||||||
cheveux: new fields.StringField({ initial: "" }),
|
cheveux: new fields.StringField({ initial: "" }),
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ export default class EquipementDataModel extends foundry.abstract.TypeDataModel
|
|||||||
return {
|
return {
|
||||||
description: new fields.HTMLField({ initial: "" }),
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
prix: new fields.NumberField({ initial: 0, integer: true })
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
enchantementLoi: new fields.SchemaField({
|
||||||
|
actif: new fields.BooleanField({ initial: false }),
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
antiChaos: new fields.BooleanField({ initial: false }),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +81,7 @@ export default class PersonnageDataModel extends foundry.abstract.TypeDataModel
|
|||||||
traumatismes: new fields.StringField({ initial: "" })
|
traumatismes: new fields.StringField({ initial: "" })
|
||||||
}),
|
}),
|
||||||
invocationsElementaires: new fields.ArrayField(new fields.ObjectField(), { initial: [] }),
|
invocationsElementaires: new fields.ArrayField(new fields.ObjectField(), { initial: [] }),
|
||||||
|
invocationsDemons: new fields.ArrayField(new fields.ObjectField(), { initial: [] }),
|
||||||
combat: new fields.SchemaField({
|
combat: new fields.SchemaField({
|
||||||
initbonus: new fields.NumberField({ initial: 0, integer: true }),
|
initbonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
|
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ export default class ProtectionDataModel extends foundry.abstract.TypeDataModel
|
|||||||
degats: new fields.StringField({ initial: "" }),
|
degats: new fields.StringField({ initial: "" }),
|
||||||
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
prix: new fields.NumberField({ initial: 0, integer: true }),
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
equipped: new fields.BooleanField({ initial: false })
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
enchantementLoi: new fields.SchemaField({
|
||||||
|
actif: new fields.BooleanField({ initial: false }),
|
||||||
|
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
antiChaos: new fields.BooleanField({ initial: false }),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
import { MournbladeUtility } from "./mournblade-utility.js";
|
import { MournbladeUtility } from "./mournblade-utility.js";
|
||||||
import { MournbladeRollDialog } from "./applications/mournblade-roll-dialog.mjs";
|
import { MournbladeRollDialog } from "./applications/mournblade-roll-dialog.mjs";
|
||||||
import MournbladeInvocationDialog from "./applications/mournblade-invocation-dialog.mjs";
|
import MournbladeInvocationDialog from "./applications/mournblade-invocation-dialog.mjs";
|
||||||
|
import MournbladeInvocationDemonDialog from "./applications/mournblade-invocation-demon-dialog.mjs";
|
||||||
|
import MournbladeInvocationEspritDialog from "./applications/mournblade-invocation-esprit-dialog.mjs";
|
||||||
|
import MournbladeEnchantementDialog from "./applications/mournblade-enchantement-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]
|
||||||
@@ -91,6 +94,8 @@ export class MournbladeActor extends Actor {
|
|||||||
prepareArme(arme) {
|
prepareArme(arme) {
|
||||||
arme = foundry.utils.duplicate(arme)
|
arme = foundry.utils.duplicate(arme)
|
||||||
let combat = this.getCombatValues()
|
let combat = this.getCombatValues()
|
||||||
|
const enchBonus = (arme.system.enchantementLoi?.actif && arme.system.enchantementLoi?.bonus > 0)
|
||||||
|
? arme.system.enchantementLoi.bonus : 0
|
||||||
if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") {
|
if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") {
|
||||||
arme.system.isMelee = true
|
arme.system.isMelee = true
|
||||||
let competence = 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")
|
||||||
@@ -98,7 +103,7 @@ export class MournbladeActor extends Actor {
|
|||||||
arme.system.competence = foundry.utils.duplicate(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 + combat.attaqueModifier
|
arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + combat.attaqueModifier + enchBonus
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -115,7 +120,7 @@ export class MournbladeActor extends Actor {
|
|||||||
if (competence) {
|
if (competence) {
|
||||||
arme.system.competence = foundry.utils.duplicate(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 + combat.attaqueModifier
|
arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + combat.attaqueModifier + enchBonus
|
||||||
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
|
||||||
@@ -133,12 +138,14 @@ export class MournbladeActor extends Actor {
|
|||||||
prepareBouclier(bouclier) {
|
prepareBouclier(bouclier) {
|
||||||
bouclier = foundry.utils.duplicate(bouclier)
|
bouclier = foundry.utils.duplicate(bouclier)
|
||||||
let combat = this.getCombatValues()
|
let combat = this.getCombatValues()
|
||||||
|
const enchBonus = (bouclier.system.enchantementLoi?.actif && bouclier.system.enchantementLoi?.bonus > 0)
|
||||||
|
? bouclier.system.enchantementLoi.bonus : 0
|
||||||
let competence = 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) {
|
if (competence) {
|
||||||
bouclier.system.competence = foundry.utils.duplicate(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 + enchBonus
|
||||||
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
|
||||||
@@ -213,6 +220,9 @@ export class MournbladeActor extends Actor {
|
|||||||
getMonnaies() {
|
getMonnaies() {
|
||||||
return this.getItemSorted(["monnaie"])
|
return this.getItemSorted(["monnaie"])
|
||||||
}
|
}
|
||||||
|
getPotions() {
|
||||||
|
return this.getItemSorted(["potion"])
|
||||||
|
}
|
||||||
getArmors() {
|
getArmors() {
|
||||||
return this.getItemSorted(["protection"])
|
return this.getItemSorted(["protection"])
|
||||||
}
|
}
|
||||||
@@ -739,6 +749,90 @@ export class MournbladeActor extends Actor {
|
|||||||
ui.notifications.info(`L'Élémentaire ${invoc.actorName} a été banni. ${invoc.soulCost} points d'Âme libérés.`)
|
ui.notifications.info(`L'Élémentaire ${invoc.actorName} a été banni. ${invoc.soulCost} points d'Âme libérés.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async invoquerDemon() {
|
||||||
|
const normalize = str => str.toLowerCase()
|
||||||
|
.replace(/œ/g, "oe").replace(/æ/g, "ae")
|
||||||
|
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.replace(/[^a-z0-9]+/g, " ").trim()
|
||||||
|
const hasKeywords = (item, ...words) => {
|
||||||
|
const n = normalize(item.name)
|
||||||
|
return words.every(w => n.includes(normalize(w)))
|
||||||
|
}
|
||||||
|
const coercitionComp = this.items.find(c => c.type === "competence" && hasKeywords(c, "coercition"))
|
||||||
|
if (!coercitionComp) {
|
||||||
|
ui.notifications.warn("La compétence Coercition est requise pour invoquer un Démon.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const isChaotique = this.system.balance.chaos > this.system.balance.loi
|
||||||
|
const hasOeilSorcier = this.items.some(c => (c.type === "capacite" || c.type === "don") && hasKeywords(c, "oeil", "sorcier"))
|
||||||
|
const hasRuneChaos = this.items.some(i => i.type === "rune" && hasKeywords(i, "chaos"))
|
||||||
|
if (!isChaotique || !hasOeilSorcier || !hasRuneChaos) {
|
||||||
|
const missing = []
|
||||||
|
if (!isChaotique) missing.push("alignement chaotique (Chaos > Loi)")
|
||||||
|
if (!hasOeilSorcier) missing.push("Capacité/Don Œil du Sorcier")
|
||||||
|
if (!hasRuneChaos) missing.push("Rune du Chaos")
|
||||||
|
ui.notifications.warn(`Prérequis manquants pour l'invocation démoniaque : ${missing.join(", ")}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let rollData = this.getCommonRollData("tre", coercitionComp._id)
|
||||||
|
rollData.isInvocationDemon = true
|
||||||
|
rollData.mainDice = "1d10"
|
||||||
|
|
||||||
|
await MournbladeInvocationDemonDialog.create(this, rollData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async libererDemon(invocIndex) {
|
||||||
|
const invocations = foundry.utils.duplicate(this.system.invocationsDemons || [])
|
||||||
|
const invoc = invocations[invocIndex]
|
||||||
|
if (!invoc) return
|
||||||
|
|
||||||
|
invocations.splice(invocIndex, 1)
|
||||||
|
await this.update({ "system.invocationsDemons": invocations })
|
||||||
|
ui.notifications.info(`Le Démon ${invoc.demonName ?? "invoqué"} a été libéré.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async invoquerEspritLoi() {
|
||||||
|
const normalize = str => str.toLowerCase()
|
||||||
|
.replace(/œ/g, "oe").replace(/æ/g, "ae")
|
||||||
|
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.replace(/[^a-z0-9]+/g, " ").trim()
|
||||||
|
const hasKeywords = (item, ...words) => {
|
||||||
|
const n = normalize(item.name)
|
||||||
|
return words.every(w => n.includes(normalize(w)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const persuasionComp = this.items.find(c => c.type === "competence" && hasKeywords(c, "persuasion"))
|
||||||
|
const isLoyal = this.system.balance.loi > this.system.balance.chaos
|
||||||
|
const hasRuneLoi = this.items.some(i => i.type === "rune" && hasKeywords(i, "loi"))
|
||||||
|
|
||||||
|
if (!isLoyal || !hasRuneLoi) {
|
||||||
|
const missing = []
|
||||||
|
if (!isLoyal) missing.push("alignement loyal (Loi > Chaos)")
|
||||||
|
if (!hasRuneLoi) missing.push("Rune de la Loi")
|
||||||
|
ui.notifications.warn(`Prérequis manquants : ${missing.join(", ")}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rollData = this.getCommonRollData("tre", persuasionComp?._id ?? null)
|
||||||
|
rollData.isInvocationEsprit = true
|
||||||
|
rollData.mainDice = "1d10"
|
||||||
|
|
||||||
|
await MournbladeInvocationEspritDialog.create(this, rollData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async enchanter(itemId) {
|
||||||
|
const item = this.items.get(itemId)
|
||||||
|
if (!item) return
|
||||||
|
if (!["arme", "equipement", "protection", "bouclier"].includes(item.type)) {
|
||||||
|
ui.notifications.warn("Seules les armes, équipements, protections et boucliers peuvent être enchantés.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await MournbladeEnchantementDialog.create(this, item)
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
async rollArmeOffensif(armeId) {
|
async rollArmeOffensif(armeId) {
|
||||||
let arme = this.items.get(armeId)
|
let arme = this.items.get(armeId)
|
||||||
|
|||||||
@@ -90,18 +90,55 @@ export class MournbladeConfig {
|
|||||||
elue: game.i18n.localize("MNBL.typeCapaciteElue"),
|
elue: game.i18n.localize("MNBL.typeCapaciteElue"),
|
||||||
elementaire: game.i18n.localize("MNBL.typeCapaciteElementaire"),
|
elementaire: game.i18n.localize("MNBL.typeCapaciteElementaire"),
|
||||||
demoniaque: game.i18n.localize("MNBL.typeCapaciteDemoniaque"),
|
demoniaque: game.i18n.localize("MNBL.typeCapaciteDemoniaque"),
|
||||||
|
automaton: game.i18n.localize("MNBL.typeCapaciteAutomaton"),
|
||||||
creature: game.i18n.localize("MNBL.typeCapaciteCreature"),
|
creature: game.i18n.localize("MNBL.typeCapaciteCreature"),
|
||||||
},
|
},
|
||||||
creatureTypeOptions: {
|
creatureTypeOptions: {
|
||||||
creature: game.i18n.localize("MNBL.creatureTypeCreature"),
|
creature: game.i18n.localize("MNBL.creatureTypeCreature"),
|
||||||
demon: game.i18n.localize("MNBL.creatureTypeDemon"),
|
demon: game.i18n.localize("MNBL.creatureTypeDemon"),
|
||||||
elementaire: game.i18n.localize("MNBL.creatureTypeElementaire"),
|
elementaire: game.i18n.localize("MNBL.creatureTypeElementaire"),
|
||||||
|
automaton: game.i18n.localize("MNBL.creatureTypeAutomaton"),
|
||||||
|
},
|
||||||
|
demonTypeOptions: {
|
||||||
|
"": game.i18n.localize("MNBL.demonTypeNone"),
|
||||||
|
combat: game.i18n.localize("MNBL.demonTypeCombat"),
|
||||||
|
desir: game.i18n.localize("MNBL.demonTypeDesir"),
|
||||||
|
savoir: game.i18n.localize("MNBL.demonTypeSavoir"),
|
||||||
|
protection: game.i18n.localize("MNBL.demonTypeProtection"),
|
||||||
|
voyage: game.i18n.localize("MNBL.demonTypeVoyage"),
|
||||||
|
},
|
||||||
|
demonPuissanceOptions: {
|
||||||
|
"": game.i18n.localize("MNBL.demonPuissanceNone"),
|
||||||
|
mineur: game.i18n.localize("MNBL.demonPuissanceMineur"),
|
||||||
|
median: game.i18n.localize("MNBL.demonPuissanceMedian"),
|
||||||
|
majeur: game.i18n.localize("MNBL.demonPuissanceMajeur"),
|
||||||
},
|
},
|
||||||
elementTypeOptions: {
|
elementTypeOptions: {
|
||||||
air: game.i18n.localize("MNBL.elementTypeAir"),
|
air: game.i18n.localize("MNBL.elementTypeAir"),
|
||||||
terre: game.i18n.localize("MNBL.elementTypeTerre"),
|
terre: game.i18n.localize("MNBL.elementTypeTerre"),
|
||||||
feu: game.i18n.localize("MNBL.elementTypeFeu"),
|
feu: game.i18n.localize("MNBL.elementTypeFeu"),
|
||||||
eau: game.i18n.localize("MNBL.elementTypeEau"),
|
eau: game.i18n.localize("MNBL.elementTypeEau"),
|
||||||
|
},
|
||||||
|
automatonTypeOptions: {
|
||||||
|
"": game.i18n.localize("MNBL.automatonTypeNone"),
|
||||||
|
combat: game.i18n.localize("MNBL.automatonTypeCombat"),
|
||||||
|
voyage: game.i18n.localize("MNBL.automatonTypeVoyage"),
|
||||||
|
perception: game.i18n.localize("MNBL.automatonTypePerception"),
|
||||||
|
restauration: game.i18n.localize("MNBL.automatonTypeRestauration"),
|
||||||
|
reparateur: game.i18n.localize("MNBL.automatonTypeReparateur"),
|
||||||
|
},
|
||||||
|
automatonPuissanceOptions: {
|
||||||
|
"": game.i18n.localize("MNBL.automatonPuissanceNone"),
|
||||||
|
mineur: game.i18n.localize("MNBL.automatonPuissanceMineur"),
|
||||||
|
median: game.i18n.localize("MNBL.automatonPuissanceMedian"),
|
||||||
|
majeur: game.i18n.localize("MNBL.automatonPuissanceMajeur"),
|
||||||
|
},
|
||||||
|
automatonVoyageTypeOptions: {
|
||||||
|
"": game.i18n.localize("MNBL.automatonVoyageTypeNone"),
|
||||||
|
terrestre: game.i18n.localize("MNBL.automatonVoyageTypeTerrestre"),
|
||||||
|
aquatique: game.i18n.localize("MNBL.automatonVoyageTypeAquatique"),
|
||||||
|
aerien: game.i18n.localize("MNBL.automatonVoyageTypeAerien"),
|
||||||
|
extradimensionnel: game.i18n.localize("MNBL.automatonVoyageTypeExtradimensionnel"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,8 +219,15 @@ export class MournbladeUtility {
|
|||||||
'systems/fvtt-mournblade/templates/partial-item-description.hbs',
|
'systems/fvtt-mournblade/templates/partial-item-description.hbs',
|
||||||
'systems/fvtt-mournblade/templates/partial-item-header.hbs',
|
'systems/fvtt-mournblade/templates/partial-item-header.hbs',
|
||||||
'systems/fvtt-mournblade/templates/partial-item-nav.hbs',
|
'systems/fvtt-mournblade/templates/partial-item-nav.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/partial-item-enchantement.hbs',
|
||||||
'systems/fvtt-mournblade/templates/dialog-invocation-elementaire.hbs',
|
'systems/fvtt-mournblade/templates/dialog-invocation-elementaire.hbs',
|
||||||
'systems/fvtt-mournblade/templates/chat-invocation-result.hbs',
|
'systems/fvtt-mournblade/templates/chat-invocation-result.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/dialog-invocation-demon.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/chat-invocation-demon-result.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/dialog-enchantement.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/chat-enchantement-result.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/dialog-invocation-esprit.hbs',
|
||||||
|
'systems/fvtt-mournblade/templates/chat-invocation-esprit-result.hbs',
|
||||||
]
|
]
|
||||||
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||||
}
|
}
|
||||||
@@ -1079,9 +1086,7 @@ export class MournbladeUtility {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const actor = rollData.tokenId
|
const actor = game.actors.get(rollData.actorId)
|
||||||
? game.canvas.tokens.get(rollData.tokenId)?.actor
|
|
||||||
: game.actors.get(rollData.actorId)
|
|
||||||
|
|
||||||
const pa = rollData.pointsAme ?? 1
|
const pa = rollData.pointsAme ?? 1
|
||||||
const seuil = rollData.runeSeuil ?? 0
|
const seuil = rollData.runeSeuil ?? 0
|
||||||
@@ -1269,6 +1274,8 @@ export class MournbladeUtility {
|
|||||||
rollData.createdActorName = createdActorName
|
rollData.createdActorName = createdActorName
|
||||||
rollData.bonusPacte = bonusPacte
|
rollData.bonusPacte = bonusPacte
|
||||||
rollData.isGM = game.user.isGM
|
rollData.isGM = game.user.isGM
|
||||||
|
const powersByTier = { mineur: 2, median: 3, majeur: 4 }
|
||||||
|
rollData.invocationPowerCount = powersByTier[rollData.invocationTier] ?? 2
|
||||||
|
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
@@ -1276,4 +1283,235 @@ export class MournbladeUtility {
|
|||||||
}, { ...rollData, rollMode: "blindroll" })
|
}, { ...rollData, rollMode: "blindroll" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async rollInvocationDemon(rollData) {
|
||||||
|
const actor = rollData.tokenId
|
||||||
|
? game.canvas.tokens.get(rollData.tokenId)?.actor
|
||||||
|
: game.actors.get(rollData.actorId)
|
||||||
|
|
||||||
|
if (!actor) {
|
||||||
|
ui.notifications.error("Acteur introuvable pour l'invocation démoniaque.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const soulCost = rollData.invocationSoulCost ?? rollData.invocationSeuil ?? 20
|
||||||
|
const compNiveau = rollData.competence?.system?.niveau ?? 0
|
||||||
|
const compMod = compNiveau === 0 ? -3 : 0
|
||||||
|
const modificateur = rollData.modificateur ?? 0
|
||||||
|
|
||||||
|
// Validate soul
|
||||||
|
const ameDisponible = Math.max(0, actor.system.ame.currentmax - actor.system.ame.value)
|
||||||
|
if (ameDisponible < soulCost) {
|
||||||
|
ui.notifications.warn(`Âme insuffisante pour cette invocation (requis : ${soulCost}, disponible : ${ameDisponible}).`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rollData.difficulte = rollData.invocationSeuil
|
||||||
|
rollData.diceFormula = `${rollData.mainDice ?? "1d10"}+${rollData.attr.value}+${compNiveau}+${modificateur}+${compMod}+${rollData.malusSante}+${rollData.malusAme}`
|
||||||
|
|
||||||
|
const myRoll = await new Roll(rollData.diceFormula).evaluate()
|
||||||
|
await this.showDiceSoNice(myRoll, "blindroll")
|
||||||
|
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||||
|
rollData.diceResult = myRoll.terms[0].results[0].result
|
||||||
|
rollData.finalResult = myRoll.total
|
||||||
|
this.computeResult(rollData)
|
||||||
|
|
||||||
|
// Soul cost handling
|
||||||
|
let ameDeduct = soulCost
|
||||||
|
let d20Result = null
|
||||||
|
let isDemonAttaque = false
|
||||||
|
let isDisastreDramatique = false
|
||||||
|
let isTraitChaotique = false
|
||||||
|
|
||||||
|
if (rollData.isSuccess || rollData.isHeroique) {
|
||||||
|
// Soul spent immediately (not blocked)
|
||||||
|
await actor.subPointsAme("prononcer", soulCost)
|
||||||
|
|
||||||
|
// Track active invocation
|
||||||
|
const invocations = foundry.utils.duplicate(actor.system.invocationsDemons || [])
|
||||||
|
invocations.push({ demonName: "Démon invoqué", soulCost, date: Date.now() })
|
||||||
|
await actor.update({ "system.invocationsDemons": invocations })
|
||||||
|
} else if (rollData.isDramatique) {
|
||||||
|
// All soul lost
|
||||||
|
await actor.subPointsAme("prononcer", soulCost)
|
||||||
|
|
||||||
|
// Roll d20 for dramatic failure consequences
|
||||||
|
const d20Roll = await new Roll("1d20").evaluate()
|
||||||
|
await this.showDiceSoNice(d20Roll, "blindroll")
|
||||||
|
d20Result = d20Roll.total
|
||||||
|
if (d20Result === 1 || d20Result === 11) {
|
||||||
|
isDisastreDramatique = true
|
||||||
|
} else if (d20Result % 2 !== 0) {
|
||||||
|
// Odd (not 1 or 11) → demon attacks
|
||||||
|
isDemonAttaque = true
|
||||||
|
} else {
|
||||||
|
// Even → chaotic trait
|
||||||
|
isTraitChaotique = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Simple failure: half soul lost (round up)
|
||||||
|
ameDeduct = Math.ceil(soulCost / 2)
|
||||||
|
await actor.subPointsAme("prononcer", ameDeduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
rollData.invocationSoulDeducted = (rollData.isSuccess || rollData.isHeroique) ? soulCost : ameDeduct
|
||||||
|
rollData.d20Result = d20Result
|
||||||
|
rollData.isDemonAttaque = isDemonAttaque
|
||||||
|
rollData.isDisastreDramatique = isDisastreDramatique
|
||||||
|
rollData.isTraitChaotique = isTraitChaotique
|
||||||
|
rollData.isGM = game.user.isGM
|
||||||
|
rollData.claValue = actor.system.attributs?.cla?.value ?? 0
|
||||||
|
|
||||||
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/fvtt-mournblade/templates/chat-invocation-demon-result.hbs`, rollData)
|
||||||
|
}, { ...rollData, rollMode: "blindroll" })
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async rollInvocationEsprit(rollData) {
|
||||||
|
const actor = rollData.tokenId
|
||||||
|
? game.canvas.tokens.get(rollData.tokenId)?.actor
|
||||||
|
: game.actors.get(rollData.actorId)
|
||||||
|
|
||||||
|
if (!actor) {
|
||||||
|
ui.notifications.error("Acteur introuvable pour l'invocation d'un Esprit de la Loi.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const soulCost = rollData.invocationSoulCost ?? 15
|
||||||
|
const compNiveau = rollData.competence?.system?.niveau ?? 0
|
||||||
|
const compMod = compNiveau === 0 ? -3 : 0
|
||||||
|
const modificateur = rollData.modificateur ?? 0
|
||||||
|
|
||||||
|
const ameDisponible = Math.max(0, actor.system.ame.currentmax - actor.system.ame.value)
|
||||||
|
if (ameDisponible < soulCost) {
|
||||||
|
ui.notifications.warn(`Âme insuffisante (requis : ${soulCost}, disponible : ${ameDisponible}).`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rollData.difficulte = soulCost
|
||||||
|
rollData.diceFormula = `${rollData.mainDice ?? "1d10"}+${rollData.attr.value}+${compNiveau}+${modificateur}+${compMod}+${rollData.malusSante}+${rollData.malusAme}`
|
||||||
|
|
||||||
|
const myRoll = await new Roll(rollData.diceFormula).evaluate()
|
||||||
|
await this.showDiceSoNice(myRoll, "roll")
|
||||||
|
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||||
|
rollData.diceResult = myRoll.terms[0].results[0].result
|
||||||
|
rollData.finalResult = myRoll.total
|
||||||
|
this.computeResult(rollData)
|
||||||
|
|
||||||
|
let ameDeduct = soulCost
|
||||||
|
|
||||||
|
if (rollData.isSuccess || rollData.isHeroique) {
|
||||||
|
await actor.subPointsAme("prononcer", soulCost)
|
||||||
|
} else if (rollData.isDramatique) {
|
||||||
|
// All soul lost, Réceptacle destroyed
|
||||||
|
await actor.subPointsAme("prononcer", soulCost)
|
||||||
|
} else {
|
||||||
|
// Simple failure: half soul lost (round up)
|
||||||
|
ameDeduct = Math.ceil(soulCost / 2)
|
||||||
|
await actor.subPointsAme("prononcer", ameDeduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
rollData.invocationSoulDeducted = (rollData.isSuccess || rollData.isHeroique) ? soulCost : ameDeduct
|
||||||
|
rollData.isGM = game.user.isGM
|
||||||
|
|
||||||
|
const typeLabels = {
|
||||||
|
combat: "Combat", voyage: "Voyage", perception: "Perception",
|
||||||
|
restauration: "Restauration", reparateur: "Réparateur",
|
||||||
|
}
|
||||||
|
const puissanceLabels = { mineur: "Mineur", median: "Médian", majeur: "Majeur" }
|
||||||
|
rollData.automatonTypeLabel = typeLabels[rollData.automatonType] ?? rollData.automatonType
|
||||||
|
rollData.automatonPuissanceLabel = puissanceLabels[rollData.automatonPuissance] ?? rollData.automatonPuissance
|
||||||
|
|
||||||
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/fvtt-mournblade/templates/chat-invocation-esprit-result.hbs`, rollData)
|
||||||
|
}, { ...rollData, rollMode: "roll" })
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async rollEnchantement({ actor, item, ptsAme, antiChaos, modificateur,
|
||||||
|
savoirRunesComp, hautParlerComp, artisanatComp, claValeur, limiteur }) {
|
||||||
|
|
||||||
|
// Validate soul
|
||||||
|
const ameDisponible = Math.max(0, actor.system.ame.currentmax - actor.system.ame.value)
|
||||||
|
if (ameDisponible < ptsAme) {
|
||||||
|
ui.notifications.warn(`Âme insuffisante (requis : ${ptsAme}, disponible : ${ameDisponible}).`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const savoirNiveau = savoirRunesComp?.system?.niveau ?? 0
|
||||||
|
const compMod = savoirNiveau === 0 ? -3 : 0
|
||||||
|
const basePool = claValeur + savoirNiveau + compMod + (modificateur ?? 0)
|
||||||
|
const effectivePool = limiteur !== null ? Math.min(basePool, limiteur) : basePool
|
||||||
|
const difficulte = ptsAme
|
||||||
|
|
||||||
|
const formula = `1d10+${effectivePool}`
|
||||||
|
const myRoll = await new Roll(formula).evaluate()
|
||||||
|
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||||
|
|
||||||
|
const rollData = this.getBasicRollData()
|
||||||
|
rollData.alias = actor.name
|
||||||
|
rollData.actorImg = actor.img
|
||||||
|
rollData.diceResult = myRoll.terms[0].results[0].result
|
||||||
|
rollData.finalResult = myRoll.total
|
||||||
|
rollData.difficulte = difficulte
|
||||||
|
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||||
|
this.computeResult(rollData)
|
||||||
|
|
||||||
|
// Compute bonus
|
||||||
|
let bonusBase = Math.floor(ptsAme / 5)
|
||||||
|
let bonusFinal = bonusBase
|
||||||
|
let ameDeduct = ptsAme
|
||||||
|
let itemDestroyed = false
|
||||||
|
let message = ""
|
||||||
|
|
||||||
|
if (rollData.isHeroique) {
|
||||||
|
bonusFinal = bonusBase + 1
|
||||||
|
await actor.subPointsAme("prononcer", ptsAme)
|
||||||
|
await item.update({
|
||||||
|
"system.enchantementLoi.actif": true,
|
||||||
|
"system.enchantementLoi.bonus": bonusFinal,
|
||||||
|
"system.enchantementLoi.antiChaos": antiChaos,
|
||||||
|
})
|
||||||
|
message = `Réussite héroïque ! L'objet est enchanté avec un bonus de +${bonusFinal}.`
|
||||||
|
} else if (rollData.isSuccess) {
|
||||||
|
await actor.subPointsAme("prononcer", ptsAme)
|
||||||
|
await item.update({
|
||||||
|
"system.enchantementLoi.actif": true,
|
||||||
|
"system.enchantementLoi.bonus": bonusFinal,
|
||||||
|
"system.enchantementLoi.antiChaos": antiChaos,
|
||||||
|
})
|
||||||
|
message = `Succès ! L'objet est enchanté avec un bonus de +${bonusFinal}.`
|
||||||
|
} else if (rollData.isDramatique) {
|
||||||
|
ameDeduct = ptsAme
|
||||||
|
await actor.subPointsAme("prononcer", ameDeduct)
|
||||||
|
await item.delete()
|
||||||
|
itemDestroyed = true
|
||||||
|
message = `Échec dramatique ! Tous les points d'Âme sont perdus et l'objet est détruit !`
|
||||||
|
} else {
|
||||||
|
// Failure: half soul lost
|
||||||
|
ameDeduct = Math.ceil(ptsAme / 2)
|
||||||
|
await actor.subPointsAme("prononcer", ameDeduct)
|
||||||
|
message = `Échec ! ${ameDeduct} points d'Âme perdus. L'objet n'est pas enchanté.`
|
||||||
|
}
|
||||||
|
|
||||||
|
rollData.itemName = item.name
|
||||||
|
rollData.itemImg = item.img
|
||||||
|
rollData.ptsAme = ptsAme
|
||||||
|
rollData.antiChaos = antiChaos
|
||||||
|
rollData.bonusFinal = bonusFinal
|
||||||
|
rollData.ameDeduct = ameDeduct
|
||||||
|
rollData.itemDestroyed = itemDestroyed
|
||||||
|
rollData.enchantMessage = message
|
||||||
|
rollData.effectivePool = effectivePool
|
||||||
|
rollData.savoirNiveau = savoirNiveau
|
||||||
|
|
||||||
|
this.createChatWithRollMode(actor.name, {
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
`systems/fvtt-mournblade/templates/chat-enchantement-result.hbs`, rollData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
MANIFEST-000368
|
MANIFEST-000396
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:23.718556 7fd754fed6c0 Recovering log #366
|
2026/05/02-22:17:12.618357 7fd747fff6c0 Recovering log #394
|
||||||
2026/05/01-23:51:23.773168 7fd754fed6c0 Delete type=3 #364
|
2026/05/02-22:17:12.628023 7fd747fff6c0 Delete type=3 #392
|
||||||
2026/05/01-23:51:23.773253 7fd754fed6c0 Delete type=0 #366
|
2026/05/02-22:17:12.628086 7fd747fff6c0 Delete type=0 #394
|
||||||
2026/05/02-08:26:03.206863 7fd7477fe6c0 Level-0 table #371: started
|
2026/05/02-23:14:30.830178 7fd7477fe6c0 Level-0 table #399: started
|
||||||
2026/05/02-08:26:03.206928 7fd7477fe6c0 Level-0 table #371: 0 bytes OK
|
2026/05/02-23:14:30.830206 7fd7477fe6c0 Level-0 table #399: 0 bytes OK
|
||||||
2026/05/02-08:26:03.241219 7fd7477fe6c0 Delete type=0 #369
|
2026/05/02-23:14:30.842898 7fd7477fe6c0 Delete type=0 #397
|
||||||
2026/05/02-08:26:03.317629 7fd7477fe6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.854693 7fd7477fe6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.849265 7fd7557ee6c0 Recovering log #362
|
2026/05/02-10:42:39.976018 7fd755fef6c0 Recovering log #390
|
||||||
2026/05/01-23:45:25.858985 7fd7557ee6c0 Delete type=3 #360
|
2026/05/02-10:42:39.986262 7fd755fef6c0 Delete type=3 #388
|
||||||
2026/05/01-23:45:25.859069 7fd7557ee6c0 Delete type=0 #362
|
2026/05/02-10:42:39.986345 7fd755fef6c0 Delete type=0 #390
|
||||||
2026/05/01-23:51:17.894067 7fd7477fe6c0 Level-0 table #367: started
|
2026/05/02-10:55:36.095495 7fd7477fe6c0 Level-0 table #395: started
|
||||||
2026/05/01-23:51:17.894105 7fd7477fe6c0 Level-0 table #367: 0 bytes OK
|
2026/05/02-10:55:36.095552 7fd7477fe6c0 Level-0 table #395: 0 bytes OK
|
||||||
2026/05/01-23:51:17.920657 7fd7477fe6c0 Delete type=0 #365
|
2026/05/02-10:55:36.101888 7fd7477fe6c0 Delete type=0 #393
|
||||||
2026/05/01-23:51:18.003271 7fd7477fe6c0 Manual compaction at level-0 from '!items!0swiE8k5zfUIqmXu' @ 72057594037927935 : 1 .. '!items!wv5EiePmPTpqFutt' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.113150 7fd7477fe6c0 Manual compaction at level-0 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.
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000002
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
2026/05/02-23:15:37.006124 7fcf117ed6c0 Delete type=3 #1
|
||||||
|
2026/05/02-23:15:37.009476 7fcef3fff6c0 Level-0 table #5: started
|
||||||
|
2026/05/02-23:15:37.013256 7fcef3fff6c0 Level-0 table #5: 30382 bytes OK
|
||||||
|
2026/05/02-23:15:37.019148 7fcef3fff6c0 Delete type=0 #3
|
||||||
|
2026/05/02-23:15:37.019361 7fcef3fff6c0 Manual compaction at level-0 from '!actors!AutomCombatMaj001' @ 72057594037927935 : 1 .. '!actors.items!OeilDeVerite0001.OeilVeritProt001' @ 0 : 0; will stop at (end)
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000002
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
2026/05/02-22:42:38.620324 7fe3377fe6c0 Delete type=3 #1
|
||||||
|
2026/05/02-22:42:38.623484 7fe335ffb6c0 Level-0 table #5: started
|
||||||
|
2026/05/02-22:42:38.627227 7fe335ffb6c0 Level-0 table #5: 3865 bytes OK
|
||||||
|
2026/05/02-22:42:38.633343 7fe335ffb6c0 Delete type=0 #3
|
||||||
|
2026/05/02-22:42:38.633487 7fe335ffb6c0 Manual compaction at level-0 from '!items!CapAutoComAbs001' @ 72057594037927935 : 1 .. '!items!CapAutoVoyVit001' @ 0 : 0; will stop at (end)
|
||||||
Binary file not shown.
BIN
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000012
|
MANIFEST-000047
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
2026/05/01-23:51:23.539516 7fd754fed6c0 Delete type=3 #1
|
2026/05/02-22:17:12.533680 7fd754fed6c0 Recovering log #44
|
||||||
2026/05/02-08:26:02.910133 7fd7477fe6c0 Level-0 table #15: started
|
2026/05/02-22:17:12.543680 7fd754fed6c0 Delete type=3 #42
|
||||||
2026/05/02-08:26:02.928665 7fd7477fe6c0 Level-0 table #15: 17652 bytes OK
|
2026/05/02-22:17:12.543746 7fd754fed6c0 Delete type=0 #44
|
||||||
2026/05/02-08:26:02.971253 7fd7477fe6c0 Delete type=0 #13
|
2026/05/02-23:14:30.692810 7fd7477fe6c0 Level-0 table #50: started
|
||||||
2026/05/02-08:26:03.098794 7fd7477fe6c0 Manual compaction at level-0 from '!actors!ElemAirMaj0000003' @ 72057594037927935 : 1 .. '!actors.items!zYGQJ8FibxCCWynl' @ 0 : 0; will stop at '!actors.items!zYGQJ8FibxCCWynl' @ 118 : 1
|
2026/05/02-23:14:30.701352 7fd7477fe6c0 Level-0 table #50: 17662 bytes OK
|
||||||
2026/05/02-08:26:03.098818 7fd7477fe6c0 Compacting 2@0 + 0@1 files
|
2026/05/02-23:14:30.711206 7fd7477fe6c0 Delete type=0 #48
|
||||||
2026/05/02-08:26:03.114677 7fd7477fe6c0 Generated table #16@0: 61 keys, 12773 bytes
|
2026/05/02-23:14:30.731427 7fd7477fe6c0 Manual compaction at level-0 from '!actors!ElemAirMaj0000003' @ 72057594037927935 : 1 .. '!actors.items!xgdbv9heAyLXGkCu' @ 0 : 0; will stop at '!actors.items!xgdbv9heAyLXGkCu' @ 547 : 1
|
||||||
2026/05/02-08:26:03.114714 7fd7477fe6c0 Compacted 2@0 + 0@1 files => 12773 bytes
|
2026/05/02-23:14:30.731439 7fd7477fe6c0 Compacting 1@0 + 1@1 files
|
||||||
2026/05/02-08:26:03.143492 7fd7477fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
2026/05/02-23:14:30.738933 7fd7477fe6c0 Generated table #51@0: 61 keys, 12842 bytes
|
||||||
2026/05/02-08:26:03.143650 7fd7477fe6c0 Delete type=2 #10
|
2026/05/02-23:14:30.739005 7fd7477fe6c0 Compacted 1@0 + 1@1 files => 12842 bytes
|
||||||
2026/05/02-08:26:03.143790 7fd7477fe6c0 Delete type=2 #15
|
2026/05/02-23:14:30.747174 7fd7477fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
2026/05/02-08:26:03.143908 7fd7477fe6c0 Manual compaction at level-0 from '!actors.items!zYGQJ8FibxCCWynl' @ 118 : 1 .. '!actors.items!zYGQJ8FibxCCWynl' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.747353 7fd7477fe6c0 Delete type=2 #46
|
||||||
|
2026/05/02-23:14:30.747504 7fd7477fe6c0 Delete type=2 #50
|
||||||
|
2026/05/02-23:14:30.779059 7fd7477fe6c0 Manual compaction at level-0 from '!actors.items!xgdbv9heAyLXGkCu' @ 547 : 1 .. '!actors.items!xgdbv9heAyLXGkCu' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
2026/05/01-23:51:23.458933 7fd754fed6c0 Log #8: 0 ops saved to Table #11 OK
|
2026/05/02-10:42:39.892083 7fd754fed6c0 Recovering log #39
|
||||||
2026/05/01-23:51:23.459064 7fd754fed6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/creatures-elementaires/creatures-elementaires/000008.log: OK
|
2026/05/02-10:42:39.902219 7fd754fed6c0 Delete type=3 #37
|
||||||
2026/05/01-23:51:23.459141 7fd754fed6c0 Table #10: 61 entries OK
|
2026/05/02-10:42:39.902273 7fd754fed6c0 Delete type=0 #39
|
||||||
2026/05/01-23:51:23.477072 7fd754fed6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-mournblade/packs/creatures-elementaires/creatures-elementaires; recovered 1 files; 12818 bytes. Some data may have been lost. ****
|
2026/05/02-10:55:36.036221 7fd7477fe6c0 Level-0 table #45: started
|
||||||
|
2026/05/02-10:55:36.039553 7fd7477fe6c0 Level-0 table #45: 17295 bytes OK
|
||||||
|
2026/05/02-10:55:36.045711 7fd7477fe6c0 Delete type=0 #43
|
||||||
|
2026/05/02-10:55:36.063221 7fd7477fe6c0 Manual compaction at level-0 from '!actors!ElemAirMaj0000003' @ 72057594037927935 : 1 .. '!actors.items!vZXf6vfOj986NEYr' @ 0 : 0; will stop at '!actors.items!zCAO1NzNTX1SCdQ8' @ 456 : 0
|
||||||
|
2026/05/02-10:55:36.063230 7fd7477fe6c0 Compacting 1@0 + 1@1 files
|
||||||
|
2026/05/02-10:55:36.066937 7fd7477fe6c0 Generated table #46@0: 61 keys, 12728 bytes
|
||||||
|
2026/05/02-10:55:36.066956 7fd7477fe6c0 Compacted 1@0 + 1@1 files => 12728 bytes
|
||||||
|
2026/05/02-10:55:36.073653 7fd7477fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
|
2026/05/02-10:55:36.073784 7fd7477fe6c0 Delete type=2 #41
|
||||||
|
2026/05/02-10:55:36.073906 7fd7477fe6c0 Delete type=2 #45
|
||||||
|
2026/05/02-10:55:36.083653 7fd7477fe6c0 Manual compaction at level-0 from '!actors.items!zCAO1NzNTX1SCdQ8' @ 456 : 0 .. '!actors.items!vZXf6vfOj986NEYr' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000026
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
2026/05/02-22:17:12.519161 7fd755fef6c0 Recovering log #23
|
||||||
|
2026/05/02-22:17:12.529405 7fd755fef6c0 Delete type=3 #21
|
||||||
|
2026/05/02-22:17:12.529482 7fd755fef6c0 Delete type=0 #23
|
||||||
|
2026/05/02-23:14:30.629959 7fd7477fe6c0 Level-0 table #29: started
|
||||||
|
2026/05/02-23:14:30.639557 7fd7477fe6c0 Level-0 table #29: 24275 bytes OK
|
||||||
|
2026/05/02-23:14:30.650756 7fd7477fe6c0 Delete type=0 #27
|
||||||
|
2026/05/02-23:14:30.662871 7fd7477fe6c0 Manual compaction at level-0 from '!actors!DemonCombatMaj001' @ 72057594037927935 : 1 .. '!actors.items!ip5rWxXRlXaEyQD6' @ 0 : 0; will stop at (end)
|
||||||
|
2026/05/02-23:14:30.662925 7fd7477fe6c0 Manual compaction at level-1 from '!actors!DemonCombatMaj001' @ 72057594037927935 : 1 .. '!actors.items!ip5rWxXRlXaEyQD6' @ 0 : 0; will stop at '!actors.items!ip5rWxXRlXaEyQD6' @ 366 : 1
|
||||||
|
2026/05/02-23:14:30.662935 7fd7477fe6c0 Compacting 1@1 + 1@2 files
|
||||||
|
2026/05/02-23:14:30.671179 7fd7477fe6c0 Generated table #30@1: 99 keys, 34643 bytes
|
||||||
|
2026/05/02-23:14:30.671218 7fd7477fe6c0 Compacted 1@1 + 1@2 files => 34643 bytes
|
||||||
|
2026/05/02-23:14:30.682390 7fd7477fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
|
||||||
|
2026/05/02-23:14:30.682554 7fd7477fe6c0 Delete type=2 #25
|
||||||
|
2026/05/02-23:14:30.682716 7fd7477fe6c0 Delete type=2 #29
|
||||||
|
2026/05/02-23:14:30.731391 7fd7477fe6c0 Manual compaction at level-1 from '!actors.items!ip5rWxXRlXaEyQD6' @ 366 : 1 .. '!actors.items!ip5rWxXRlXaEyQD6' @ 0 : 0; will stop at (end)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
2026/05/02-10:42:39.878619 7fd7557ee6c0 Recovering log #18
|
||||||
|
2026/05/02-10:42:39.888389 7fd7557ee6c0 Delete type=3 #16
|
||||||
|
2026/05/02-10:42:39.888487 7fd7557ee6c0 Delete type=0 #18
|
||||||
|
2026/05/02-10:55:36.007797 7fd7477fe6c0 Level-0 table #24: started
|
||||||
|
2026/05/02-10:55:36.011680 7fd7477fe6c0 Level-0 table #24: 24272 bytes OK
|
||||||
|
2026/05/02-10:55:36.018592 7fd7477fe6c0 Delete type=0 #22
|
||||||
|
2026/05/02-10:55:36.025623 7fd7477fe6c0 Manual compaction at level-0 from '!actors!DemonCombatMaj001' @ 72057594037927935 : 1 .. '!actors.items!GqtMRrjUTucZj9cj' @ 0 : 0; will stop at (end)
|
||||||
|
2026/05/02-10:55:36.025666 7fd7477fe6c0 Manual compaction at level-1 from '!actors!DemonCombatMaj001' @ 72057594037927935 : 1 .. '!actors.items!GqtMRrjUTucZj9cj' @ 0 : 0; will stop at '!actors.items!gPJSnL0lXZ0x3Uvt' @ 300 : 0
|
||||||
|
2026/05/02-10:55:36.025679 7fd7477fe6c0 Compacting 1@1 + 1@2 files
|
||||||
|
2026/05/02-10:55:36.030173 7fd7477fe6c0 Generated table #25@1: 99 keys, 34641 bytes
|
||||||
|
2026/05/02-10:55:36.030197 7fd7477fe6c0 Compacted 1@1 + 1@2 files => 34641 bytes
|
||||||
|
2026/05/02-10:55:36.035952 7fd7477fe6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
|
||||||
|
2026/05/02-10:55:36.036040 7fd7477fe6c0 Delete type=2 #20
|
||||||
|
2026/05/02-10:55:36.036148 7fd7477fe6c0 Delete type=2 #24
|
||||||
|
2026/05/02-10:55:36.063204 7fd7477fe6c0 Manual compaction at level-1 from '!actors.items!gPJSnL0lXZ0x3Uvt' @ 300 : 0 .. '!actors.items!GqtMRrjUTucZj9cj' @ 0 : 0; will stop at (end)
|
||||||
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
|||||||
MANIFEST-000367
|
MANIFEST-000395
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:23.899083 7fd755fef6c0 Recovering log #365
|
2026/05/02-22:17:12.656044 7fd754fed6c0 Recovering log #393
|
||||||
2026/05/01-23:51:23.963281 7fd755fef6c0 Delete type=3 #363
|
2026/05/02-22:17:12.666131 7fd754fed6c0 Delete type=3 #391
|
||||||
2026/05/01-23:51:23.963367 7fd755fef6c0 Delete type=0 #365
|
2026/05/02-22:17:12.666231 7fd754fed6c0 Delete type=0 #393
|
||||||
2026/05/02-08:26:03.439764 7fd7477fe6c0 Level-0 table #370: started
|
2026/05/02-23:14:30.872981 7fd7477fe6c0 Level-0 table #398: started
|
||||||
2026/05/02-08:26:03.439860 7fd7477fe6c0 Level-0 table #370: 0 bytes OK
|
2026/05/02-23:14:30.873018 7fd7477fe6c0 Level-0 table #398: 0 bytes OK
|
||||||
2026/05/02-08:26:03.476944 7fd7477fe6c0 Delete type=0 #368
|
2026/05/02-23:14:30.884443 7fd7477fe6c0 Delete type=0 #396
|
||||||
2026/05/02-08:26:03.477143 7fd7477fe6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.905834 7fd7477fe6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.887738 7fd755fef6c0 Recovering log #361
|
2026/05/02-10:42:40.016911 7fd7557ee6c0 Recovering log #389
|
||||||
2026/05/01-23:45:25.897606 7fd755fef6c0 Delete type=3 #359
|
2026/05/02-10:42:40.026916 7fd7557ee6c0 Delete type=3 #387
|
||||||
2026/05/01-23:45:25.897664 7fd755fef6c0 Delete type=0 #361
|
2026/05/02-10:42:40.026980 7fd7557ee6c0 Delete type=0 #389
|
||||||
2026/05/01-23:51:18.003483 7fd7477fe6c0 Level-0 table #366: started
|
2026/05/02-10:55:36.154029 7fd7477fe6c0 Level-0 table #394: started
|
||||||
2026/05/01-23:51:18.003518 7fd7477fe6c0 Level-0 table #366: 0 bytes OK
|
2026/05/02-10:55:36.154054 7fd7477fe6c0 Level-0 table #394: 0 bytes OK
|
||||||
2026/05/01-23:51:18.037812 7fd7477fe6c0 Delete type=0 #364
|
2026/05/02-10:55:36.161757 7fd7477fe6c0 Delete type=0 #392
|
||||||
2026/05/01-23:51:18.167176 7fd7477fe6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.175161 7fd7477fe6c0 Manual compaction at level-0 from '!items!5dGXNiL3WN4cAk7X' @ 72057594037927935 : 1 .. '!items!zzz9JrtWjELdoAfK' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000367
|
MANIFEST-000395
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:23.839110 7fd754fed6c0 Recovering log #365
|
2026/05/02-22:17:12.642987 7fd747fff6c0 Recovering log #393
|
||||||
2026/05/01-23:51:23.896151 7fd754fed6c0 Delete type=3 #363
|
2026/05/02-22:17:12.653769 7fd747fff6c0 Delete type=3 #391
|
||||||
2026/05/01-23:51:23.896228 7fd754fed6c0 Delete type=0 #365
|
2026/05/02-22:17:12.653830 7fd747fff6c0 Delete type=0 #393
|
||||||
2026/05/02-08:26:03.403387 7fd7477fe6c0 Level-0 table #370: started
|
2026/05/02-23:14:30.884592 7fd7477fe6c0 Level-0 table #398: started
|
||||||
2026/05/02-08:26:03.403427 7fd7477fe6c0 Level-0 table #370: 0 bytes OK
|
2026/05/02-23:14:30.884621 7fd7477fe6c0 Level-0 table #398: 0 bytes OK
|
||||||
2026/05/02-08:26:03.439575 7fd7477fe6c0 Delete type=0 #368
|
2026/05/02-23:14:30.895025 7fd7477fe6c0 Delete type=0 #396
|
||||||
2026/05/02-08:26:03.477128 7fd7477fe6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.905852 7fd7477fe6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.874605 7fd754fed6c0 Recovering log #361
|
2026/05/02-10:42:40.003066 7fd755fef6c0 Recovering log #389
|
||||||
2026/05/01-23:45:25.884831 7fd754fed6c0 Delete type=3 #359
|
2026/05/02-10:42:40.013617 7fd755fef6c0 Delete type=3 #387
|
||||||
2026/05/01-23:45:25.884885 7fd754fed6c0 Delete type=0 #361
|
2026/05/02-10:42:40.013702 7fd755fef6c0 Delete type=0 #389
|
||||||
2026/05/01-23:51:17.956955 7fd7477fe6c0 Level-0 table #366: started
|
2026/05/02-10:55:36.147664 7fd7477fe6c0 Level-0 table #394: started
|
||||||
2026/05/01-23:51:17.956995 7fd7477fe6c0 Level-0 table #366: 0 bytes OK
|
2026/05/02-10:55:36.147700 7fd7477fe6c0 Level-0 table #394: 0 bytes OK
|
||||||
2026/05/01-23:51:18.002857 7fd7477fe6c0 Delete type=0 #364
|
2026/05/02-10:55:36.153904 7fd7477fe6c0 Delete type=0 #392
|
||||||
2026/05/01-23:51:18.003348 7fd7477fe6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.168246 7fd7477fe6c0 Manual compaction at level-0 from '!items!1cZd2hlTV9tykDED' @ 72057594037927935 : 1 .. '!items!y47dBO3Mf5Pn7tOd' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000367
|
MANIFEST-000395
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:24.071860 7fd755fef6c0 Recovering log #365
|
2026/05/02-22:17:12.696255 7fd747fff6c0 Recovering log #393
|
||||||
2026/05/01-23:51:24.119019 7fd755fef6c0 Delete type=3 #363
|
2026/05/02-22:17:12.706814 7fd747fff6c0 Delete type=3 #391
|
||||||
2026/05/01-23:51:24.119097 7fd755fef6c0 Delete type=0 #365
|
2026/05/02-22:17:12.706879 7fd747fff6c0 Delete type=0 #393
|
||||||
2026/05/02-08:26:03.577938 7fd7477fe6c0 Level-0 table #370: started
|
2026/05/02-23:14:30.921060 7fd7477fe6c0 Level-0 table #398: started
|
||||||
2026/05/02-08:26:03.577975 7fd7477fe6c0 Level-0 table #370: 0 bytes OK
|
2026/05/02-23:14:30.921121 7fd7477fe6c0 Level-0 table #398: 0 bytes OK
|
||||||
2026/05/02-08:26:03.614658 7fd7477fe6c0 Delete type=0 #368
|
2026/05/02-23:14:30.931783 7fd7477fe6c0 Delete type=0 #396
|
||||||
2026/05/02-08:26:03.684819 7fd7477fe6c0 Manual compaction at level-0 from '!items!2GaJZsqr2c2mcDRv' @ 72057594037927935 : 1 .. '!items!ui4JGsGwHNlSXVK3' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.954777 7fd7477fe6c0 Manual compaction at level-0 from '!items!2GaJZsqr2c2mcDRv' @ 72057594037927935 : 1 .. '!items!ui4JGsGwHNlSXVK3' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.928160 7fd7557ee6c0 Recovering log #361
|
2026/05/02-10:42:40.055555 7fd747fff6c0 Recovering log #389
|
||||||
2026/05/01-23:45:25.938724 7fd7557ee6c0 Delete type=3 #359
|
2026/05/02-10:42:40.065759 7fd747fff6c0 Delete type=3 #387
|
||||||
2026/05/01-23:45:25.938783 7fd7557ee6c0 Delete type=0 #361
|
2026/05/02-10:42:40.065822 7fd747fff6c0 Delete type=0 #389
|
||||||
2026/05/01-23:51:18.037971 7fd7477fe6c0 Level-0 table #366: started
|
2026/05/02-10:55:36.175186 7fd7477fe6c0 Level-0 table #394: started
|
||||||
2026/05/01-23:51:18.038026 7fd7477fe6c0 Level-0 table #366: 0 bytes OK
|
2026/05/02-10:55:36.175225 7fd7477fe6c0 Level-0 table #394: 0 bytes OK
|
||||||
2026/05/01-23:51:18.080237 7fd7477fe6c0 Delete type=0 #364
|
2026/05/02-10:55:36.182810 7fd7477fe6c0 Delete type=0 #392
|
||||||
2026/05/01-23:51:18.167195 7fd7477fe6c0 Manual compaction at level-0 from '!items!2GaJZsqr2c2mcDRv' @ 72057594037927935 : 1 .. '!items!ui4JGsGwHNlSXVK3' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.196140 7fd7477fe6c0 Manual compaction at level-0 from '!items!2GaJZsqr2c2mcDRv' @ 72057594037927935 : 1 .. '!items!ui4JGsGwHNlSXVK3' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000027
|
MANIFEST-000055
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
2026/05/01-23:51:24.450092 7fd754fed6c0 Recovering log #25
|
2026/05/02-22:17:12.787407 7fd754fed6c0 Recovering log #53
|
||||||
2026/05/01-23:51:24.512021 7fd754fed6c0 Delete type=3 #23
|
2026/05/02-22:17:12.798064 7fd754fed6c0 Delete type=3 #51
|
||||||
2026/05/01-23:51:24.512123 7fd754fed6c0 Delete type=0 #25
|
2026/05/02-22:17:12.798132 7fd754fed6c0 Delete type=0 #53
|
||||||
2026/05/02-08:26:03.753438 7fd7477fe6c0 Level-0 table #30: started
|
2026/05/02-23:14:30.999818 7fd7477fe6c0 Level-0 table #58: started
|
||||||
2026/05/02-08:26:03.753526 7fd7477fe6c0 Level-0 table #30: 0 bytes OK
|
2026/05/02-23:14:30.999850 7fd7477fe6c0 Level-0 table #58: 0 bytes OK
|
||||||
2026/05/02-08:26:03.797061 7fd7477fe6c0 Delete type=0 #28
|
2026/05/02-23:14:31.012433 7fd7477fe6c0 Delete type=0 #56
|
||||||
2026/05/02-08:26:03.865053 7fd7477fe6c0 Manual compaction at level-0 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:31.041770 7fd7477fe6c0 Manual compaction at level-0 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
||||||
2026/05/02-08:26:03.911157 7fd7477fe6c0 Manual compaction at level-1 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:31.054104 7fd7477fe6c0 Manual compaction at level-1 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
2026/05/01-23:45:26.025091 7fd747fff6c0 Recovering log #21
|
2026/05/02-10:42:40.145436 7fd754fed6c0 Recovering log #49
|
||||||
2026/05/01-23:45:26.035419 7fd747fff6c0 Delete type=3 #19
|
2026/05/02-10:42:40.155668 7fd754fed6c0 Delete type=3 #47
|
||||||
2026/05/01-23:45:26.035499 7fd747fff6c0 Delete type=0 #21
|
2026/05/02-10:42:40.155737 7fd754fed6c0 Delete type=0 #49
|
||||||
2026/05/01-23:51:18.423732 7fd7477fe6c0 Level-0 table #26: started
|
2026/05/02-10:55:36.221587 7fd7477fe6c0 Level-0 table #54: started
|
||||||
2026/05/01-23:51:18.423792 7fd7477fe6c0 Level-0 table #26: 0 bytes OK
|
2026/05/02-10:55:36.221615 7fd7477fe6c0 Level-0 table #54: 0 bytes OK
|
||||||
2026/05/01-23:51:18.458393 7fd7477fe6c0 Delete type=0 #24
|
2026/05/02-10:55:36.228497 7fd7477fe6c0 Delete type=0 #52
|
||||||
2026/05/01-23:51:18.501042 7fd7477fe6c0 Manual compaction at level-0 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.234981 7fd7477fe6c0 Manual compaction at level-0 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
||||||
2026/05/01-23:51:18.535320 7fd7477fe6c0 Manual compaction at level-1 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.253890 7fd7477fe6c0 Manual compaction at level-1 from '!journal!JurnlHelpGuide01' @ 72057594037927935 : 1 .. '!journal.pages!JurnlHelpGuide01.JHelpPage0000008' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000367
|
MANIFEST-000395
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:24.121693 7fd747fff6c0 Recovering log #365
|
2026/05/02-22:17:12.708790 7fd7557ee6c0 Recovering log #393
|
||||||
2026/05/01-23:51:24.175522 7fd747fff6c0 Delete type=3 #363
|
2026/05/02-22:17:12.719560 7fd7557ee6c0 Delete type=3 #391
|
||||||
2026/05/01-23:51:24.175606 7fd747fff6c0 Delete type=0 #365
|
2026/05/02-22:17:12.719616 7fd7557ee6c0 Delete type=0 #393
|
||||||
2026/05/02-08:26:03.543492 7fd7477fe6c0 Level-0 table #370: started
|
2026/05/02-23:14:30.931940 7fd7477fe6c0 Level-0 table #398: started
|
||||||
2026/05/02-08:26:03.543537 7fd7477fe6c0 Level-0 table #370: 0 bytes OK
|
2026/05/02-23:14:30.931974 7fd7477fe6c0 Level-0 table #398: 0 bytes OK
|
||||||
2026/05/02-08:26:03.577774 7fd7477fe6c0 Delete type=0 #368
|
2026/05/02-23:14:30.943441 7fd7477fe6c0 Delete type=0 #396
|
||||||
2026/05/02-08:26:03.614845 7fd7477fe6c0 Manual compaction at level-0 from '!items!09s33sFuju8zjPqI' @ 72057594037927935 : 1 .. '!items!xlyFCQClBZ1N3O1B' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.954807 7fd7477fe6c0 Manual compaction at level-0 from '!items!09s33sFuju8zjPqI' @ 72057594037927935 : 1 .. '!items!xlyFCQClBZ1N3O1B' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.941264 7fd755fef6c0 Recovering log #361
|
2026/05/02-10:42:40.067527 7fd7557ee6c0 Recovering log #389
|
||||||
2026/05/01-23:45:25.951481 7fd755fef6c0 Delete type=3 #359
|
2026/05/02-10:42:40.078178 7fd7557ee6c0 Delete type=3 #387
|
||||||
2026/05/01-23:45:25.951534 7fd755fef6c0 Delete type=0 #361
|
2026/05/02-10:42:40.078243 7fd7557ee6c0 Delete type=0 #389
|
||||||
2026/05/01-23:51:18.167311 7fd7477fe6c0 Level-0 table #366: started
|
2026/05/02-10:55:36.182940 7fd7477fe6c0 Level-0 table #394: started
|
||||||
2026/05/01-23:51:18.167365 7fd7477fe6c0 Level-0 table #366: 0 bytes OK
|
2026/05/02-10:55:36.182962 7fd7477fe6c0 Level-0 table #394: 0 bytes OK
|
||||||
2026/05/01-23:51:18.213184 7fd7477fe6c0 Delete type=0 #364
|
2026/05/02-10:55:36.189078 7fd7477fe6c0 Delete type=0 #392
|
||||||
2026/05/01-23:51:18.334580 7fd7477fe6c0 Manual compaction at level-0 from '!items!09s33sFuju8zjPqI' @ 72057594037927935 : 1 .. '!items!xlyFCQClBZ1N3O1B' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.202390 7fd7477fe6c0 Manual compaction at level-0 from '!items!09s33sFuju8zjPqI' @ 72057594037927935 : 1 .. '!items!xlyFCQClBZ1N3O1B' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000367
|
MANIFEST-000395
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:24.015685 7fd754fed6c0 Recovering log #365
|
2026/05/02-22:17:12.682739 7fd754fed6c0 Recovering log #393
|
||||||
2026/05/01-23:51:24.068740 7fd754fed6c0 Delete type=3 #363
|
2026/05/02-22:17:12.693951 7fd754fed6c0 Delete type=3 #391
|
||||||
2026/05/01-23:51:24.068836 7fd754fed6c0 Delete type=0 #365
|
2026/05/02-22:17:12.694015 7fd754fed6c0 Delete type=0 #393
|
||||||
2026/05/02-08:26:03.477243 7fd7477fe6c0 Level-0 table #370: started
|
2026/05/02-23:14:30.905870 7fd7477fe6c0 Level-0 table #398: started
|
||||||
2026/05/02-08:26:03.477285 7fd7477fe6c0 Level-0 table #370: 0 bytes OK
|
2026/05/02-23:14:30.905915 7fd7477fe6c0 Level-0 table #398: 0 bytes OK
|
||||||
2026/05/02-08:26:03.516015 7fd7477fe6c0 Delete type=0 #368
|
2026/05/02-23:14:30.920801 7fd7477fe6c0 Delete type=0 #396
|
||||||
2026/05/02-08:26:03.614814 7fd7477fe6c0 Manual compaction at level-0 from '!items!2t1KmBeQNuKK5qlN' @ 72057594037927935 : 1 .. '!items!yBvkQb9S64s908sR' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.943625 7fd7477fe6c0 Manual compaction at level-0 from '!items!2t1KmBeQNuKK5qlN' @ 72057594037927935 : 1 .. '!items!yBvkQb9S64s908sR' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.914723 7fd755fef6c0 Recovering log #361
|
2026/05/02-10:42:40.043275 7fd7557ee6c0 Recovering log #389
|
||||||
2026/05/01-23:45:25.925030 7fd755fef6c0 Delete type=3 #359
|
2026/05/02-10:42:40.053566 7fd7557ee6c0 Delete type=3 #387
|
||||||
2026/05/01-23:45:25.925095 7fd755fef6c0 Delete type=0 #361
|
2026/05/02-10:42:40.053647 7fd7557ee6c0 Delete type=0 #389
|
||||||
2026/05/01-23:51:18.080386 7fd7477fe6c0 Level-0 table #366: started
|
2026/05/02-10:55:36.161945 7fd7477fe6c0 Level-0 table #394: started
|
||||||
2026/05/01-23:51:18.080421 7fd7477fe6c0 Level-0 table #366: 0 bytes OK
|
2026/05/02-10:55:36.161989 7fd7477fe6c0 Level-0 table #394: 0 bytes OK
|
||||||
2026/05/01-23:51:18.124527 7fd7477fe6c0 Delete type=0 #364
|
2026/05/02-10:55:36.168112 7fd7477fe6c0 Delete type=0 #392
|
||||||
2026/05/01-23:51:18.167208 7fd7477fe6c0 Manual compaction at level-0 from '!items!2t1KmBeQNuKK5qlN' @ 72057594037927935 : 1 .. '!items!yBvkQb9S64s908sR' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.175175 7fd7477fe6c0 Manual compaction at level-0 from '!items!2t1KmBeQNuKK5qlN' @ 72057594037927935 : 1 .. '!items!yBvkQb9S64s908sR' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000045
|
MANIFEST-000073
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:23.965723 7fd7557ee6c0 Recovering log #43
|
2026/05/02-22:17:12.669289 7fd755fef6c0 Recovering log #71
|
||||||
2026/05/01-23:51:24.013270 7fd7557ee6c0 Delete type=3 #41
|
2026/05/02-22:17:12.680179 7fd755fef6c0 Delete type=3 #69
|
||||||
2026/05/01-23:51:24.013359 7fd7557ee6c0 Delete type=0 #43
|
2026/05/02-22:17:12.680245 7fd755fef6c0 Delete type=0 #71
|
||||||
2026/05/02-08:26:03.516163 7fd7477fe6c0 Level-0 table #48: started
|
2026/05/02-23:14:30.895178 7fd7477fe6c0 Level-0 table #76: started
|
||||||
2026/05/02-08:26:03.516201 7fd7477fe6c0 Level-0 table #48: 0 bytes OK
|
2026/05/02-23:14:30.895205 7fd7477fe6c0 Level-0 table #76: 0 bytes OK
|
||||||
2026/05/02-08:26:03.543330 7fd7477fe6c0 Delete type=0 #46
|
2026/05/02-23:14:30.905579 7fd7477fe6c0 Delete type=0 #74
|
||||||
2026/05/02-08:26:03.614833 7fd7477fe6c0 Manual compaction at level-0 from '!items!7KKX5anO8rJX492Y' @ 72057594037927935 : 1 .. '!items!veZcaW70wCVk99R7' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.921033 7fd7477fe6c0 Manual compaction at level-0 from '!items!7KKX5anO8rJX492Y' @ 72057594037927935 : 1 .. '!items!veZcaW70wCVk99R7' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.901174 7fd754fed6c0 Recovering log #39
|
2026/05/02-10:42:40.029411 7fd755fef6c0 Recovering log #67
|
||||||
2026/05/01-23:45:25.912205 7fd754fed6c0 Delete type=3 #37
|
2026/05/02-10:42:40.040864 7fd755fef6c0 Delete type=3 #65
|
||||||
2026/05/01-23:45:25.912263 7fd754fed6c0 Delete type=0 #39
|
2026/05/02-10:42:40.040925 7fd755fef6c0 Delete type=0 #67
|
||||||
2026/05/01-23:51:18.124721 7fd7477fe6c0 Level-0 table #44: started
|
2026/05/02-10:55:36.168263 7fd7477fe6c0 Level-0 table #72: started
|
||||||
2026/05/01-23:51:18.124762 7fd7477fe6c0 Level-0 table #44: 0 bytes OK
|
2026/05/02-10:55:36.168296 7fd7477fe6c0 Level-0 table #72: 0 bytes OK
|
||||||
2026/05/01-23:51:18.167031 7fd7477fe6c0 Delete type=0 #42
|
2026/05/02-10:55:36.175025 7fd7477fe6c0 Delete type=0 #70
|
||||||
2026/05/01-23:51:18.167219 7fd7477fe6c0 Manual compaction at level-0 from '!items!7KKX5anO8rJX492Y' @ 72057594037927935 : 1 .. '!items!veZcaW70wCVk99R7' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.182928 7fd7477fe6c0 Manual compaction at level-0 from '!items!7KKX5anO8rJX492Y' @ 72057594037927935 : 1 .. '!items!veZcaW70wCVk99R7' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000263
|
MANIFEST-000292
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:51:23.352194 7fd7557ee6c0 Recovering log #261
|
2026/05/02-22:17:12.499898 7fd7557ee6c0 Recovering log #290
|
||||||
2026/05/01-23:51:23.408632 7fd7557ee6c0 Delete type=3 #259
|
2026/05/02-22:17:12.510769 7fd7557ee6c0 Delete type=3 #288
|
||||||
2026/05/01-23:51:23.408711 7fd7557ee6c0 Delete type=0 #261
|
2026/05/02-22:17:12.510837 7fd7557ee6c0 Delete type=0 #290
|
||||||
2026/05/02-08:26:02.971512 7fd7477fe6c0 Level-0 table #266: started
|
2026/05/02-23:14:30.650934 7fd7477fe6c0 Level-0 table #295: started
|
||||||
2026/05/02-08:26:02.975617 7fd7477fe6c0 Level-0 table #266: 0 bytes OK
|
2026/05/02-23:14:30.650972 7fd7477fe6c0 Level-0 table #295: 0 bytes OK
|
||||||
2026/05/02-08:26:03.013303 7fd7477fe6c0 Delete type=0 #264
|
2026/05/02-23:14:30.662662 7fd7477fe6c0 Delete type=0 #293
|
||||||
2026/05/02-08:26:03.143866 7fd7477fe6c0 Manual compaction at level-0 from '!actors!00CKDCqVh5fLZbYo' @ 72057594037927935 : 1 .. '!folders!dwT9WnH0ZnpuZh92' @ 0 : 0; will stop at (end)
|
2026/05/02-23:14:30.662885 7fd7477fe6c0 Manual compaction at level-0 from '!actors!00CKDCqVh5fLZbYo' @ 72057594037927935 : 1 .. '!folders!dwT9WnH0ZnpuZh92' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
2026/05/01-23:45:25.784183 7fd754fed6c0 Recovering log #257
|
2026/05/02-10:42:39.863251 7fd754fed6c0 Recovering log #286
|
||||||
2026/05/01-23:45:25.793965 7fd754fed6c0 Delete type=3 #255
|
2026/05/02-10:42:39.874170 7fd754fed6c0 Delete type=3 #284
|
||||||
2026/05/01-23:45:25.794022 7fd754fed6c0 Delete type=0 #257
|
2026/05/02-10:42:39.874230 7fd754fed6c0 Delete type=0 #286
|
||||||
2026/05/01-23:51:17.545136 7fd7477fe6c0 Level-0 table #262: started
|
2026/05/02-10:55:36.018781 7fd7477fe6c0 Level-0 table #291: started
|
||||||
2026/05/01-23:51:17.545243 7fd7477fe6c0 Level-0 table #262: 0 bytes OK
|
2026/05/02-10:55:36.018828 7fd7477fe6c0 Level-0 table #291: 0 bytes OK
|
||||||
2026/05/01-23:51:17.585589 7fd7477fe6c0 Delete type=0 #260
|
2026/05/02-10:55:36.025349 7fd7477fe6c0 Delete type=0 #289
|
||||||
2026/05/01-23:51:17.709862 7fd7477fe6c0 Manual compaction at level-0 from '!actors!00CKDCqVh5fLZbYo' @ 72057594037927935 : 1 .. '!folders!dwT9WnH0ZnpuZh92' @ 0 : 0; will stop at (end)
|
2026/05/02-10:55:36.025635 7fd7477fe6c0 Manual compaction at level-0 from '!actors!00CKDCqVh5fLZbYo' @ 72057594037927935 : 1 .. '!folders!dwT9WnH0ZnpuZh92' @ 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
Reference in New Issue
Block a user