Compare commits

...

37 Commits

Author SHA1 Message Date
1c7cf343b1 Update arts obscurs 2025-08-12 23:24:50 +02:00
d4b00e3508 Update arts obscurs 2025-08-12 23:20:51 +02:00
adc912e6cd Ajout/upgrade des arts obscurs 2025-08-11 22:53:23 +02:00
51a457ebf6 Foundry v13 migration 2025-05-02 08:34:22 +02:00
2e9c558027 Ajout arts obscurs 2024-11-17 22:46:52 +01:00
bcd0758328 Ajout arts obscurs 2024-11-17 22:45:48 +01:00
2b680a203f Fix jet avec prestance 2024-08-11 14:40:41 +02:00
e3d7874dce Fix masque/demaque + pouvoir passif avec point 2024-07-30 13:45:50 +02:00
ab6a5832c0 Various v12 fixes 2024-07-13 12:13:06 +02:00
d83a999974 Various v12 fixes 2024-07-13 12:12:38 +02:00
b83890a764 Add v12 support 2024-05-23 15:38:50 +02:00
5ad3c165e5 Add v12 support 2024-05-23 11:25:51 +02:00
2b3e774cbb Correction sur les pouvoirs des fiches de PNJ 2024-04-25 10:30:15 +02:00
96f8d2bceb Fix roll pouvoir + pouvoirs passifs 2024-04-11 12:36:50 +02:00
e288c90ee4 Bouton d'ajout d'items dans la fiche PNJ 2024-04-01 17:27:20 +02:00
8916de8613 Bouton d'ajout d'items dans la fiche 2024-03-31 17:53:56 +02:00
8598df5a57 Ajout de la gestion des Points d'usage 2024-03-23 11:37:15 +01:00
8781462c8d Correction sur competences à 0 + diverses ameliorations 2024-03-06 19:00:01 +01:00
8c38aead3e Correction sur competences à 0 + diverses ameliorations 2024-03-06 18:46:53 +01:00
67bf71e6c0 Fix competences 2024-03-01 13:39:32 +01:00
63d15e82bb Enhance stats 2024-02-08 12:56:25 +01:00
62c3787cea Affichage des specialisations 2023-12-19 22:13:26 +01:00
df61abac19 Gestion des heritages 2023-12-19 21:35:20 +01:00
a7d1a14c52 Fix css for compendiums 2023-05-29 17:00:57 +02:00
b0dc6f36e4 Fix css for compendiums 2023-05-29 16:33:08 +02:00
5109d2aa91 Modification CSS pour compendiums 2023-05-29 13:22:43 +02:00
51c162ecbb v10/v11 compat 2023-05-25 15:43:55 +02:00
44d02b0cd1 Fix sur pouvoirs, heritage et 2 pts de tricherie 2023-05-01 18:48:34 +02:00
9b1600304a Fix sur pouvoirs, heritage et 2 pts de tricherie 2023-04-30 20:08:29 +02:00
2dff59c829 Fix pv value pour PNJ 2023-04-24 15:59:07 +02:00
55a2a8e3c3 Fix malus 2023-04-23 23:13:31 +02:00
2da1f56a91 Ajout XP + rework fees 2023-04-23 17:13:08 +02:00
66bd9dd2c8 Ajout XP + rework fees 2023-04-23 17:12:43 +02:00
15427f3747 Gestion des attaques ciblées 2023-04-11 13:29:04 +02:00
577eccbbd3 Gestion des attaques ciblées 2023-04-11 13:26:51 +02:00
05026d454b Add attaque dos+plusieurs 2023-04-10 14:11:47 +02:00
6497369d7f Gestion assommer/charge 2023-04-09 21:45:46 +02:00
125 changed files with 5131 additions and 2602 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.history/

View File

@@ -1,8 +1,8 @@
# Système Foundry pour Hawkmoon (French RPG, Titam France/Sombres Projets)
# Système Foundry pour Les Héritiers (French RPG, Titam France/Sombres Projets)
## EN
Unofficial system for Hawkmoon (French version from Titam France).
Unofficial system for Les Heritiers (from Titam France).
This system has been approved by Département des Sombres Projets ( http://www.titam-france.fr/ ), thanks !
@@ -10,7 +10,7 @@ Books are mandatory to play and are available at : http://www.titam-france.fr
## FR
Système non-officiel pour le JDR Hawkmoon (Titam France/Sombres Projets).
Système non-officiel pour le JDR Les Héritiers (Titam France/Sombres Projets).
Ce système a été autorisé par le Département des Sombres Projets ( http://www.titam-france.fr/ ), merci à eux !
@@ -18,7 +18,7 @@ Les livres du jeu sont nécessaires pour jouer, et sont disponibles ici : http:/
# Credits
Hawkmoon, le jeu de rôle du Troisième Millénaire, is a property of Titam France/Sombres Projets.
Les Héritiers, is a property of Titam France/Sombres Projets.
# Developmement
@@ -26,4 +26,4 @@ LeRatierBretonnien
# Tests, icones et saisie des données
Prêtre, Blondin, Zechrub/Chris, Kyllian, Lightbringer
Prêtre, Carter

BIN
assets/icons/sort.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -1,30 +1,24 @@
{
"ACTOR": {
"TypePersonnage": "Personnage",
"TypePNJ": "PNJ"
},
"ITEM": {
"TypeArtefact": "Artefact",
"TypeArme": "Arme",
"TypeTalent": "Talent",
"TypeHistorique": "Historique",
"TypeProfil": "Profil",
"TypeCompetence": "Compétence",
"TypeProtection": "Protection",
"TypeMonnaie": "Monnaie",
"TypeEquipement": "Equipement",
"TypeRessource": "Ressource",
"TypeContact": "Contact"
},
"HAWKMOON": {
"ui": {
"editContact": "Modifier le contact",
"deleteContact": "Supprimer le contact",
"editTrait": "Modifier le trait",
"deleteTrait": "Supprimer le trait"
"TYPES": {
"Actor": {
"personnage": "Personnage",
"pnj": "PNJ"
},
"Item": {
"accessoire": "Accessoire",
"arme": "Arme",
"atoutfeerique": "Atout féerique",
"avantage": "Avantage",
"capacitenaturelle": "Capacité naturelle",
"competence": "Compétence",
"contact": "Contact",
"desavantage": "Désavantage",
"equipement": "Equipement",
"fee": "e",
"pouvoir": "Pouvoir",
"profil": "Profil",
"protection": "Protection",
"sort": "Sort"
}
}
}

View File

@@ -12,7 +12,7 @@ export class HeritiersActorPNJSheet extends HeritiersActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-les-heritiers", "sheet", "actor"],
template: "systems/fvtt-les-heritiers/templates/actor-pnj-sheet.html",
width: 780,

View File

@@ -6,12 +6,12 @@
import { HeritiersUtility } from "./heritiers-utility.js";
/* -------------------------------------------- */
export class HeritiersActorSheet extends ActorSheet {
export class HeritiersActorSheet extends foundry.appv1.sheets.ActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-les-heritiers", "sheet", "actor"],
template: "systems/fvtt-les-heritiers/templates/actor-sheet.html",
width: 780,
@@ -24,7 +24,7 @@ export class HeritiersActorSheet extends ActorSheet {
/* -------------------------------------------- */
async getData() {
const objectData = duplicate(this.object)
const objectData = foundry.utils.duplicate(this.object)
let formData = {
title: this.title,
@@ -38,30 +38,33 @@ export class HeritiersActorSheet extends ActorSheet {
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
limited: this.object.limited,
skills: this.actor.getSkills(),
utileSkillsMental :this.actor.organizeUtileSkills("mental"),
utileSkillsPhysical :this.actor.organizeUtileSkills("physical"),
futileSkills :this.actor.organizeFutileSkills(),
utileSkillsMental: this.actor.organizeUtileSkills("mental"),
utileSkillsPhysical: this.actor.organizeUtileSkills("physical"),
competencesMagie: HeritiersUtility.getCompetencesMagie(),
futileSkills: this.actor.organizeFutileSkills(),
contacts: this.actor.organizeContacts(),
armes: duplicate(this.actor.getWeapons()),
monnaies: duplicate(this.actor.getMonnaies()),
pouvoirs: duplicate(this.actor.getPouvoirs()),
fee: duplicate(this.actor.getFee() || {} ),
protections: duplicate(this.actor.getArmors()),
combat: this.actor.getCombatValues(),
equipements: duplicate(this.actor.getEquipments()),
avantages: duplicate(this.actor.getAvantages()),
atouts: duplicate(this.actor.getAtouts()),
capacites: duplicate(this.actor.getCapacites()),
desavantages: duplicate(this.actor.getDesavantages()),
profils: duplicate(this.actor.getProfils()),
pvMalus: this.actor.getPvMalus(),
armes: foundry.utils.duplicate(this.actor.getWeapons()),
monnaies: foundry.utils.duplicate(this.actor.getMonnaies()),
pouvoirs: foundry.utils.duplicate(this.actor.getPouvoirs()),
fee: foundry.utils.duplicate(this.actor.getFee() || {}),
protections: foundry.utils.duplicate(this.actor.getArmors()),
combat: this.actor.getCombatValues(),
equipements: foundry.utils.duplicate(this.actor.getEquipments()),
avantages: foundry.utils.duplicate(this.actor.getAvantages()),
atouts: foundry.utils.duplicate(this.actor.getAtouts()),
capacites: foundry.utils.duplicate(this.actor.getCapacites()),
desavantages: foundry.utils.duplicate(this.actor.getDesavantages()),
profils: foundry.utils.duplicate(this.actor.getProfils()),
pvMalus: this.actor.getPvMalus(),
heritage: game.settings.get("fvtt-les-heritiers", "heritiers-heritage"),
initiative: this.actor.getFlag("world", "last-initiative") || -1,
description: await TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}),
revesetranges: await TextEditor.enrichHTML(this.object.system.biodata.revesetranges, {async: true}),
secretsdecouverts: await TextEditor.enrichHTML(this.object.system.biodata.secretsdecouverts, {async: true}),
questions: await TextEditor.enrichHTML(this.object.system.biodata.questions, {async: true}),
habitat: await TextEditor.enrichHTML(this.object.system.biodata.habitat, {async: true}),
playernotes: await TextEditor.enrichHTML(this.object.system.biodata.playernotes, {async: true}),
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.description, { async: true }),
revesetranges: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.revesetranges, { async: true }),
secretsdecouverts: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.secretsdecouverts, { async: true }),
questions: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.questions, { async: true }),
habitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.habitat, { async: true }),
playernotes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.playernotes, { async: true }),
magieList: this.actor.prepareMagie(),
options: this.options,
owner: this.document.isOwner,
editScore: this.options.editScore,
@@ -76,19 +79,42 @@ export class HeritiersActorSheet extends ActorSheet {
}
/* -------------------------------------------- */
getCelluleTalents( ) {
let talents = []
for(let cellule of game.actors) {
if (cellule.type == "cellule") {
let found = cellule.system.members.find( it => it.id == this.actor.id)
if (found) {
talents = talents.concat( cellule.getTalents() )
dialogRecupUsage() {
new Dialog({
title: "Récupération des Points d'Usage",
content: "<p>Combien de Points d'Usage souhaitez-vous récupérer ?</p>",
buttons: {
one: {
icon: '<i class="fas fa-check"></i>',
label: "1 Point",
callback: () => {
this.actor.recupUsage(1)
}
},
two: {
icon: '<i class="fas fa-check"></i>',
label: "2 Points",
callback: () => {
this.actor.recupUsage(2)
}
},
four: {
icon: '<i class="fas fa-check"></i>',
label: "4 Points",
callback: () => {
this.actor.recupUsage(4)
}
},
all: {
icon: '<i class="fas fa-check"></i>',
label: "Tous les Points",
callback: () => {
this.actor.recupUsage(100)
}
}
}
}
return talents
}).render(true)
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
@@ -96,14 +122,14 @@ export class HeritiersActorSheet extends ActorSheet {
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Update Inventory Item
html.find('.item-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item")
let itemId = li.data("item-id")
const item = this.actor.items.get( itemId )
const item = this.actor.items.get(itemId)
item.sheet.render(true)
})
})
// Delete Inventory Item
html.find('.item-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item");
@@ -111,8 +137,8 @@ export class HeritiersActorSheet extends ActorSheet {
})
html.find('.edit-item-data').change(ev => {
const li = $(ev.currentTarget).parents(".item")
let itemId = li.data("item-id")
let itemType = li.data("item-type")
let itemId = li.data("item-id")
let itemType = li.data("item-type")
let itemField = $(ev.currentTarget).data("item-field")
let dataType = $(ev.currentTarget).data("dtype")
let value = ev.currentTarget.value
@@ -125,17 +151,17 @@ export class HeritiersActorSheet extends ActorSheet {
let value = Number($(event.currentTarget).data("adversite-value"))
this.actor.incDecAdversite(adv, value)
})
html.find('.quantity-modify').click(event => {
const li = $(event.currentTarget).parents(".item")
const value = Number($(event.currentTarget).data("quantite-value"))
this.actor.incDecQuantity( li.data("item-id"), value );
this.actor.incDecQuantity(li.data("item-id"), value);
})
html.find('.roll-initiative').click((event) => {
this.actor.rollInitiative()
})
html.find('.roll-carac').click((event) => {
const key = $(event.currentTarget).data("key")
this.actor.rollCarac(key, false)
@@ -143,44 +169,76 @@ export class HeritiersActorSheet extends ActorSheet {
html.find('.roll-rang').click((event) => {
const key = $(event.currentTarget).data("rang-key")
this.actor.rollRang(key, false)
})
})
html.find('.roll-root-competence').click((event) => {
const compKey = $(event.currentTarget).data("attr-key")
this.actor.rollRootCompetence(compKey)
})
html.find('.roll-competence').click((event) => {
const li = $(event.currentTarget).parents(".item")
let compId = li.data("item-id")
let compId = li.data("item-id")
this.actor.rollCompetence(compId)
})
html.find('.roll-sort').click((event) => {
const li = $(event.currentTarget).parents(".item")
let sortId = li.data("item-id")
this.actor.rollSort(sortId)
})
html.find('.roll-attaque-arme').click((event) => {
const li = $(event.currentTarget).parents(".item")
let armeId = li.data("item-id")
let armeId = li.data("item-id")
this.actor.rollAttaqueArme(armeId)
})
html.find('.roll-attaque-brutale-arme').click((event) => {
const li = $(event.currentTarget).parents(".item")
let armeId = li.data("item-id")
this.actor.rollAttaqueBrutaleArme(armeId)
})
html.find('.roll-attaque-charge-arme').click((event) => {
const li = $(event.currentTarget).parents(".item")
let armeId = li.data("item-id")
this.actor.rollAttaqueChargeArme(armeId)
})
html.find('.roll-assomer-arme').click((event) => {
const li = $(event.currentTarget).parents(".item")
let armeId = li.data("item-id")
this.actor.rollAssomerArme(armeId)
})
html.find('.roll-pouvoir').click((event) => {
const li = $(event.currentTarget).parents(".item")
let pouvoirId = li.data("item-id")
this.actor.rollPouvoir(pouvoirId)
})
html.find('.dialog-recup-usage').click((event) => {
this.dialogRecupUsage()
})
html.find('.item-add').click((event) => {
const itemType = $(event.currentTarget).data("type")
if (itemType == "sort") {
// Get data-sort-competence
let sortCompetence = $(event.currentTarget).data("sort-competence");
if (sortCompetence) {
this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType} de ${sortCompetence}`, type: itemType, system: { competence: sortCompetence } }], { renderSheet: true })
return
}
}
this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
})
html.find('.lock-unlock-sheet').click((event) => {
this.options.editScore = !this.options.editScore;
this.render(true);
});
});
html.find('.item-equip').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.equipItem( li.data("item-id") );
this.render(true);
this.actor.equipItem(li.data("item-id"));
this.render(true);
});
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {

View File

@@ -17,8 +17,8 @@ export class HeritiersActor extends Actor {
/**
* Override the create() function to provide additional SoS functionality.
*
* This overrided create() function adds initial items
* Namely: Basic skills, money,
* This overrided create() function adds initial items
* Namely: Basic skills, money,
*
* @param {Object} data Barebones actor data which this function adds onto.
* @param {Object} options (Unused) Additional options which customize the creation workflow.
@@ -41,7 +41,7 @@ export class HeritiersActor extends Actor {
const skills = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.competences")
data.items = []
for (let skill of skills) {
if (skill.system.categorie == "utile") {
if (skill.system.categorie == "utile" && skill.system.profil != "magie") {
data.items.push(skill.toObject())
}
}
@@ -54,26 +54,8 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
prepareArme(arme) {
arme = duplicate(arme)
let combat = this.getCombatValues()
if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") {
let bonusDefense = 0
arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée"))
arme.system.attrKey = "pui"
arme.system.totalDegats = arme.system.degats + "+" + combat.bonusDegatsTotal
arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff
arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.seuildefense + bonusDefense
arme.system.isdefense = true
}
if (arme.system.typearme == "jet" || arme.system.typearme == "tir") {
arme.system.competence = duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance"))
arme.system.attrKey = "adr"
arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff
arme.system.totalDegats = arme.system.degats
if (arme.system.isdefense) {
arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.seuildefense
}
}
arme = foundry.utils.duplicate(arme)
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
return arme
}
@@ -88,13 +70,23 @@ export class HeritiersActor extends Actor {
return armes
}
/* -------------------------------------------- */
getOtherMeleeWeapons(excludeArme) {
let armes = []
for (let arme of this.items) {
if (HeritiersUtility.isArmeMelee(arme) && arme.id != excludeArme._id) {
armes.push(this.prepareArme(arme))
}
}
return armes
}
/* -------------------------------------------- */
getMonnaies() {
return this.items.filter(it => it.type == "monnaie")
}
/* ----------------------- --------------------- */
addMember(actorId) {
let members = duplicate(this.system.members)
let members = foundry.utils.duplicate(this.system.members)
members.push({ id: actorId })
this.update({ 'system.members': members })
}
@@ -104,52 +96,99 @@ export class HeritiersActor extends Actor {
}
/* ----------------------- --------------------- */
getItemSorted( types) {
let items = this.items.filter(item => types.includes(item.type )) || []
getItemSorted(types) {
let items = this.items.filter(item => types.includes(item.type)) || []
HeritiersUtility.sortArrayObjectsByName(items)
return items
}
getEquipments() {
return this.getItemSorted( ["equipement", "accessoire"] )
return this.getItemSorted(["equipement", "accessoire"])
}
getAvantages() {
return this.getItemSorted( ["avantage"])
return this.getItemSorted(["avantage"])
}
getDesavantages() {
return this.getItemSorted( ["desavantage"])
return this.getItemSorted(["desavantage"])
}
getMonnaies() {
return this.getItemSorted( ["monnaie"])
return this.getItemSorted(["monnaie"])
}
getArmors() {
return this.getItemSorted( ["protection"])
return this.getItemSorted(["protection"])
}
getTalents() {
return this.getItemSorted( ["talent"])
return this.getItemSorted(["talent"])
}
getContacts() {
return this.getItemSorted( ["contact"])
return this.getItemSorted(["contact"])
}
getAtouts() {
return this.getItemSorted( ["atoutfeerique"])
return this.getItemSorted(["atoutfeerique"])
}
getCapacites() {
return this.getItemSorted( ["capacitenaturelle"])
return this.getItemSorted(["capacitenaturelle"])
}
getFee() {
return this.items.find(item => item.type =="fee")
return this.items.find(item => item.type == "fee")
}
getProfils() {
return this.getItemSorted( ["profil"])
return this.getItemSorted(["profil"])
}
getPouvoirs() {
return this.getItemSorted( ["pouvoir"])
let pouvoirs = []
for (let item of this.items) {
if (item.type == "pouvoir") {
let itemObj = foundry.utils.duplicate(item)
itemObj.maxUsage = this.getPouvoirUsageMax(item)
pouvoirs.push(itemObj)
}
}
HeritiersUtility.sortArrayObjectsByName(pouvoirs)
return pouvoirs
}
getSorts() {
return this.getItemSorted(["sort"])
}
getCompetencesMagie() {
let comp = []
for (let item of this.items) {
if (item.type == "competence" && item.system.profil == "magie") {
let itemObj = foundry.utils.duplicate(item)
comp.push(itemObj)
}
}
HeritiersUtility.sortArrayObjectsByName(comp)
return comp
}
prepareMagie() {
let magieList = []
for (let item of this.items) {
if (item.type == "competence" && item.system.profil == "magie") {
let magie = {}
magie.name = item.name
magie.competence = foundry.utils.duplicate(item)
magie.rang = Math.round(item.system.niveau / 2);
magie.rangGenericName = game.system.lesheritiers.config.rangName[magie.rang];
console.log("Magie", item.name, item.system.niveau, magie.rang, magie.rangGenericName)
magie.rangSpecificName = game.system.lesheritiers.config.rangNameSpecific[item.name][magie.rangGenericName];
magie.sorts = []
for (let sort of this.items) {
if (sort.type == "sort" && sort.system.competence == item.name) {
magie.sorts.push(sort)
}
}
magieList.push(magie)
}
}
return magieList
}
/* -------------------------------------------- */
getSkills() {
let comp = []
for (let item of this.items) {
item = duplicate(item)
item = foundry.utils.duplicate(item)
if (item.type == "competence") {
comp.push(item)
}
@@ -160,7 +199,7 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
prepareUtileSkill(item) {
let specList = []
if (item && item.system.categorie && item.system.categorie == "utile") {
if (item?.system?.categorie == "utile") {
for (let spec of item.system.specialites) {
specList.push(spec.name)
}
@@ -170,11 +209,11 @@ export class HeritiersActor extends Actor {
}
/* -------------------------------------------- */
organizeUtileSkills(kind = "mental") {
organizeMagicSkills() {
let comp = {}
for (let key in game.system.lesheritiers.config.competenceProfil) {
if ( game.system.lesheritiers.config.competenceProfil[key].kind == kind)
comp[key] = { skills: [], niveau: this.system.competences[key].niveau }
if (game.system.lesheritiers.config.competenceProfil[key].kind == "magical")
comp[key] = { skills: [], niveau: 0 }
}
for (let item of this.items) {
if (item.type == "competence") {
@@ -190,6 +229,29 @@ export class HeritiersActor extends Actor {
}
return Object.fromEntries(Object.entries(comp).sort())
}
/* -------------------------------------------- */
organizeUtileSkills(kind = "mental") {
let comp = {}
for (let key in game.system.lesheritiers.config.competenceProfil) {
if (game.system.lesheritiers.config.competenceProfil[key].kind == kind)
comp[key] = { skills: [], niveau: this.system.competences[key].niveau }
}
for (let item of this.items) {
if (item.type == "competence") {
if (item.system.categorie == "utile" && comp[item.system.profil]) {
this.prepareUtileSkill(item)
comp[item.system.profil].skills.push(item)
}
}
}
for (let key in comp) {
HeritiersUtility.sortArrayObjectsByName(comp[key].skills)
}
return Object.fromEntries(Object.entries(comp).sort())
}
/* -------------------------------------------- */
organizeContacts() {
let contactList = {}
@@ -256,9 +318,39 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
async prepareData() {
let pvMax = (this.system.caracteristiques.con.rang * 3) + 9 + this.system.pv.mod
if (this.system.pv.max != pvMax) {
this.update({ 'system.pv.max': pvMax })
}
if (this.system.biodata.magie || this.type == "pnj") {
let pointsAmes = this.system.caracteristiques.esp.rang + this.system.caracteristiques.san.rang + this.getMaxRangMagie()
if (this.system.magie.pointsame.max != pointsAmes) {
this.update({ 'system.magie.pointsame.max': pointsAmes })
}
}
super.prepareData();
}
/* -------------------------------------------- */
getMaxRangMagie() {
let niv = 0
let bestMagie
for (let comp of this.items) {
if (comp.type == "competence" && comp.system.profil == "magie") {
if (comp.system.niveau > niv) {
bestMagie = comp
niv = comp.system.niveau
}
}
}
if (bestMagie) {
return Math.round(bestMagie.system.niveau / 2)
}
return 0
}
/* -------------------------------------------- */
prepareDerivedData() {
@@ -278,7 +370,7 @@ export class HeritiersActor extends Actor {
getItemById(id) {
let item = this.items.find(item => item.id == id);
if (item) {
item = duplicate(item)
item = foundry.utils.duplicate(item)
}
return item;
}
@@ -286,7 +378,7 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
async equipItem(itemId) {
let item = this.items.find(item => item.id == itemId)
if (item && item.system) {
if (item?.system) {
let update = { _id: item.id, "system.equipped": !item.system.equipped }
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
}
@@ -297,7 +389,7 @@ export class HeritiersActor extends Actor {
let item = this.items.find(item => item.id == itemId)
if (item) {
console.log("Item ", item, itemField, dataType, value)
if (dataType) {
if (dataType) {
if (dataType.toLowerCase() == "number") {
value = Number(value)
} else {
@@ -312,15 +404,15 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
getPvMalus() {
if (this.system.pv.value > 0) {
if (this.system.pv.value < this.system.pv.max / 2) {
return -1
}
if (this.system.pv.value < 5) {
return -2
return { name: "Santé", value: -2 }
}
return 0
if (this.system.pv.value < this.system.pv.max / 2) {
return { name: "Santé", value: -1 }
}
return { name: "Santé", value: 0 }
}
return "Moribond(e)"
return { name: "Moribond(e)", value: -50 }
}
/* -------------------------------------------- */
@@ -336,7 +428,7 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
getCarac(attrKey) {
return duplicate(this.system.caracteristiques)
return foundry.utils.duplicate(this.system.caracteristiques)
}
/* -------------------------------------------- */
@@ -347,7 +439,7 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
async equipGear(equipmentId) {
let item = this.items.find(item => item.id == equipmentId);
if (item && item.system.data) {
if (item?.system) {
let update = { _id: item.id, "system.equipped": !item.system.equipped };
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
}
@@ -357,13 +449,13 @@ export class HeritiersActor extends Actor {
getSubActors() {
let subActors = [];
for (let id of this.system.subactors) {
subActors.push(duplicate(game.actors.get(id)));
subActors.push(foundry.utils.duplicate(game.actors.get(id)));
}
return subActors;
}
/* -------------------------------------------- */
async addSubActor(subActorId) {
let subActors = duplicate(this.system.subactors);
let subActors = foundry.utils.duplicate(this.system.subactors);
subActors.push(subActorId);
await this.update({ 'system.subactors': subActors });
}
@@ -385,11 +477,12 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
async incDecAdversite(adv, incDec = 0) {
let adversite = duplicate(this.system.adversite)
let adversite = foundry.utils.duplicate(this.system.adversite)
adversite[adv] += Number(incDec)
adversite[adv] = Math.max(adversite[adv], 0)
this.update({ 'system.adversite': adversite })
}
/* -------------------------------------------- */
async incDecQuantity(objetId, incDec = 0) {
let objetQ = this.items.get(objetId)
@@ -432,7 +525,7 @@ export class HeritiersActor extends Actor {
/* -------------------------------------------- */
async setPredilectionUsed(compId, predIdx) {
let comp = this.items.get(compId)
let pred = duplicate(comp.system.predilections)
let pred = foundry.utils.duplicate(comp.system.predilections)
pred[predIdx].used = true
await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }])
}
@@ -454,7 +547,7 @@ export class HeritiersActor extends Actor {
}
if (arme.system.totalDefensif > maxDef) {
maxDef = arme.system.totalDefensif
bestArme = duplicate(arme)
bestArme = foundry.utils.duplicate(arme)
}
}
return bestArme
@@ -469,7 +562,7 @@ export class HeritiersActor extends Actor {
for (let auto of talent.system.automations) {
if (auto.eventtype === "prepare-roll") {
if (auto.competence.toLowerCase() == competence.name.toLowerCase()) {
talent = duplicate(talent)
talent = foundry.utils.duplicate(talent)
talent.system.bonus = auto.bonus
talent.system.baCost = auto.baCost
talents.push(talent)
@@ -482,19 +575,21 @@ export class HeritiersActor extends Actor {
}
/* -------------------------------------------- */
getCurrentParade() {
if ( this.system.statutmasque == "masque") {
if (this.system.statutmasque == "masque") {
return this.system.combat.parade.masquee
}
return this.system.combat.parade.demasquee
}
/* -------------------------------------------- */
getCurrentEsquive() {
if ( this.system.statutmasque == "masque") {
if (this.system.statutmasque == "masque") {
return this.system.combat.esquive.masquee
}
return this.system.combat.esquive.demasquee
}
getCurrentResistancePhysique() {
return this.system.combat.resistancephysique.value
}
/* -------------------------------------------- */
getTricherie() {
return this.system.rang.tricherie.value
@@ -507,9 +602,27 @@ export class HeritiersActor extends Actor {
incDecTricherie(value) {
let tricherie = this.system.rang.tricherie
tricherie.value += value
if (tricherie.value < 0 || tricherie.value > tricherie.max) {
ui.notifications.warn("Pas assez de points de Tricherie !")
return false
}
tricherie.value = Math.max(tricherie.value, 0)
tricherie.value = Math.min(tricherie.value, tricherie.max)
this.update({ 'system.rang.tricherie': tricherie })
return true
}
/* -------------------------------------------- */
getPireCompetence(compName1, compName2) {
let comp1 = this.items.find(it => it.name == compName1)
let comp2 = this.items.find(it => it.name == compName2)
if (comp1 && comp2) {
if (comp1.system.niveau > comp2.system.niveau) {
return comp1
} else {
return comp2
}
}
return undefined
}
/* -------------------------------------------- */
@@ -527,18 +640,19 @@ export class HeritiersActor extends Actor {
rollData.useTricherie = false
rollData.useSpecialite = false
rollData.useHeritage = false
rollData.pvMalus = this.getPvMalus()
rollData.pouvoirPointsUsage = 1
rollData.rulesMalus.push(this.getPvMalus())
if (compId) {
rollData.competence = duplicate(this.items.get(compId) || {})
rollData.competence = foundry.utils.duplicate(this.items.get(compId) || {})
this.prepareUtileSkill(rollData.competence)
rollData.actionImg = rollData.competence?.img
}
if (compName) {
rollData.competence = duplicate(this.items.find(item => item.name.toLowerCase() == compName.toLowerCase()) || {})
if (rollData.competence && rollData.competence.name) {
rollData.competence = foundry.utils.duplicate(this.items.find(item => item.name.toLowerCase() == compName.toLowerCase()) || {})
if (rollData.competence?.name) {
this.prepareUtileSkill(rollData.competence)
rollData.actionImg = rollData.competence?.img
rollData.actionImg = rollData.competence?.img
} else {
rollData.competence = undefined
}
@@ -584,8 +698,7 @@ export class HeritiersActor extends Actor {
async rollRootCompetence(compKey) {
let rollData = this.getCommonRollData()
rollData.mode = "competence"
console.log("Compkey", compKey)
rollData.competence = {name: this.system.competences[compKey].label, system: { niveau: this.system.competences[compKey].niveau }}
rollData.competence = { name: this.system.competences[compKey].label, system: { niveau: this.system.competences[compKey].niveau } }
console.log("RollDatra", rollData)
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
@@ -599,83 +712,292 @@ export class HeritiersActor extends Actor {
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
inDecCarac(key, incDec) {
let carac = this.system.caracteristiques[key]
carac.value += incDec
if (carac.value < 0 || carac.value > carac.max) {
ui.notifications.warn("Pas assez de points dans cette caractéristique !")
return false
}
carac.value = Math.max(carac.value, 0)
carac.value = Math.min(carac.value, carac.max)
this.update({ [`system.caracteristiques.${key}`]: carac })
return true
}
/* -------------------------------------------- */
async rollSort(sortId) {
let sort = this.items.get(sortId)
let comp = this.items.find(it => it.type == "competence" && it.name.toLowerCase() == sort.system.competence.toLowerCase())
if (!comp) {
ui.notifications.warn("Compétence de magie associée non trouvée !")
return
}
let rollData = this.getCommonRollData(comp.id)
rollData.mode = "sort"
rollData.sort = foundry.utils.duplicate(sort)
rollData.sdValue = HeritiersUtility.getSDSortValue(Number(sort.system.niveau))
rollData.sortPointsAme = Number(sort.system.niveau)
if (rollData.sortPointsAme > this.system.magie.pointsame.value) {
// Vérifier si au moins 1 point d'Esprit est disponible
if (this.system.caracteristiques.esp.value < 1) {
ui.notifications.warn("Pas assez de Points d'Esprit ni de Points d'Ame pour lancer ce sort (requis: 1, disponible: " + this.system.caracteristiques.esp.value + ")")
return
} else {
rollData.spendEsprit = true
ui.notifications.warn(`Vous n'avez pas assez de Points d'Ame pour lancer ce sort (requis: ${rollData.sortPointsAme}, disponible: ${this.system.magie.pointsame.value}). Un Point d'Esprit sera utilisé à la place si vous effectuez le lancer.`)
}
}
if (sort.system.carac2 != "none") {
// get the best carac between carac1 and carac2
if (this.system.caracteristiques[sort.system.carac1].value > this.system.caracteristiques[sort.system.carac2].value) {
rollData.caracKey = sort.system.carac1
} else {
rollData.caracKey = sort.system.carac2
}
rollData.caracMessage = "Ce sort peut être lancé avec " + game.system.lesheritiers.config.caracList[sort.system.carac1] + " ou " + game.system.lesheritiers.config.caracList[sort.system.carac2] + ". La meilleure caractéristique a été selectionnée."
} else {
rollData.caracKey = sort.system.carac1
}
console.log("RollData", rollData)
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollAttaqueArme(armeId) {
let arme = this.items.get(armeId)
if (arme) {
arme = foundry.utils.duplicate(arme)
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
let competenceName = "Tir"
let key = "prec"
if (arme.system.categorie == "blanche" || arme.system.categorie == "improvise") {
if (arme.system.isMelee) {
competenceName = "Mêlée"
key = "agi"
}
let rollData = this.getCommonRollData(undefined, competenceName)
rollData.carac = this.system.caracteristiques[key]
rollData.caracKey = key
rollData.arme = duplicate(arme)
rollData.arme = arme
rollData.mode = "arme"
rollData.armes = this.getOtherMeleeWeapons(arme)
if (rollData.defenderTokenId && arme.system.isMelee) {
rollData.cacheDifficulte = true
}
console.log(">>>> ARME", rollData)
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
}
/* -------------------------------------------- */
async rollAttaqueBrutaleArme(armeId) {
let arme = this.items.get(armeId)
if (arme) {
let key = "for"
let competenceName = "Mêlée"
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
let rollData = this.getCommonRollData(undefined, competenceName)
rollData.carac = this.system.caracteristiques[key]
rollData.caracKey = key
rollData.arme = foundry.utils.duplicate(arme)
rollData.mode = "attaquebrutale"
rollData.armes = this.getOtherMeleeWeapons(arme)
rollData.rulesMalus.push({ name: "Attaque brutale", value: -2 })
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
}
/* -------------------------------------------- */
async rollAttaqueChargeArme(armeId) {
let arme = this.items.get(armeId)
if (arme) {
let key = "agi"
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
let pireCompetence = this.getPireCompetence("Mêlée", "Mouvement")
let rollData = this.getCommonRollData(undefined, pireCompetence.name)
rollData.carac = this.system.caracteristiques[key]
rollData.caracKey = key
rollData.arme = foundry.utils.duplicate(arme)
rollData.armes = this.getOtherMeleeWeapons(arme)
rollData.mode = "attaquecharge"
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
}
/* -------------------------------------------- */
async rollAssomerArme(armeId) {
let arme = this.items.get(armeId)
if (arme) {
let competenceName = "Mêlée"
//arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
let rollData = this.getCommonRollData(undefined, competenceName)
rollData.carac = this.system.caracteristiques["agi"]
rollData.caracKey = "agi"
rollData.arme = foundry.utils.duplicate(arme)
rollData.mode = "assommer"
if (rollData.defenderTokenId) {
rollData.cacheDifficulte = true
}
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
}
/* -------------------------------------------- */
pouvoirPassifDialog(pouvoir) {
let rollData = this.getCommonRollData()
rollData.pouvoir = pouvoir
rollData.mode = "pouvoirpassif"
rollData.pouvoirPointsUsage = 0
rollData.noRoll = true
let d = new Dialog({
title: "Activer le pouvoir passif " + pouvoir.name,
content: "<p>Choisissez le nombre de Points d'Usage</p>",
buttons: {
one: {
icon: '<i class="fas fa-check"></i>',
label: "1 Point d'Usage",
callback: () => {
rollData.pouvoirPointsUsage = 1;
HeritiersUtility.rollHeritiers(rollData);
}
},
two: {
icon: '<i class="fas fa-check"></i>',
label: "2 Points d'Usage",
callback: () => {
rollData.pouvoirPointsUsage = 2;
HeritiersUtility.rollHeritiers(rollData);
}
},
three: {
icon: '<i class="fas fa-check"></i>',
label: "3 Points d'Usage",
callback: () => {
rollData.pouvoirPointsUsage = 3;
HeritiersUtility.rollHeritiers(rollData);
}
},
four: {
icon: '<i class="fas fa-check"></i>',
label: "4 Points d'Usage",
callback: () => {
rollData.pouvoirPointsUsage = 4;
HeritiersUtility.rollHeritiers(rollData);
}
},
close: {
icon: '<i class="fas fa-times"></i>',
label: "Annuler",
callback: () => {
}
}
},
default: "one",
render: html => console.log("Pouvoir passif"),
close: html => console.log("No option")
});
d.render(true);
}
/* -------------------------------------------- */
async rollPouvoir(pouvoirId) {
let pouvoir = this.items.get(pouvoirId)
if (pouvoir) {
let rollData = this.getCommonRollData(undefined, undefined)
if ( pouvoir.system.feeriemasque != "autre") {
rollData.pouvoirBase = duplicate(this.system.rang[pouvoir.system.feeriemasque.toLowerCase()])
rollData.pouvoirBase.label = "Féerie"
rollData.carac = duplicate(this.system.caracteristiques[pouvoir.system.carac])
rollData.caracKey = pouvoir.system.carac
if (pouvoir.system.pouvoirtype == "passif") {
this.pouvoirPassifDialog(pouvoir)
return
}
rollData.pouvoir = duplicate(pouvoir)
let rollData = this.getCommonRollData(undefined, undefined)
rollData.pouvoirMaxUsage = this.getPouvoirUsageMax(pouvoir)
rollData.pouvoir = foundry.utils.duplicate(pouvoir)
rollData.mode = "pouvoir"
if (pouvoir.system.feeriemasque != "autre") {
rollData.pouvoirBase = foundry.utils.duplicate(this.system.rang[pouvoir.system.feeriemasque.toLowerCase()])
rollData.pouvoirBase.label = "Féerie"
if (pouvoir.system.istest && !pouvoir.system.carac) {
ui.notifications.warn("Le pouvoir actif " + pouvoir.name + " n'a pas de caractéristique associée")
}
if (pouvoir.system.istest) {
rollData.carac = foundry.utils.duplicate(this.system.caracteristiques[pouvoir.system.carac])
rollData.caracKey = pouvoir.system.carac
} else {
rollData.noRoll = true
HeritiersUtility.rollHeritiers(rollData);
return;
//this.incDecPointsUsage(pouvoir.id, -rollData.pouvoirPointsUsage)
//ui.notifications.warn("Le pouvoir actif " + pouvoir.name + " a été utilisé, dépense de " + pouvoirPointsUsage + " points d'usage")
}
}
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
}
}
/* -------------------------------------------- */
async rollArmeOffensif(armeId) {
let arme = this.items.get(armeId)
if (arme.type == "arme") {
arme = this.prepareArme(arme)
}
let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id)
rollData.arme = arme
HeritiersUtility.updateWithTarget(rollData)
console.log("ARME!", rollData)
let rollDialog = await HeritiersRollDialog.create(this, rollData)
rollDialog.render(true)
incDecPointsAme(value) {
let newValue = this.system.magie.pointsame.value + value
newValue = Math.max(newValue, 0)
newValue = Math.min(newValue, this.system.magie.pointsame.max)
this.update({ 'system.magie.pointsame.value': newValue })
}
/* -------------------------------------------- */
async rollArmeDegats(armeId, targetVigueur = undefined) {
let arme = this.items.get(armeId)
if (arme.type == "arme") {
arme = this.prepareArme(arme)
}
console.log("DEGATS", arme)
let roll = new Roll("1d10+" + arme.system.totalDegats).roll({ async: false })
await HeritiersUtility.showDiceSoNice(roll, game.settings.get("core", "rollMode"));
let nbEtatPerdus = 0
if (targetVigueur) {
nbEtatPerdus = Math.floor(roll.total / targetVigueur)
}
let rollData = {
arme: arme,
finalResult: roll.total,
alias: this.name,
actorImg: this.img,
actorId: this.id,
actionImg: arme.img,
targetVigueur: targetVigueur,
nbEtatPerdus: nbEtatPerdus
}
HeritiersUtility.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-degats-result.html`, rollData)
})
incDecPV(value) {
let newValue = this.system.pv.value + value
newValue = Math.max(newValue, 0)
newValue = Math.min(newValue, this.system.pv.max)
this.update({ 'system.pv.value': newValue })
}
/* -------------------------------------------- */
incDecPointsUsage(pouvoirId, value) {
let pouvoir = this.items.get(pouvoirId)
let newValue = pouvoir.system.pointsusagecourant + value
newValue = Math.max(newValue, 0)
newValue = Math.min(newValue, this.getPouvoirUsageMax(pouvoir))
this.updateEmbeddedDocuments('Item', [{ _id: pouvoirId, 'system.pointsusagecourant': newValue }])
}
/* -------------------------------------------- */
getPouvoirUsage(pouvoirId) {
let pouvoir = this.items.get(pouvoirId)
return pouvoir.system.pointsusagecourant
}
/* -------------------------------------------- */
getPouvoirUsageMax(pouvoir) {
if (pouvoir.system.masquetype == "masque") {
return this.system.rang.masque.value
}
return this.system.rang.feerie.value
}
/* -------------------------------------------- */
recupUsage(value) {
let updates = []
for (let pouvoir of this.items) {
if (pouvoir.type == "pouvoir") {
let newValue = pouvoir.system.pointsusagecourant + value
newValue = Math.max(newValue, 0)
newValue = Math.min(newValue, this.getPouvoirUsageMax(pouvoir))
updates.push({ _id: pouvoir.id, 'system.pointsusagecourant': newValue })
}
}
if (updates.length > 0) {
this.updateEmbeddedDocuments('Item', updates)
}
}
}

View File

@@ -12,25 +12,26 @@ export const HERITIERS_CONFIG = {
"san": "Sang-Froid"
},
competenceCategorie : {
competenceCategorie: {
"utile": "Utile",
"futile": "Futile"
},
contactType: {
contact: "Contact",
contact: "Contact",
allie: "Allié",
ennemi: "Ennemi",
interet: "Personne d'interêt"
},
competenceProfil : {
"aventurier": {kind: "physical", name: "Aventurier"},
"roublard": {kind: "physical",name: "Roublard"},
"combattant": {kind: "physical",name:"Combattant"},
"erudit": {kind: "mental",name:"Erudit"},
"savant": {kind: "mental",name:"Savant"},
"gentleman": {kind: "mental",name:"Gentleman"}
competenceProfil: {
"aventurier": { kind: "physical", name: "Aventurier" },
"roublard": { kind: "physical", name: "Roublard" },
"combattant": { kind: "physical", name: "Combattant" },
"erudit": { kind: "mental", name: "Erudit" },
"savant": { kind: "mental", name: "Savant" },
"gentleman": { kind: "mental", name: "Gentleman" },
"magie": { kind: "magical", name: "Magie" },
},
baseTestPouvoir: {
"feerie": "Féerie",
@@ -52,7 +53,7 @@ export const HERITIERS_CONFIG = {
},
statutMasque: {
"masque": "Masqué",
"demasque":"Démasqué"
"demasque": "Démasqué"
},
niveauPouvoir: {
"normal": "Normal",
@@ -65,35 +66,42 @@ export const HERITIERS_CONFIG = {
},
seuilsDifficulte: {
"-1": "Aucun/Non applicable",
"5": "Enfantine",
"6": "Triviale",
"7": "Moins Triviale",
"8": "Aisée",
"7": "Moins Aisée",
"10": "Normale",
"11": "Moins Normale",
"12": "Compliquée",
"13": "Plus Compliquée",
"14": "Difficile",
"15": "Plus Difficile",
"16": "Très Difficile",
"17": "Très Très Difficile",
"18": "Critique",
"19": "Plus Critique",
"20": "Insurmontable",
"20": "Très Insurmontable",
"22": "Surhumaine",
"23": "Très Surhumaine",
"24": "Epique",
"25": "Plus Epique",
"26": "Légendaire",
"26": "Très Légendaire",
"28": "Mythique",
"29": "Plus Mythique",
"30": "Divine"
"5": "Enfantine (5)",
"6": "Triviale (6)",
"7": "Moins Triviale (7)",
"8": "Aisée (8)",
"9": "Moins Aisée (9)",
"10": "Normale (10)",
"11": "Moins Normale (11)",
"12": "Compliquée (12)",
"13": "Plus Compliquée (13)",
"14": "Difficile (14)",
"15": "Plus Difficile (15)",
"16": "Très Difficile (16)",
"17": "Très Très Difficile (17)",
"18": "Critique (18)",
"19": "Plus Critique (19)",
"20": "Insurmontable (20)",
"21": "Très Insurmontable (21)",
"22": "Surhumaine (22)",
"23": "Très Surhumaine (23)",
"24": "Epique (24)",
"25": "Plus Epique (25)",
"26": "Légendaire (26)",
"27": "Très Légendaire (27)",
"28": "Mythique (28)",
"29": "Plus Mythique (29)",
"30": "Divine (30)"
},
categorieArme : {
attaqueCible: {
"none": "Aucune",
"membre": "Membre",
"main": "Main",
"tete": "Tête/Coeur"
},
categorieArme: {
"trait": "Arme de trait",
"poing": "Arme de poing",
"epaule": "Arme d'épaule",
@@ -101,7 +109,7 @@ export const HERITIERS_CONFIG = {
"blanche": "Arme blanche",
"improvise": "Arme improvisée",
"explosif": "Explosif"
},
},
typeArme: {
"naturelle": "Arme naturelle",
"trait": "Trait",
@@ -123,13 +131,13 @@ export const HERITIERS_CONFIG = {
"controlee": "Contrôlée (C)",
"prohibee": "Prohibée (P)"
},
armeDissimulation :{
armeDissimulation: {
"tresfacile": "Très facile (TF)",
"facile": "Facile (F)",
"difficile": "Difficile (D)",
"impossible": "Impossible (I)"
},
typeProtection : {
typeProtection: {
"balle": "Protège ds balles",
"melee": "Protège en mélée",
"tout": "Tout type de dégats"
@@ -138,7 +146,122 @@ export const HERITIERS_CONFIG = {
"traditionnelle": "Traditionnelle",
"moderne": "Moderne",
"orientale": "Orientale"
},
typeContact: {
"contact": "Contact",
"allie": "Allié",
"ennemi": "Ennemi",
"interet": "Personne d'interêt"
},
niveauContact: {
"1": "1",
"2": "2",
"3": "3",
},
pointsUsageList: {
"1": "1",
"2": "2",
"3": "3",
"4": "4",
},
attaquePlusieursList: {
"0": "0",
"1": "+1",
"2": "+2",
},
attaque2ArmesListe: [
{ value: "0", label: "Aucun" },
{ value: "-4", label: "Deux armes à 1 main" },
{ value: "-2", label: "Deux armes naturelles" },
{ value: "-2", label: "Avec spécialisation \"Mauvaise Main\"" }
],
typeProfil: {
"mineur": "Mineur",
"majeur": "Majeur",
},
bonusMalusContext: [
{ value: "-6", label: "-6" },
{ value: "-5", label: "-5" },
{ value: "-4", label: "-4" },
{ value: "-3", label: "-3" },
{ value: "-2", label: "-2" },
{ value: "-1", label: "-1" },
{ value: "0", label: "0" },
{ value: "1", label: "+1" },
{ value: "2", label: "+2" },
{ value: "3", label: "+3" },
{ value: "4", label: "+4" },
{ value: "5", label: "+5" },
{ value: "6", label: "+6" }
],
listNiveauSort: {
"1": "1",
"2": "2",
"3": "3",
"4": "4"
},
listNiveau: {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"10": "10"
},
rangName: [
"Novice",
"Novice",
"Adepte",
"Maître",
"Grand Maître"
],
rangNameSpecific: {
"Druidisme": {
"Novice": "Eubage",
"Adepte": "Saronide",
"Maître": "Ovate",
"Grand Maître": "Archidruide"
},
"Faëomancie": {
"Novice": "Marmiton",
"Adepte": "Queux",
"Maître": "Chef",
"Grand Maître": "Maître-queux"
},
"Nécromancie": {
"Novice": "Inexpertus",
"Adepte": "Discipulus",
"Maître": "Dominus",
"Grand Maître": "Magister"
},
"Magie du Clan": {
"Novice": "Apprenti",
"Adepte": "Disciple",
"Maître": "Maître",
"Grand Maître": "Éminence"
},
"Théurgie": {
"Novice": "Frère",
"Adepte": "Père",
"Maître": "Saint",
"Grand Maître": "Apôtre"
},
"Grand Langage": {
"Novice": "Éveillé",
"Adepte": "Initié",
"Maître": "Sage",
"Grand Maître": "Docteur"
}
},
soufflesMagieDuClan: {
"Souffle du Combat": "Souffle du Combat",
"Souffle du Mouvement": "Souffle du Mouvement",
"Souffle de l'Esprit": "Souffle de l'Esprit"
}
}

View File

@@ -1,25 +0,0 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { HeritiersActorSheet } from "./heritiers-actor-sheet.js";
import { HeritiersUtility } from "./heritiers-utility.js";
/* -------------------------------------------- */
export class HeritiersCreatureSheet extends HeritiersActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["fvtt-les-heritiers", "sheet", "actor"],
template: "systems/fvtt-les-heritiers/templates/creature-sheet.html",
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
editScore: false
})
}
}

View File

@@ -4,12 +4,12 @@ import { HeritiersUtility } from "./heritiers-utility.js";
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
export class HeritiersItemSheet extends ItemSheet {
export class HeritiersItemSheet extends foundry.appv1.sheets.ItemSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["fvtt-les-heritiers", "sheet", "item"],
template: "systems/fvtt-les-heritiers/templates/item-sheet.html",
dragDrop: [{ dragSelector: null, dropSelector: null }],
@@ -48,7 +48,7 @@ export class HeritiersItemSheet extends ItemSheet {
/* -------------------------------------------- */
async getData() {
const objectData = duplicate(this.object)
const objectData = foundry.utils.duplicate(this.object)
let formData = {
title: this.title,
id: this.id,
@@ -62,11 +62,25 @@ export class HeritiersItemSheet extends ItemSheet {
options: this.options,
owner: this.document.isOwner,
config: game.system.lesheritiers.config,
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}),
isArmeMelee: HeritiersUtility.isArmeMelee(this.object),
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, {async: true}),
mr: (this.object.type == 'specialisation'),
isGM: game.user.isGM
isGM: game.user.isGM,
usageMax: -1
}
// Items specific data
if (this.object.type == 'pouvoir' && this.document.isOwner && this.actor) {
formData.usageMax = this.actor.getPouvoirUsageMax(this.object)
if (this.object.system.pointsusagecourant == -1) {
this.object.system.pointsusagecourant = formData.usageMax
}
}
if (this.object.type == 'sort' ) {
formData.competencesMagie = HeritiersUtility.getCompetencesMagie()
}
//this.options.editable = !(this.object.origin == "embeddedItem");
console.log("ITEM DATA", formData, this);
return formData;
@@ -86,7 +100,7 @@ export class HeritiersItemSheet extends ItemSheet {
/* -------------------------------------------- */
postItem() {
let chatData = duplicate(HeritiersUtility.data(this.item));
let chatData = foundry.utils.duplicate(HeritiersUtility.data(this.item));
if (this.actor) {
chatData.actor = { id: this.actor.id };
}
@@ -128,55 +142,55 @@ export class HeritiersItemSheet extends ItemSheet {
})
html.find('#add-specialite').click(ev => {
let spec = duplicate(this.object.system.specialites)
spec.push( { name: "Nouvelle Spécialité", id: randomID(16), used: false })
let spec = foundry.utils.duplicate(this.object.system.specialites)
spec.push( { name: "Nouvelle Spécialité", id: foundry.utils.randomID(16), used: false })
this.object.update( { 'system.specialites': spec })
})
html.find('.delete-specialite').click(ev => {
const li = $(ev.currentTarget).parents(".specialite-item")
let index = li.data("specialite-index")
let spec = duplicate(this.object.system.specialites)
let spec = foundry.utils.duplicate(this.object.system.specialites)
spec.splice(index,1)
this.object.update( { 'system.specialites': spec })
})
html.find('.edit-specialite').change(ev => {
const li = $(ev.currentTarget).parents(".specialite-item")
let index = li.data("specialite-index")
let spec = duplicate(this.object.system.specialites)
let spec = foundry.utils.duplicate(this.object.system.specialites)
spec[index].name = ev.currentTarget.value
spec[index].id = spec[index].id || randomID(16)
spec[index].id = spec[index].id || foundry.utils.randomID(16)
this.object.update( { 'system.specialites': spec })
})
html.find('.edit-specialite-description').change(ev => {
const li = $(ev.currentTarget).parents(".specialite-item")
let index = li.data("specialite-index")
let spec = duplicate(this.object.system.specialites)
let spec = foundry.utils.duplicate(this.object.system.specialites)
spec[index].description = ev.currentTarget.value
spec[index].id = spec[index].id || randomID(16)
spec[index].id = spec[index].id || foundry.utils.randomID(16)
this.object.update( { 'system.specialites': spec })
})
})
html.find('#add-automation').click(ev => {
let autom = duplicate(this.object.system.automations)
autom.push( { eventtype: "on-drop", name: "Automatisation 1", competence: "", minLevel: 0, id: randomID(16) })
let autom = foundry.utils.duplicate(this.object.system.automations)
autom.push( { eventtype: "on-drop", name: "Automatisation 1", competence: "", minLevel: 0, id: foundry.utils.randomID(16) })
this.object.update( { 'system.automations': autom })
})
html.find('.delete-automation').click(ev => {
const li = $(ev.currentTarget).parents(".automation-item")
let index = li.data("automation-index")
let autom = duplicate(this.object.system.automations)
let autom = foundry.utils.duplicate(this.object.system.automations)
autom.splice(index,1)
this.object.update( { 'system.automations': autom })
})
html.find('.automation-edit-field').change(ev => {
let index = $(ev.currentTarget).data("automation-index")
let field = $(ev.currentTarget).data("automation-field")
let auto = duplicate(this.object.system.automations)
let auto = foundry.utils.duplicate(this.object.system.automations)
auto[index][field] = ev.currentTarget.value
auto[index].id = auto[index].id || randomID(16)
auto[index].id = auto[index].id || foundry.utils.randomID(16)
this.object.update( { 'system.automations': auto })
})
})
// Update Inventory Item
html.find('.item-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item");

View File

@@ -18,6 +18,7 @@ export const defaultItemImg = {
fee: "systems/fvtt-les-heritiers/assets/icons/faery_type.webp",
profil: "systems/fvtt-les-heritiers/assets/icons/profil.webp",
equipement: "systems/fvtt-les-heritiers/assets/icons/equipement.webp",
sort: "systems/fvtt-les-heritiers/assets/icons/sort.webp",
}
/**

View File

@@ -12,7 +12,6 @@ import { HeritiersActor } from "./heritiers-actor.js";
import { HeritiersItemSheet } from "./heritiers-item-sheet.js";
import { HeritiersActorSheet } from "./heritiers-actor-sheet.js";
import { HeritiersActorPNJSheet } from "./heritiers-actor-pnj-sheet.js";
import { HeritiersCreatureSheet } from "./heritiers-creature-sheet.js";
import { HeritiersUtility } from "./heritiers-utility.js";
import { HeritiersCombat } from "./heritiers-combat.js";
import { HeritiersItem } from "./heritiers-item.js";
@@ -31,7 +30,7 @@ Hooks.once("init", async function () {
HeritiersUtility.preloadHandlebarsTemplates()
/* -------------------------------------------- */
// Set an initiative formula for the system
// Set an initiative formula for the system
CONFIG.Combat.initiative = {
formula: "1d10",
decimals: 1
@@ -47,6 +46,8 @@ Hooks.once("init", async function () {
CONFIG.Combat.documentClass = HeritiersCombat
CONFIG.Actor.documentClass = HeritiersActor
CONFIG.Item.documentClass = HeritiersItem
// Create an object of bonus/malus from -6 to +6 signed
HERITIERS_CONFIG.bonusMalus = Array.from({ length: 7 }, (v, k) => toString(k - 6))
game.system.lesheritiers = {
HeritiersUtility,
config: HERITIERS_CONFIG
@@ -54,12 +55,12 @@ Hooks.once("init", async function () {
/* -------------------------------------------- */
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet("fvtt-les-heritiers", HeritiersActorSheet, { types: ["personnage"], makeDefault: true })
Actors.registerSheet("fvtt-les-heritiers", HeritiersActorPNJSheet, { types: ["pnj"], makeDefault: true })
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", HeritiersActorSheet, { types: ["personnage"], makeDefault: true })
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", HeritiersActorPNJSheet, { types: ["pnj"], makeDefault: true })
Items.unregisterSheet("core", ItemSheet);
Items.registerSheet("fvtt-les-heritiers", HeritiersItemSheet, { makeDefault: true })
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", HeritiersItemSheet, { makeDefault: true })
HeritiersUtility.init()
@@ -79,31 +80,17 @@ function welcomeMessage() {
}
/* -------------------------------------------- */
// Register world usage statistics
function registerUsageCount(registerKey) {
if (game.user.isGM) {
game.settings.register(registerKey, "world-key", {
name: "Unique world key",
scope: "world",
config: false,
default: "",
type: String
});
let worldKey = game.settings.get(registerKey, "world-key")
if (worldKey == undefined || worldKey == "") {
worldKey = randomID(32)
game.settings.set(registerKey, "world-key", worldKey)
}
// Simple API counter
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
//$.ajaxSetup({
//headers: { 'Access-Control-Allow-Origin': '*' }
//})
$.ajax(regURL)
async function importDefaultScene() {
let exists = game.scenes.find(j => j.name == "Accueil");
if (!exists) {
const scenes = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.scenes")
let newDocuments = scenes.filter(i => i.name == "Accueil");
await game.scenes.documentClass.create(newDocuments);
game.scenes.find(i => i.name == "Accueil").activate();
}
}
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
@@ -120,14 +107,15 @@ Hooks.once("ready", function () {
});
}
registerUsageCount('fvtt-les-heritiers')
welcomeMessage()
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
console.log("ClassCounter loaded", moduleCounter)
moduleCounter.ClassCounter.registerUsageCount()
}).catch(err =>
console.log("No stats available, giving up.")
)
welcomeMessage();
importDefaultScene();
// CSS patch for v9
if (game.version) {
let sidebar = document.getElementById("sidebar");
sidebar.style.width = "min-content";
}
});
/* -------------------------------------------- */
@@ -137,10 +125,9 @@ Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {
let regExp = /(\S+)/g;
let commands = content.match(regExp);
if (game.system.mournblade.commands.processChatCommand(commands, content, msg)) {
if (game.system.lesheritiers.commands.processChatCommand(commands, content, msg)) {
return false;
}
}
return true;
});

View File

@@ -5,8 +5,8 @@ export class HeritiersRollDialog extends Dialog {
/* -------------------------------------------- */
static async create(actor, rollData) {
let options = { classes: ["HeritiersDialog"], width: 320, height: 'fit-content', 'z-index': 99999 };
let html = await renderTemplate('systems/fvtt-les-heritiers/templates/roll-dialog-generic.html', rollData);
let options = { classes: ["HeritiersDialog"], width: 420, height: 'fit-content', 'z-index': 99999 };
let html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-les-heritiers/templates/roll-dialog-generic.html', rollData);
return new HeritiersRollDialog(actor, rollData, html, options);
}
@@ -18,18 +18,31 @@ export class HeritiersRollDialog extends Dialog {
icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d8",
callback: () => { this.roll("d8") }
},
rolld10: {
}
}
let enableD10 = false
let enableD12 = false
if (rollData.mode == "pouvoir" || rollData.competence?.system.niveau > 0) {
enableD10 = true
}
if (rollData.mode == "pouvoir" || rollData.competence?.system.niveau > 1) {
enableD12 = true
}
if (enableD10) {
buttons.rolld10 = {
icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d10",
callback: () => { this.roll("d10") }
},
rolld12: {
}
}
if (enableD12) {
buttons.rolld12 = {
icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d12",
callback: () => { this.roll("d12") }
}
}
if (rollData.tricherie) {
buttons["rollTricherie"] = {
icon: '<i class="fas fa-check"></i>',
@@ -55,7 +68,7 @@ export class HeritiersRollDialog extends Dialog {
buttons: buttons,
close: close
}
// Overwrite in case of carac only -> 1d10
// Overwrite in case of carac only -> 1d8
if (rollData.mode == "carac") {
conf.buttons = {
rolld8: {
@@ -96,7 +109,6 @@ export class HeritiersRollDialog extends Dialog {
activateListeners(html) {
super.activateListeners(html);
var dialog = this;
function onLoad() {
}
$(function () { onLoad(); });
@@ -105,19 +117,30 @@ export class HeritiersRollDialog extends Dialog {
this.rollData.sdValue = Number(event.currentTarget.value)
})
html.find('#caracKey').change(async (event) => {
//console.log("caracKey", event.currentTarget.value)
this.rollData.caracKey = String(event.currentTarget.value)
})
html.find('#bonus-malus-context').change((event) => {
this.rollData.bonusMalusContext = Number(event.currentTarget.value)
})
html.find('#useTricherie').change((event) => {
this.rollData.useTricherie = event.currentTarget.checked
html.find('#bonus-attaque-plusieurs').change((event) => {
this.rollData.bonusAttaquePlusieurs = Number(event.currentTarget.value)
})
html.find('#useSpecialite').change((event) => {
this.rollData.useSpecialite = event.currentTarget.checked
})
html.find('#useHeritage').change((event) => {
this.rollData.useHeritage = event.currentTarget.checked
html.find('#pouvoirPointsUsage').change((event) => {
this.rollData.pouvoirPointsUsage = Number(event.currentTarget.value)
})
html.find('#attaqueDos').change((event) => {
this.rollData.attaqueDos = event.currentTarget.checked
})
html.find('#bonus-attaque-seconde-arme').change((event) => {
this.rollData.secondeArme = String(event.currentTarget.value)
})
html.find('#attaque-cible').change((event) => {
this.rollData.attaqueCible = String(event.currentTarget.value)
})
}
}

View File

@@ -2,6 +2,45 @@
import { HeritiersCombat } from "./heritiers-combat.js";
import { HeritiersCommands } from "./heritiers-commands.js";
const __facesAdjacentes = {
"d8": {
1: [4, 8, 6],
2: [7, 5, 3],
3: [2, 8, 6],
4: [1, 5, 7],
5: [2, 4, 8],
6: [1, 7, 3],
7: [2, 4, 6],
8: [1, 3, 5]
},
"d10": {
1: [4, 6, 9, 7],
2: [6, 8, 5, 9],
3: [7, 5, 8, 10],
4: [10, 6, 7, 1],
5: [3, 9, 2, 8],
6: [1, 4, 2, 9],
7: [1, 3, 4, 10],
8: [2, 10, 5, 3],
9: [1, 5, 6, 2],
10: [8, 4, 3, 7]
},
"d12": {
1: [2, 3, 4, 5, 6],
2: [1, 6, 8, 12, 3],
3: [1, 4, 11, 12, 2],
4: [1, 5, 10, 11, 3],
5: [1, 6, 9, 10, 4],
6: [1, 2, 8, 9, 5],
7: [8, 9, 10, 11, 12],
8: [2, 6, 9, 7, 12],
9: [5, 10, 7, 8, 6],
10: [4, 11, 7, 9, 5],
11: [7, 10, 4, 3, 12],
12: [2, 8, 7, 11, 3]
}
}
/* -------------------------------------------- */
export class HeritiersUtility {
@@ -9,7 +48,7 @@ export class HeritiersUtility {
/* -------------------------------------------- */
static async init() {
Hooks.on('renderChatLog', (log, html, data) => HeritiersUtility.chatListeners(html))
Hooks.on("getChatLogEntryContext", (html, options) => HeritiersUtility.chatRollMenu(html, options))
/* Unused for Heitiers : Hooks.on("getChatMessageContextOptions", (html, options) => HeritiersUtility.chatRollMenu(html, options))*/
this.rollDataStore = {}
this.defenderStore = {}
@@ -65,6 +104,42 @@ export class HeritiersUtility {
const skills = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.competences")
this.skills = skills.map(i => i.toObject())
this.competencesMagie = this.skills.filter(s => s.system.profil == "magie")
game.settings.register("fvtt-les-heritiers", "heritiers-heritage", {
name: "Points d'héritage",
hint: "Points d'héritage du groupe",
scope: "world",
config: true,
default: 0,
type: Number
})
}
/* -------------------------------------------- */
static getSDSortValue(niveau) {
if (niveau <= 1) return 12;
if (niveau == 2) return 14;
if (niveau == 3) return 16;
if (niveau > 3) return 18;
return 18;
}
/* -------------------------------------------- */
static getCompetencesMagie() {
return this.competencesMagie
}
/* -------------------------------------------- */
static buildCompetencesMagie() {
let competences = foundry.utils.duplicate(this.getCompetencesMagie())
for (let comp of competences) {
// Calcul du rang
let rang = Math.round(comp.system.niveau / 2);
competences.system.rang = rang;
competences.system.rangGenericName = game.system.lesheritiers.config.rangName[rang];
competences.system.rangSpecificName = game.system.lesheritiers.config.rangNameSpecific[comp.Name][competences.system.rangGenericName];
}
}
/* -------------------------------------------- */
@@ -86,18 +161,29 @@ export class HeritiersUtility {
/* -------------------------------------------- */
static async chatListeners(html) {
html.on("click", '.predilection-reroll', async event => {
$(html).on("click", '.predilection-reroll', async event => {
let predIdx = $(event.currentTarget).data("predilection-index")
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "heritiers-roll")
let actor = this.getActorFromRollData(rollData)
await actor.setPredilectionUsed(rollData.competence._id, predIdx)
rollData.competence = duplicate(actor.getCompetence(rollData.competence._id))
rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
HeritiersUtility.rollHeritiers(rollData)
})
html.on("click", '.roll-chat-degat', async event => {
$(html).on("click", '.roll-tricherie-2', async event => {
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "heritiers-roll")
let actor = this.getActorFromRollData(rollData)
if (await actor.incDecTricherie(-2)) {
rollData.forcedValue = Number($(event.currentTarget).data("dice-value"))
HeritiersUtility.rollHeritiers(rollData)
}
})
$(html).on("click", '.roll-chat-degat', async event => {
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
let message = game.messages.get(messageId)
let rollData = message.getFlag("world", "heritiers-roll")
@@ -114,10 +200,9 @@ export class HeritiersUtility {
'systems/fvtt-les-heritiers/templates/partial-item-header.html',
'systems/fvtt-les-heritiers/templates/partial-item-description.html',
'systems/fvtt-les-heritiers/templates/partial-item-nav.html',
'systems/fvtt-les-heritiers/templates/partial-utile-skills.html',
'systems/fvtt-les-heritiers/templates/partial-list-niveau.html'
'systems/fvtt-les-heritiers/templates/partial-utile-skills.html'
]
return loadTemplates(templatePaths);
return foundry.applications.handlebars.loadTemplates(templatePaths);
}
/* -------------------------------------------- */
@@ -194,14 +279,14 @@ export class HeritiersUtility {
let id = rollData.rollId;
let oldRollData = this.rollDataStore[id] || {};
let newRollData = mergeObject(oldRollData, rollData);
let newRollData = foundry.utils.mergeObject(oldRollData, rollData);
this.rollDataStore[id] = newRollData;
}
/* -------------------------------------------- */
static saveRollData(rollData) {
game.socket.emit("system.fvtt-les-heritiers", {
name: "msg_update_roll", data: rollData
}); // Notify all other clients of the roll
}); // Notify all other clients of the roll
this.updateRollData(rollData);
}
@@ -212,7 +297,6 @@ export class HeritiersUtility {
/* -------------------------------------------- */
static onSocketMesssage(msg) {
//console.log("SOCKET MESSAGE", msg.name, game.user.character.id, msg.data.defenderId);
if (msg.name == "msg_update_defense_state") {
this.updateDefenseState(msg.data.defenderId, msg.data.rollId);
}
@@ -290,11 +374,12 @@ export class HeritiersUtility {
if (isTricherieHeritage) {
let resTab = [rollData.roll.terms[0].results[0].result, rollData.roll.terms[0].results[1].result, rollData.roll.terms[0].results[2].result]
rollData.diceResult = resTab[0] + "," + resTab[1] + "," + resTab[2]
let subResult = Math.max(Math.max(resTab[0], resTab[1]), resTab[2])
let foundryTotal = resTab[0] + resTab[1] + resTab[2]
if (resTab[1] == 1) { resTab[1] -= 4 }
if (resTab[2] == 1) { resTab[2] -= 6 }
if (resTab[2] == 2) { resTab[2] -= 7 }
rollData.finalResult = rollData.roll.total - subResult + Math.max(Math.max(resTab[0], resTab[1]), resTab[2])
rollData.diceValue = Math.max(Math.max(resTab[0], resTab[1]), resTab[2])
rollData.finalResult = rollData.roll.total - foundryTotal + rollData.diceValue
// Gestion des résultats spéciaux
resTab = resTab.sort()
@@ -319,8 +404,9 @@ export class HeritiersUtility {
} else {
rollData.finalResult = rollData.roll.total
let rollValue = rollData.roll.terms[0].results[0].result
rollData.diceResult = rollData.roll.terms[0].results[0].result
let rollValue = rollData.forcedValue || rollData.roll.terms[0].results[0].result
rollData.diceResult = rollValue
rollData.diceValue = rollValue
if (rollData.mainDice.includes("d10")) {
if (rollValue == 1) {
rollData.finalResult -= 3 + rollValue // substract 3 and the 1 value that has been added
@@ -331,18 +417,40 @@ export class HeritiersUtility {
rollData.finalResult -= 5 + rollValue // Remove also the dice result has it has been added already
}
}
if (!rollData.forcedValue) {
rollData.adjacentFaces = foundry.utils.duplicate(__facesAdjacentes[rollData.mainDice][rollData.diceValue])
}
}
}
/* -------------------------------------------- */
static computeArmeDegats(rollData, actor) {
rollData.degatsArme = rollData.arme.system.degats + rollData.marge
if (rollData.arme.system.categorie == "lourde") {
rollData.degatsArme += actor.system.caracteristiques.for.value
if (rollData.attaqueDeuxArmes != 0 && rollData.secondeArme) {
let secondeArme = actor.items.get(secondeArme)
if (secondeArme) {
rollData.degatsArme += secondeArme.system.degats
rollData.degatsArme += actor.system.caracteristiques.for.value
}
} else {
if (rollData.arme.system.categorie == "lourde") {
rollData.degatsArme += actor.system.caracteristiques.for.value
}
if (rollData.arme.system.categorie == "blanche" || rollData.arme.system.categorie == "improvise") {
rollData.degatsArme += Math.max(0, actor.system.caracteristiques.for.value - 2)
}
if (rollData.mode == "attaquecharge") {
rollData.degatsArme += 3
}
}
if (rollData.arme.system.categorie == "blanche" || rollData.arme.system.categorie == "improvise") {
rollData.degatsArme += Math.max(0, actor.system.caracteristiques.for.value - 2)
if (rollData.attaqueCible == "membre") {
rollData.degatsArme -= 2
}
if (rollData.attaqueCible == "main") {
rollData.degatsArme -= 3
}
if (rollData.attaqueCible == "tete") {
rollData.degatsArme *= 3
}
}
@@ -353,6 +461,10 @@ export class HeritiersUtility {
rollData.isSuccess = (rollData.finalResult >= seuil)
rollData.isCriticalSuccess = ((rollData.finalResult - seuil) >= 7)
rollData.isCriticalFailure = ((rollData.finalResult - seuil) <= -7)
// Si compétence > 0 et d8 -> echec critique impossible
if (rollData?.competence?.system.niveau > 0 && rollData?.mainDice == "d8") {
rollData.isCriticalFailure = false
}
}
}
@@ -366,28 +478,43 @@ export class HeritiersUtility {
this.computeArmeDegats(rollData, actor)
}
this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-cc-result.html`, rollData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-cc-result.html`, rollData)
}, rollData, "selfroll")
}
/* -------------------------------------------- */
static async displayAsssomer(rollData, actor, nomAttaque, etatAssomer, valeurDefense) {
rollData.defenderMode = nomAttaque
rollData.etatAssommer = etatAssomer
rollData.defenderValue = valeurDefense
rollData.marge = 0
this.computeMarge(rollData, valeurDefense)
rollData.dureeAssommer = (rollData.marge) ? rollData.marge * 2 : 1
this.createChatWithRollMode(rollData.alias, {
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-assommer-result.html`, rollData)
}, rollData, "selfroll")
}
/* -------------------------------------------- */
static async rollHeritiers(rollData) {
let actor = this.getActorFromRollData(rollData)
if (typeof (rollData.pvMalus) != "number") {
ui.notifications.warn("Votre personnage est Moribond(e). Aucun jet autorisé")
if (rollData.mode == "pouvoir" && actor.getPouvoirUsage(rollData.pouvoir._id) < rollData.pouvoirPointsUsage) {
ui.notifications.warn("Pas assez de points d'usage pour ce pouvoir.")
return
}
//rollData.actionImg = "systems/fvtt-les-heritiers/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
rollData.carac = duplicate(actor.system.caracteristiques[rollData.caracKey])
if (rollData.caracKey == "pre") rollData.caracKey = "pres"; // Patch tomanage wrong carac key
rollData.carac = foundry.utils.duplicate(actor.system.caracteristiques[rollData.caracKey])
if (rollData.useTricherie || rollData.useHeritage) {
rollData.diceFormula = "{1d8, 1d10, 1d12}"
if (rollData.forcedValue) {
rollData.diceFormula = rollData.forcedValue
} else {
rollData.diceFormula = "1" + rollData.mainDice + "kh1"
if (rollData.useTricherie || rollData.useHeritage) {
rollData.diceFormula = "{1d8, 1d10, 1d12}"
} else {
rollData.diceFormula = "1" + rollData.mainDice + "kh1"
}
}
let rangValue = 0
@@ -395,33 +522,84 @@ export class HeritiersUtility {
rangValue = rollData.rang.value
}
if (rollData.competence) {
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
let compmod = 0 // Bonus de compétence à 0 dans Les Heritiers
let specBonus = (rollData.useSpecialite) ? 1 : 0
rollData.diceFormula += `+${rollData.carac.value}+${rangValue}+${rollData.competence.system.niveau}+${specBonus}+${rollData.bonusMalusContext}+${compmod}`
} else if (rollData.pouvoirBase) {
rollData.diceFormula += `+${rollData.pouvoirBase.value}+${rangValue}+${rollData.bonusMalusContext}`
rollData.diceFormula += `+${rollData.carac.value}+${rollData.pouvoirBase.value}+${rangValue}+${rollData.bonusMalusContext}`
} else {
rollData.diceFormula += `+${rollData.carac.value}+${rangValue}+${rollData.bonusMalusContext}`
}
rollData.diceFormula += `+${rollData.pvMalus}`
let ruleMalus = 0
for (let malus of rollData.rulesMalus) {
ruleMalus += malus.value
}
rollData.diceFormula += `+${ruleMalus}`
let myRoll = new Roll(rollData.diceFormula).roll({ async: false })
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
rollData.roll = myRoll
console.log(">>>> ", myRoll)
this.computeResult(actor, rollData)
this.computeMarge(rollData, rollData.sdValue) // Calcul de la marge si seuil présent
// Gestion bonus attaque à plusieurs
let bonusAttaque = rollData.bonusAttaquePlusieurs
if (rollData.attaqueDos) {
bonusAttaque = 2
if (rollData.bonusAttaquePlusieurs) {
bonusAttaque = 3 // Valeur max, cf règle page 197
}
}
rollData.diceFormula += `+${bonusAttaque}`
// Gestion attaque avec 2 armes
if (rollData.attaqueDeuxArmes != 0) {
rollData.diceFormula += `+${rollData.attaqueDeuxArmes}`
}
// Gestion des attaques ciblées
if (rollData.attaqueCible != "none") {
if (rollData.attaqueCible == "membre") {
rollData.diceFormula += `-2`
}
if (rollData.attaqueCible == "main") {
rollData.diceFormula += `-3`
}
if (rollData.attaqueCible == "tete") {
rollData.diceFormula += `-6`
}
}
if (!rollData.noRoll) {
let myRoll = await new Roll(rollData.diceFormula).roll()
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
rollData.roll = foundry.utils.duplicate(myRoll)
console.log(">>>> ", myRoll)
this.computeResult(actor, rollData)
this.computeMarge(rollData, rollData.sdValue) // Calcul de la marge si seuil présent
}
if (rollData.mode == "init") {
actor.setFlag("world", "last-initiative", rollData.finalResult)
}
// Gestion pouvoir et points d'usage
if (rollData.mode == "pouvoir" || rollData.mode == "pouvoirpassif") {
actor.incDecPointsUsage(rollData.pouvoir._id, -rollData.pouvoirPointsUsage)
}
// Gestion sort et points d'âme
if (rollData.mode == "sort") {
if (rollData.spendEsprit) {
actor.inDecCarac("esp", -1)
} else {
actor.incDecPointsAme(-rollData.sortPointsAme)
if (rollData.sort.system.competence == "Magie du Clan") {
actor.incDecPV(-2)
}
}
}
this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
}, rollData)
if (rollData.defenderTokenId && rollData.arme) {
if (rollData.arme.system.categorie != "trait" && rollData.arme.system.categorie != "poing" && rollData.arme.system.categorie != "epaule" ) {
// Gestion attaque standard
if ((rollData.mode == "arme" || rollData.mode == "attaquebrutale" || rollData.mode == "attaquecharge") &&
rollData.defenderTokenId && rollData.arme) {
if (rollData.arme.system.categorie != "trait" && rollData.arme.system.categorie != "poing" && rollData.arme.system.categorie != "epaule") {
await this.displayUneDefense(rollData, actor, "Parade", rollData.defenderParade)
await this.displayUneDefense(rollData, actor, "Esquive", rollData.defenderEsquive)
} else if (rollData.sdValue) {
@@ -430,6 +608,13 @@ export class HeritiersUtility {
ui.notifications.warn("Pas de difficulté positionnée pour l'attaque à distance.")
}
}
// Gestion assomer
if (rollData.mode == "assommer" && rollData.defenderTokenId && rollData.arme) {
await this.displayAsssomer(rollData, actor, "Assommer", "Surprise", rollData.defenderResistancePhysique)
await this.displayAsssomer(rollData, actor, "Assommer", "Conscient, Résistance+6", rollData.defenderResistancePhysique + 6)
await this.displayAsssomer(rollData, actor, "Assommer", "Conscient, Parade", rollData.defenderParade)
await this.displayAsssomer(rollData, actor, "Assommer", "Conscient, Esquive", rollData.defenderEsquive + 6)
}
}
@@ -437,16 +622,16 @@ export class HeritiersUtility {
static async bonusRollHeritiers(rollData) {
rollData.bonusFormula = rollData.addedBonus
let bonusRoll = new Roll(rollData.bonusFormula).roll({ async: false })
let bonusRoll = await new Roll(rollData.bonusFormula).roll()
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
rollData.bonusRoll = bonusRoll
rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
rollData.finalResult += rollData.bonusRoll.total
this.computeResult(rollData)
this.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
}, rollData)
}
@@ -456,6 +641,10 @@ export class HeritiersUtility {
return game.users.filter(filter).map(user => user._id);
}
/* -------------------------------------------- */
static isArmeMelee(arme) {
return (arme.type == "arme" && (arme.system.categorie == "lourde" || arme.system.categorie == "blanche" || arme.system.categorie == "improvise"))
}
/* -------------------------------------------- */
static getWhisperRecipients(rollMode, name) {
switch (rollMode) {
@@ -473,7 +662,7 @@ export class HeritiersUtility {
/* -------------------------------------------- */
static blindMessageToGM(chatOptions) {
let chatGM = duplicate(chatOptions);
let chatGM = foundry.utils.duplicate(chatOptions);
chatGM.whisper = this.getUsers(user => user.isGM);
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
console.log("blindMessageToGM", chatGM);
@@ -537,11 +726,17 @@ export class HeritiersUtility {
/* -------------------------------------------- */
static getBasicRollData() {
let rollData = {
rollId: randomID(16),
rollId: foundry.utils.randomID(16),
rollMode: game.settings.get("core", "rollMode"),
sdList: game.system.lesheritiers.config.seuilsDifficulte,
sdValue: -1,
bonusMalusContext: 0
bonusAttaquePlusieurs: 0,
attaqueDeuxArmes: 0,
attaqueDos: false,
bonusMalusContext: 0,
attaqueCible: "none",
config: game.system.lesheritiers.config,
rulesMalus: []
}
return rollData
}
@@ -555,6 +750,7 @@ export class HeritiersUtility {
rollData.defenderName = defender.name
rollData.defenderParade = defender.getCurrentParade()
rollData.defenderEsquive = defender.getCurrentEsquive()
rollData.defenderResistancePhysique = defender.getCurrentResistancePhysique()
}
}
@@ -608,13 +804,13 @@ export class HeritiersUtility {
static chatRollMenu(html, options) {
let canApply = li => canvas.tokens.controlled.length && li.find(".heritiers-roll").length
let canApplyBA = function (li) {
let message = game.messages.get(li.attr("data-message-id"))
let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "heritiers-roll")
let actor = this.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getBonneAventure() > 0)
}
let canApplyPE = function (li) {
let message = game.messages.get(li.attr("data-message-id"))
let message = game.messages.get($(li).attr("data-message-id"))
let rollData = message.getFlag("world", "heritiers-roll")
let actor = this.getActorFromRollData(rollData)
return (!rollData.isReroll && actor.getEclat() > 0)
@@ -649,11 +845,11 @@ export class HeritiersUtility {
/* -------------------------------------------- */
static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id");
let msgTxt = "<p>Are you sure to remove this Item ?";
let msgTxt = "<p>Certain de supprimer cet item ?";
let buttons = {
delete: {
icon: '<i class="fas fa-check"></i>',
label: "Yes, remove it",
label: "Oui !",
callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
li.slideUp(200, () => actorSheet.render(false));
@@ -661,12 +857,12 @@ export class HeritiersUtility {
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel"
label: "Non !"
}
}
msgTxt += "</p>";
let d = new Dialog({
title: "Confirm removal",
title: "Confirmer la suppression",
content: msgTxt,
buttons: buttons,
default: "cancel"
@@ -674,21 +870,56 @@ export class HeritiersUtility {
d.render(true);
}
/************************************************************************************/
static async __create_talents_table() {
let compName = "fvtt-les-heritiers.talents-cellule"
const compData = await HeritiersUtility.loadCompendium(compName)
let talents = compData.map(i => i.toObject())
let htmlTab = "<table border='1'><tbody>";
for (let entryData of talents) {
console.log(entryData)
htmlTab += `<tr><td>@UUID[Compendium.${compName}.${entryData._id}]{${entryData.name}}</td>`
htmlTab += `<td>${entryData.system.description}</td>`;
//htmlTab += `<td>${entryData.system.resumebonus}</td>`;
htmlTab += "</tr>\n";
static loadSort() {
// Create afolder in the item directory if it doesn't exist
if (!game.folders.getName("Magie du Clan")) {
Folder.create({
name: "Magie du Clan",
type: "Item",
color: "#3b1361"
});
}
htmlTab += "</table>";
await JournalEntry.create({ name: 'Liste des Talents de Cellule', content: htmlTab });
// Load the srcdata/sorts-druidisme.json file
return fetch("systems/fvtt-les-heritiers/srcdata/sort_magieduclan.json")
.then(response => response.json())
.then(data => {
console.log("Sorts Magie du Clan loaded:", data);
this.sortDruidisme = data;
// Loop through the spell and create the "sort "item based on the JSON content
data.forEach(spell => {
spell.name = spell.name;
spell.type = "sort";
spell.system = {
niveau: spell.niveau,
competence: spell.competence,
carac1: spell.carac1,
carac2: spell.carac2,
description: spell.description,
ingredients: spell.ingredients,
portee: spell.portee,
duree: spell.duree,
concentration: spell.concentration,
critique: spell.critique,
resistance: spell.resistance,
coutactivation: spell.coutactivation
};
spell.img = "systems/fvtt-les-heritiers/assets/icons/sort.webp";
spell.folder = game.folders.getName("Magie du Clan").id;
// Create the item in the world
Item.create(spell)
.then(item => {
console.log("Sort created:", item);
})
.catch(error => {
console.error("Error creating sort item:", error);
});
})
})
.catch(error => {
console.error("Error loading druidism spells:", error);
return [];
});
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

View File

@@ -0,0 +1 @@
MANIFEST-000216

View File

View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.841305 7f12eeffd6c0 Recovering log #214
2025/08/12-23:22:09.894721 7f12eeffd6c0 Delete type=3 #212
2025/08/12-23:22:09.894801 7f12eeffd6c0 Delete type=0 #214
2025/08/12-23:24:44.447479 7f12edffb6c0 Level-0 table #219: started
2025/08/12-23:24:44.447513 7f12edffb6c0 Level-0 table #219: 0 bytes OK
2025/08/12-23:24:44.453775 7f12edffb6c0 Delete type=0 #217
2025/08/12-23:24:44.453928 7f12edffb6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.453962 7f12edffb6c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)

View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:12.134639 7f12ef7fe6c0 Recovering log #210
2025/08/12-22:03:12.178916 7f12ef7fe6c0 Delete type=3 #208
2025/08/12-22:03:12.178963 7f12ef7fe6c0 Delete type=0 #210
2025/08/12-23:20:29.693131 7f12edffb6c0 Level-0 table #215: started
2025/08/12-23:20:29.693153 7f12edffb6c0 Level-0 table #215: 0 bytes OK
2025/08/12-23:20:29.699501 7f12edffb6c0 Delete type=0 #213
2025/08/12-23:20:29.716914 7f12edffb6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.716949 7f12edffb6c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1 @@
MANIFEST-000216

View File

View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.953525 7f12ef7fe6c0 Recovering log #214
2025/08/12-23:22:09.999140 7f12ef7fe6c0 Delete type=3 #212
2025/08/12-23:22:09.999193 7f12ef7fe6c0 Delete type=0 #214
2025/08/12-23:24:44.461664 7f12edffb6c0 Level-0 table #219: started
2025/08/12-23:24:44.461699 7f12edffb6c0 Level-0 table #219: 0 bytes OK
2025/08/12-23:24:44.467731 7f12edffb6c0 Delete type=0 #217
2025/08/12-23:24:44.481357 7f12edffb6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.481393 7f12edffb6c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)

View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:12.234213 7f12eeffd6c0 Recovering log #210
2025/08/12-22:03:12.274961 7f12eeffd6c0 Delete type=3 #208
2025/08/12-22:03:12.275015 7f12eeffd6c0 Delete type=0 #210
2025/08/12-23:20:29.728859 7f12edffb6c0 Level-0 table #215: started
2025/08/12-23:20:29.728889 7f12edffb6c0 Level-0 table #215: 0 bytes OK
2025/08/12-23:20:29.734828 7f12edffb6c0 Delete type=0 #213
2025/08/12-23:20:29.748366 7f12edffb6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.758780 7f12edffb6c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1 @@
MANIFEST-000216

View File

View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.739184 7f12ef7fe6c0 Recovering log #214
2025/08/12-23:22:09.787438 7f12ef7fe6c0 Delete type=3 #212
2025/08/12-23:22:09.787511 7f12ef7fe6c0 Delete type=0 #214
2025/08/12-23:24:44.402280 7f12edffb6c0 Level-0 table #219: started
2025/08/12-23:24:44.402330 7f12edffb6c0 Level-0 table #219: 0 bytes OK
2025/08/12-23:24:44.408548 7f12edffb6c0 Delete type=0 #217
2025/08/12-23:24:44.427555 7f12edffb6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.427616 7f12edffb6c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)

View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:12.046235 7f12effff6c0 Recovering log #210
2025/08/12-22:03:12.089332 7f12effff6c0 Delete type=3 #208
2025/08/12-22:03:12.089400 7f12effff6c0 Delete type=0 #210
2025/08/12-23:20:29.686986 7f12edffb6c0 Level-0 table #215: started
2025/08/12-23:20:29.687016 7f12edffb6c0 Level-0 table #215: 0 bytes OK
2025/08/12-23:20:29.693011 7f12edffb6c0 Delete type=0 #213
2025/08/12-23:20:29.716900 7f12edffb6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.716935 7f12edffb6c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)

Binary file not shown.

BIN
packs/avantages/000179.ldb Normal file

Binary file not shown.

View File

1
packs/avantages/CURRENT Normal file
View File

@@ -0,0 +1 @@
MANIFEST-000216

0
packs/avantages/LOCK Normal file
View File

8
packs/avantages/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.532032 7f12ef7fe6c0 Recovering log #214
2025/08/12-23:22:09.583613 7f12ef7fe6c0 Delete type=3 #212
2025/08/12-23:22:09.583679 7f12ef7fe6c0 Delete type=0 #214
2025/08/12-23:24:44.414597 7f12edffb6c0 Level-0 table #219: started
2025/08/12-23:24:44.414620 7f12edffb6c0 Level-0 table #219: 0 bytes OK
2025/08/12-23:24:44.420650 7f12edffb6c0 Delete type=0 #217
2025/08/12-23:24:44.427592 7f12edffb6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.427633 7f12edffb6c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)

8
packs/avantages/LOG.old Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:11.912233 7f12effff6c0 Recovering log #210
2025/08/12-22:03:11.957119 7f12effff6c0 Delete type=3 #208
2025/08/12-22:03:11.957180 7f12effff6c0 Delete type=0 #210
2025/08/12-23:20:29.661087 7f12edffb6c0 Level-0 table #215: started
2025/08/12-23:20:29.661131 7f12edffb6c0 Level-0 table #215: 0 bytes OK
2025/08/12-23:20:29.667177 7f12edffb6c0 Delete type=0 #213
2025/08/12-23:20:29.686825 7f12edffb6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.686871 7f12edffb6c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)

Binary file not shown.

BIN
packs/capacites/000179.ldb Normal file

Binary file not shown.

View File

1
packs/capacites/CURRENT Normal file
View File

@@ -0,0 +1 @@
MANIFEST-000216

0
packs/capacites/LOCK Normal file
View File

8
packs/capacites/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.678492 7f12eeffd6c0 Recovering log #214
2025/08/12-23:22:09.736918 7f12eeffd6c0 Delete type=3 #212
2025/08/12-23:22:09.737004 7f12eeffd6c0 Delete type=0 #214
2025/08/12-23:24:44.408642 7f12edffb6c0 Level-0 table #219: started
2025/08/12-23:24:44.408666 7f12edffb6c0 Level-0 table #219: 0 bytes OK
2025/08/12-23:24:44.414489 7f12edffb6c0 Delete type=0 #217
2025/08/12-23:24:44.427576 7f12edffb6c0 Manual compaction at level-0 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.427625 7f12edffb6c0 Manual compaction at level-1 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)

8
packs/capacites/LOG.old Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:12.004614 7f12ef7fe6c0 Recovering log #210
2025/08/12-22:03:12.044171 7f12ef7fe6c0 Delete type=3 #208
2025/08/12-22:03:12.044244 7f12ef7fe6c0 Delete type=0 #210
2025/08/12-23:20:29.673446 7f12edffb6c0 Level-0 table #215: started
2025/08/12-23:20:29.673471 7f12edffb6c0 Level-0 table #215: 0 bytes OK
2025/08/12-23:20:29.680450 7f12edffb6c0 Delete type=0 #213
2025/08/12-23:20:29.686854 7f12edffb6c0 Manual compaction at level-0 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.686885 7f12edffb6c0 Manual compaction at level-1 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1 @@
MANIFEST-000219

0
packs/competences/LOCK Normal file
View File

8
packs/competences/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.475992 7f12effff6c0 Recovering log #217
2025/08/12-23:22:09.530135 7f12effff6c0 Delete type=3 #215
2025/08/12-23:22:09.530186 7f12effff6c0 Delete type=0 #217
2025/08/12-23:24:44.420774 7f12edffb6c0 Level-0 table #222: started
2025/08/12-23:24:44.420800 7f12edffb6c0 Level-0 table #222: 0 bytes OK
2025/08/12-23:24:44.427391 7f12edffb6c0 Delete type=0 #220
2025/08/12-23:24:44.427605 7f12edffb6c0 Manual compaction at level-0 from '!folders!FBCujRu055QLePB2' @ 72057594037927935 : 1 .. '!items!zEl2NQsnCpELVWzh' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.427641 7f12edffb6c0 Manual compaction at level-1 from '!folders!FBCujRu055QLePB2' @ 72057594037927935 : 1 .. '!items!zEl2NQsnCpELVWzh' @ 0 : 0; will stop at (end)

View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:11.864963 7f12ee7fc6c0 Recovering log #213
2025/08/12-22:03:11.909538 7f12ee7fc6c0 Delete type=3 #211
2025/08/12-22:03:11.909606 7f12ee7fc6c0 Delete type=0 #213
2025/08/12-23:20:29.667307 7f12edffb6c0 Level-0 table #218: started
2025/08/12-23:20:29.667339 7f12edffb6c0 Level-0 table #218: 0 bytes OK
2025/08/12-23:20:29.673335 7f12edffb6c0 Delete type=0 #216
2025/08/12-23:20:29.686842 7f12edffb6c0 Manual compaction at level-0 from '!folders!FBCujRu055QLePB2' @ 72057594037927935 : 1 .. '!items!zEl2NQsnCpELVWzh' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.686878 7f12edffb6c0 Manual compaction at level-1 from '!folders!FBCujRu055QLePB2' @ 72057594037927935 : 1 .. '!items!zEl2NQsnCpELVWzh' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1 @@
MANIFEST-000216

0
packs/desavantages/LOCK Normal file
View File

8
packs/desavantages/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.585932 7f12ee7fc6c0 Recovering log #214
2025/08/12-23:22:09.675400 7f12ee7fc6c0 Delete type=3 #212
2025/08/12-23:22:09.675482 7f12ee7fc6c0 Delete type=0 #214
2025/08/12-23:24:44.434363 7f12edffb6c0 Level-0 table #219: started
2025/08/12-23:24:44.434400 7f12edffb6c0 Level-0 table #219: 0 bytes OK
2025/08/12-23:24:44.440785 7f12edffb6c0 Delete type=0 #217
2025/08/12-23:24:44.453906 7f12edffb6c0 Manual compaction at level-0 from '!items!2QqvtClSVnh5ejXu' @ 72057594037927935 : 1 .. '!items!xzRJ6JP1HqoqxLdj' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.453946 7f12edffb6c0 Manual compaction at level-1 from '!items!2QqvtClSVnh5ejXu' @ 72057594037927935 : 1 .. '!items!xzRJ6JP1HqoqxLdj' @ 0 : 0; will stop at (end)

View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:11.960107 7f12eeffd6c0 Recovering log #210
2025/08/12-22:03:12.002183 7f12eeffd6c0 Delete type=3 #208
2025/08/12-22:03:12.002239 7f12eeffd6c0 Delete type=0 #210
2025/08/12-23:20:29.680595 7f12edffb6c0 Level-0 table #215: started
2025/08/12-23:20:29.680637 7f12edffb6c0 Level-0 table #215: 0 bytes OK
2025/08/12-23:20:29.686672 7f12edffb6c0 Delete type=0 #213
2025/08/12-23:20:29.686864 7f12edffb6c0 Manual compaction at level-0 from '!items!2QqvtClSVnh5ejXu' @ 72057594037927935 : 1 .. '!items!xzRJ6JP1HqoqxLdj' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.686893 7f12edffb6c0 Manual compaction at level-1 from '!items!2QqvtClSVnh5ejXu' @ 72057594037927935 : 1 .. '!items!xzRJ6JP1HqoqxLdj' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1 @@
MANIFEST-000015

0
packs/magie-sorts/LOCK Normal file
View File

8
packs/magie-sorts/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.790283 7f12effff6c0 Recovering log #12
2025/08/12-23:22:09.838285 7f12effff6c0 Delete type=3 #10
2025/08/12-23:22:09.838351 7f12effff6c0 Delete type=0 #12
2025/08/12-23:24:44.440907 7f12edffb6c0 Level-0 table #18: started
2025/08/12-23:24:44.440937 7f12edffb6c0 Level-0 table #18: 0 bytes OK
2025/08/12-23:24:44.447316 7f12edffb6c0 Delete type=0 #16
2025/08/12-23:24:44.453918 7f12edffb6c0 Manual compaction at level-0 from '!folders!NE8l8XLXdVUw0aZm' @ 72057594037927935 : 1 .. '!items!zjQQhJpujpdbG4zl' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.453954 7f12edffb6c0 Manual compaction at level-1 from '!folders!NE8l8XLXdVUw0aZm' @ 72057594037927935 : 1 .. '!items!zjQQhJpujpdbG4zl' @ 0 : 0; will stop at (end)

15
packs/magie-sorts/LOG.old Normal file
View File

@@ -0,0 +1,15 @@
2025/08/12-22:03:12.092093 7f12ee7fc6c0 Recovering log #8
2025/08/12-22:03:12.132029 7f12ee7fc6c0 Delete type=3 #6
2025/08/12-22:03:12.132100 7f12ee7fc6c0 Delete type=0 #8
2025/08/12-23:20:29.699933 7f12edffb6c0 Level-0 table #13: started
2025/08/12-23:20:29.704268 7f12edffb6c0 Level-0 table #13: 15679 bytes OK
2025/08/12-23:20:29.710447 7f12edffb6c0 Delete type=0 #11
2025/08/12-23:20:29.716925 7f12edffb6c0 Manual compaction at level-0 from '!folders!NE8l8XLXdVUw0aZm' @ 72057594037927935 : 1 .. '!items!zjQQhJpujpdbG4zl' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.716961 7f12edffb6c0 Manual compaction at level-1 from '!folders!NE8l8XLXdVUw0aZm' @ 72057594037927935 : 1 .. '!items!zjQQhJpujpdbG4zl' @ 0 : 0; will stop at '!items!vO9OLd8LQMPNckkU' @ 112 : 1
2025/08/12-23:20:29.716966 7f12edffb6c0 Compacting 1@1 + 1@2 files
2025/08/12-23:20:29.721393 7f12edffb6c0 Generated table #14@1: 89 keys, 73118 bytes
2025/08/12-23:20:29.721441 7f12edffb6c0 Compacted 1@1 + 1@2 files => 73118 bytes
2025/08/12-23:20:29.728469 7f12edffb6c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2025/08/12-23:20:29.728621 7f12edffb6c0 Delete type=2 #5
2025/08/12-23:20:29.728770 7f12edffb6c0 Delete type=2 #13
2025/08/12-23:20:29.742024 7f12edffb6c0 Manual compaction at level-1 from '!items!vO9OLd8LQMPNckkU' @ 112 : 1 .. '!items!zjQQhJpujpdbG4zl' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@@ -10,7 +10,7 @@
{"name":"Élémentaire majeur (Légendaire)","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"metamorphose","masquetype":"demasque","niveau":"legendaire","activation":"2 points dusage au lieu dun, ainsi qu1 point temporaire de Constitution.","istest":false,"feeriemasque":"feerie","zoneffet":"","testautre":"Attaque avec des crocs","carac":"pres","duree":"une scène","cibles":"","effet":"transformation en élémentaire de son\naffinité ; absorbe Féérie points de dégâts sauf contre des attaques efficaces (élément contraire ou argent). \nAttaques : effectuées avec Mêlée + AGI ; dégâts élémentaires = Féérie + MR (pas de bonus de Force).\n","portee":"","resistance":"aucune","resistanceautre":"Esquive / test dopposition de Mêlée ou Ressort + Force pour se dégager.","isvirulence":false,"virulence":"","description":"<p>Le djinn se transforme en &eacute;l&eacute;mentaire majeur de tr&egrave;s grande taille (environ 2,5 m&egrave;tres de haut et d&rsquo;envergure), avec d&rsquo;importants pouvoirs de contr&ocirc;le de son &eacute;l&eacute;ment de pr&eacute;dilection : tornade de sable ou d&rsquo;eau, contr&ocirc;le du feu, temp&ecirc;te, etc. Ce contr&ocirc;le &eacute;l&eacute;mentaire est de forme relativement libre selon la vraisemblance et ce qu&rsquo;autorise le Docte et sa puissance est comparable au Pouvoir profond du ph&eacute;nix Contr&ocirc;le du feu (voir p. 264). Il utilise ses longs bras (1,5 m) pour porter des attaques &eacute;l&eacute;mentaires avec des effets critiques li&eacute;s : le feu enflamme les v&ecirc;tements, l&rsquo;eau et le sable &eacute;tourdissent, l&rsquo;air ajoute des d&eacute;g&acirc;ts de projection ou de chute, etc.</p>\n<p>Les attaques physiques port&eacute;es contre lui sont r&eacute;duites d&rsquo;autant de points de d&eacute;g&acirc;ts que son rang de F&eacute;&eacute;rie, sauf si elles font intervenir de l&rsquo;argent. Toutefois, projeter sur lui une grande quantit&eacute; (1 m3 environ) d&rsquo;un &eacute;l&eacute;ment auquel il est vuln&eacute;rable (l&rsquo;eau pour les djinns du feu, la terre ou le sable pour les djinns de l&rsquo;eau, le vent pour les djinns de sable, le feu pour les djinns de l&rsquo;air) pourra contraindre un djinn l&eacute;gendaire &agrave; abandonner sa forme d&rsquo;&Eacute;l&eacute;mentaire majeur.</p>","zoneeffet":""},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.18","coreVersion":"10.291","createdTime":1678214487456,"modifiedTime":1678303349575,"lastModifiedBy":"xGnwWyEbO2k89UsP"},"_id":"4v8Z2ThDSwwWb0DU","folder":null,"sort":0,"ownership":{"default":0,"xGnwWyEbO2k89UsP":3}}
{"name":"Psychotopie","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"actif","masquetype":"masque","niveau":"normal","activation":"Féérie + Esprit et une transe de 5 minutes au moins, contre un SD variable selon la taille et les événements se déroulant dans le lieu (une chambre isolée depuis des années : SD10 ; un immeuble rempli d'habitants dans lequel il y a fréquemment du passage : SD 20).","istest":true,"feeriemasque":"feerie","zoneffet":"","testautre":"","carac":"esp","duree":"","cibles":"un seul lieu","effet":"apprend des informations marquantes sur un lieu","portee":"un lieu (pas plus grand quun bâtiment)","resistance":"aucune","resistanceautre":"","isvirulence":false,"virulence":"","description":"<p><strong>Type de Pouvoir : </strong>actif.</p>\n<p><strong>Activation : </strong>F&eacute;&eacute;rie + Esprit et une transe de 5 minutes au moins, contre un SD variable selon la taille et les &eacute;v&eacute;nements se d&eacute;roulant dans le lieu (une chambre isol&eacute;e depuis des ann&eacute;es : SD10 ; un immeuble rempli d'habitants dans lequel il y a fr&eacute;quemment du passage : SD 20).</p>\n<p><strong>Cibles :</strong> un seul lieu.</p>\n<p><strong>Effet :</strong> apprend des informations marquantes sur un lieu.</p>\n<p><strong>Zone d&rsquo;effet : </strong>un lieu (pas plus grand qu&rsquo;un b&acirc;timent).</p>\n<p>La cr&eacute;ature est capable, apr&egrave;s avoir &eacute;tabli un contact mental avec un lieu par une transe prolong&eacute;e, de r&eacute;cup&eacute;rer des informations sur ce qui s&rsquo;y est pass&eacute;. En fonction de sa MR, elle aura acc&egrave;s &agrave; diff&eacute;rents d&eacute;tails : paroles prononc&eacute;es r&eacute;cemment ou qui ont marqu&eacute; l&rsquo;atmosph&egrave;re, traits physiques g&eacute;n&eacute;raux de protagonistes pr&eacute;sents (odeur, poids, etc.). Les &eacute;v&eacute;nements marquants (meurtre, incendie, etc.) impr&egrave;gnent davantage un lieu et faciliteront le SD du test &agrave; la discr&eacute;tion du Docte mais peuvent rester imperceptibles dans la masse d&rsquo;informations si le lieu est vaste et fr&eacute;quent&eacute;.</p>","zoneeffet":""},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.18","coreVersion":"10.291","createdTime":1678280900282,"modifiedTime":1678314472642,"lastModifiedBy":"3jSVOAZZNxArxD7h"},"_id":"672N9fk2mJahvJIL","folder":null,"sort":0,"ownership":{"default":0,"3jSVOAZZNxArxD7h":3}}
{"name":"Doppelgänger universel","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"metamorphose","masquetype":"demasque","niveau":"legendaire","activation":"test de Masque + Féérie contre un SD de 10 + Prestance de la personne imitée modifiée par la précision de la source servant à limitation (connaissance personnelle profonde = bonus de + 3, connaissance personnelle superficielle = + 1, photographie = - 1, illustration fidèle = - 2).","istest":true,"feeriemasque":"Masque","zoneffet":"","testautre":"","carac":"agi","duree":" une scène.","cibles":"","effet":"prendre lapparence de nimporte qui.","portee":"","resistance":"autre","resistanceautre":"percer à jour le protys légendaire nécessite un test de Sensibilité (Psychologie) + Perception en opposition au test de Féérie + Masque du protys.","isvirulence":false,"virulence":"","description":"<p><strong>Type de Pouvoir : </strong>d&eacute;masqu&eacute; ; actif puis m&eacute;tamorphose pour adopter l&rsquo;identit&eacute; imit&eacute;e.</p>\n<p><strong>Activation : </strong>test de Masque + F&eacute;&eacute;rie contre un SD de 10 + Prestance de la personne imit&eacute;e modifi&eacute;e par la pr&eacute;cision de la source servant &agrave; l&rsquo;imitation (connaissance personnelle profonde = bonus de + 3, connaissance personnelle superficielle = + 1, photographie = - 1, illustration fid&egrave;le = - 2).</p>\n<p><strong>Dur&eacute;e : </strong>une sc&egrave;ne.</p>\n<p><strong>Effet : </strong>prendre l&rsquo;apparence de n&rsquo;importe qui.</p>\n<p><strong>R&eacute;sistance : </strong>percer &agrave; jour le protys l&eacute;gendaire n&eacute;cessite un test de Sensibilit&eacute; (Psychologie) + Perception en opposition au test de F&eacute;&eacute;rie + Masque du protys.</p>\n<p>Le protys l&eacute;gendaire est capable, pendant une sc&egrave;ne, de prendre l&rsquo;apparence (traits du visage, corpulence, etc.) de n&rsquo;importe quel individu dont il aurait vu le visage en r&eacute;alit&eacute; ou en photographie, voire sur une illustration suffisamment fid&egrave;le. Il peut m&ecirc;me imiter une f&eacute;e avec ses capacit&eacute;s naturelles (ailes, crocs, pelage, etc.) au point de pouvoir s&rsquo;en servir normalement (il pourra ainsi voler, utiliser une queue pr&eacute;hensile, etc.). Il n&rsquo;aura en revanche pas acc&egrave;s aux Pouvoirs de la f&eacute;e imit&eacute;e.</p>","zoneeffet":""},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.21","coreVersion":"10.291","createdTime":1678354656026,"modifiedTime":1678356879899,"lastModifiedBy":"3jSVOAZZNxArxD7h"},"_id":"67ECitVegs2SFn2g","folder":null,"sort":0,"ownership":{"default":0,"3jSVOAZZNxArxD7h":3}}
{"name":"Souffle enflammé","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"actif","masquetype":"masque","niveau":"normal","activation":" test de Féérie + Agilité ou Précision.","istest":false,"feeriemasque":"feerie","zoneffet":"","testautre":"","carac":"pre","duree":"","cibles":"une seule.","effet":"souffle de feu de dégâts = 2 + MR","portee":"Féérie mètres.","resistance":"autre","resistanceautre":"Esquive","isvirulence":false,"virulence":"","description":"<p><strong>Type de Pouvoir : </strong>actif.</p>\n<p><strong>Activation : </strong>test de F&eacute;&eacute;rie + Agilit&eacute; ou Pr&eacute;cision.</p>\n<p><strong>Cibles : </strong>une seule.</p>\n<p><strong>Effet : </strong>souffle de feu de d&eacute;g&acirc;ts = 2 + MR.</p>\n<p><strong>Port&eacute;e : </strong>F&eacute;&eacute;rie m&egrave;tres.</p>\n<p><strong>R&eacute;sistance : </strong>Esquive.</p>\n<p>La cr&eacute;ature souffle un jet de feu capables d&rsquo;enflammer les mat&eacute;riaux combustibles (papier, tissus, bois) et faire des d&eacute;g&acirc;ts sur une cible. Cette derni&egrave;re peut &ecirc;tre durablement enflamm&eacute;e si les d&eacute;g&acirc;ts subis sont de 8 ou plus (en cumulant les attaques). La cible subira alors 1 point de d&eacute;g&acirc;t par tour jusqu&rsquo;&agrave; ce que les flammes soient &eacute;teintes par la cible ou un alli&eacute;. En&nbsp; as de r&eacute;ussite critique du Souffle enflamm&eacute;, la cible peut perdre, en plus de points de Vie, un point de Prestance permanent &agrave; moins d&rsquo;&ecirc;tre soign&eacute;e imm&eacute;diatement.</p>","zoneeffet":""},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.18","coreVersion":"10.291","createdTime":1678293330850,"modifiedTime":1678314472644,"lastModifiedBy":"3jSVOAZZNxArxD7h"},"_id":"68wj1CJt4oipIedV","folder":null,"sort":0,"ownership":{"default":0,"3jSVOAZZNxArxD7h":3}}
{"name":"Souffle enflammé","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"actif","masquetype":"masque","niveau":"normal","activation":" test de Féérie + Agilité ou Précision.","istest":true,"feeriemasque":"feerie","zoneffet":"","testautre":"","carac":"agi","duree":"","cibles":"une seule.","effet":"souffle de feu de dégâts = 2 + MR","portee":"Féérie mètres.","resistance":"autre","resistanceautre":"Esquive","isvirulence":false,"virulence":"","description":"<p><strong>Type de Pouvoir : </strong>actif.</p>\n<p><strong>Activation : </strong>test de F&eacute;&eacute;rie + Agilit&eacute; ou Pr&eacute;cision.</p>\n<p><strong>Cibles : </strong>une seule.</p>\n<p><strong>Effet : </strong>souffle de feu de d&eacute;g&acirc;ts = 2 + MR.</p>\n<p><strong>Port&eacute;e : </strong>F&eacute;&eacute;rie m&egrave;tres.</p>\n<p><strong>R&eacute;sistance : </strong>Esquive.</p>\n<p>La cr&eacute;ature souffle un jet de feu capables d&rsquo;enflammer les mat&eacute;riaux combustibles (papier, tissus, bois) et faire des d&eacute;g&acirc;ts sur une cible. Cette derni&egrave;re peut &ecirc;tre durablement enflamm&eacute;e si les d&eacute;g&acirc;ts subis sont de 8 ou plus (en cumulant les attaques). La cible subira alors 1 point de d&eacute;g&acirc;t par tour jusqu&rsquo;&agrave; ce que les flammes soient &eacute;teintes par la cible ou un alli&eacute;. En&nbsp; as de r&eacute;ussite critique du Souffle enflamm&eacute;, la cible peut perdre, en plus de points de Vie, un point de Prestance permanent &agrave; moins d&rsquo;&ecirc;tre soign&eacute;e imm&eacute;diatement.</p>","zoneeffet":""},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.1.3","coreVersion":"10.291","createdTime":1678293330850,"modifiedTime":1682875304779,"lastModifiedBy":"xGnwWyEbO2k89UsP"},"_id":"68wj1CJt4oipIedV","folder":null,"sort":0,"ownership":{"default":0,"3jSVOAZZNxArxD7h":3}}
{"name":"Chute indolore","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"passif","masquetype":"masque","niveau":"normal","activation":"","istest":false,"feeriemasque":"feerie","zoneffet":"","testautre":"","carac":"pres","duree":"","cibles":"","effet":"la hauteur dune chute est réduite de Féérie x 4 m pour le calcul de son SD.","portee":"","resistance":"aucune","resistanceautre":"Celle de la structure ou de lobjet (voir p. 204).","isvirulence":false,"virulence":"","description":"<p>La cr&eacute;ature est capable d&rsquo;affronter les pires chutes sans se faire mal. Lorsque ce Pouvoir est actif, on soustrait F&eacute;&eacute;rie x 4 m &agrave; la hauteur de la chute pour le calcul de son SD.</p>\n<p><strong>Exemple</strong> : <em>si une cr&eacute;ature a 4 en F&eacute;&eacute;rie et chute de 20 m, c&rsquo;est comme si elle ne chutait que de 4 m (elle ignore 4 x 4 = 16 m). Le SD de son test de Ressort + Agilit&eacute; pour limiter les d&eacute;g&acirc;ts de la chute sera &eacute;gal &agrave; 8 au lieu de 22 (voir le tableau des chutes, p. 190).</em></p>"},"effects":[],"flags":{"core":{"sourceId":"Item.745Qz4iEGB7RnBPk"}},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.18","coreVersion":"10.291","createdTime":1678214487456,"modifiedTime":1678303349644,"lastModifiedBy":"xGnwWyEbO2k89UsP"},"_id":"6v3s1I0blnPJHipG","folder":null,"sort":0,"ownership":{"default":0,"xGnwWyEbO2k89UsP":3}}
{"name":"Charme","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"actif","masquetype":"masque","niveau":"normal","activation":"test de Féérie + Prestance.","istest":true,"feeriemasque":"feerie","zoneffet":"","testautre":"","carac":"pres","duree":"Féérie + MR jours.","cibles":" une cible.","effet":"la cible tombe amoureuse.","portee":"conversation.","resistance":"psychiquepassive","resistanceautre":"Celle de la structure ou de lobjet (voir p. 204).","isvirulence":false,"virulence":"","description":"<p>La cr&eacute;ature est capable, au cours d&rsquo;une conversation, de faire tomber sa cible follement amoureuse d&rsquo;elle si elle ne R&eacute;siste pas. La cible, une fois &eacute;prise de la cr&eacute;ature, aura du mal &agrave; lui refuser quelque chose mais ne fera rien qui puisse directement nuire &agrave; ses jours et ira rarement contre son code moral.</p>\n<p>Pour la pousser &agrave; une telle extr&eacute;mit&eacute;, la cr&eacute;ature devra obtenir une r&eacute;ussite critique ou r&eacute;ussir un test de Rh&eacute;torique ou S&eacute;duction de SD 15 minimum (au jugement du Docte) b&eacute;n&eacute;ficiant d&rsquo;un bonus &eacute;gal &agrave; la MR du Pouvoir.</p>"},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.18","coreVersion":"10.291","createdTime":1678214487456,"modifiedTime":1678303349644,"lastModifiedBy":"xGnwWyEbO2k89UsP"},"_id":"745Qz4iEGB7RnBPk","folder":null,"sort":0,"ownership":{"default":0,"xGnwWyEbO2k89UsP":3}}
{"name":"Sujet qui fâche","type":"pouvoir","img":"systems/fvtt-les-heritiers/assets/icons/power.webp","system":{"pouvoirtype":"actif","masquetype":"masque","niveau":"normal","activation":"test de Féérie + Esprit.","istest":true,"feeriemasque":"feerie","zoneffet":"","testautre":"","carac":"esp","duree":"","cibles":" une cible","effet":"fait deviner ce qui fâche quelquun ; fait perdre automatiquement un point de Sang-froid quand ce sujet est abordé et pousse à la colère.","portee":" à vue","resistance":"autre","resistanceautre":"psychique pour ne pas être percé à jour puis Fortitude + Sang-froid pour garder le contrôle contre un SD difficile (13 ou plus) fixé par le Docte.","isvirulence":false,"virulence":"","description":"<p><strong>Type de Pouvoir :</strong> actif.</p>\n<p><strong>Activation : </strong>test de F&eacute;&eacute;rie + Esprit.</p>\n<p><strong>Cibles : </strong>une cible.</p>\n<p><strong>Effet :</strong> fait deviner ce qui f&acirc;che quelqu&rsquo;un ; fait perdre automatiquement un point de Sang-froid quand ce sujet est abord&eacute; et pousse &agrave; la col&egrave;re.</p>\n<p><strong>Port&eacute;e :</strong> &agrave; vue.</p>\n<p><strong>R&eacute;sistance : </strong>psychique pour ne pas &ecirc;tre perc&eacute; &agrave; jour puis Fortitude + Sang-froid pour garder le contr&ocirc;le contre un SD difficile (13 ou plus) fix&eacute; par le Docte.</p>\n<p>Lorsqu&rsquo;elle active ce Pouvoir, la cr&eacute;ature peut, en observant son interlocuteur (il peut avoir un bonus d&rsquo;apr&egrave;s des &eacute;l&eacute;ments qu&rsquo;il conna&icirc;t), trouver instinctivement le sujet qui va le f&acirc;cher et, selon son caract&egrave;re, le mettre en col&egrave;re ou le faire fuir en rougissant. Ce peut &ecirc;tre un complexe, une histoire louche, une phobie&hellip; Toutefois, elle n&rsquo;est pas oblig&eacute;e d&rsquo;en faire usage : le Docte doit juste lui communiquer le sujet susceptible de froisser la personne en question.<br>Si la victime R&eacute;siste, la cr&eacute;ature n&rsquo;est pas s&ucirc;re du &laquo; sujet qui f&acirc;che &raquo;. En cas de succ&egrave;s du Pouvoir, si la cr&eacute;ature se sert du Sujet qui f&acirc;che, la cible perd automatiquement un point de Sang-froid (effet non cumulable) et doit r&eacute;ussir &agrave; se ma&icirc;triser pour ne pas montrer sa col&egrave;re ou sa g&ecirc;ne. Toutefois, la victime ne peut que ma&icirc;triser ses r&eacute;actions apparentes (et bouillir int&eacute;rieurement).</p>","zoneeffet":""},"effects":[],"flags":{},"_stats":{"systemId":"fvtt-les-heritiers","systemVersion":"10.0.18","coreVersion":"10.291","createdTime":1678313273572,"modifiedTime":1678314472645,"lastModifiedBy":"3jSVOAZZNxArxD7h"},"_id":"7BsyUzwv31oxldYQ","folder":null,"sort":0,"ownership":{"default":0,"3jSVOAZZNxArxD7h":3}}

BIN
packs/pouvoirs/000180.ldb Normal file

Binary file not shown.

View File

1
packs/pouvoirs/CURRENT Normal file
View File

@@ -0,0 +1 @@
MANIFEST-000217

0
packs/pouvoirs/LOCK Normal file
View File

8
packs/pouvoirs/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:09.898470 7f12ee7fc6c0 Recovering log #215
2025/08/12-23:22:09.950596 7f12ee7fc6c0 Delete type=3 #213
2025/08/12-23:22:09.950662 7f12ee7fc6c0 Delete type=0 #215
2025/08/12-23:24:44.427852 7f12edffb6c0 Level-0 table #220: started
2025/08/12-23:24:44.427889 7f12edffb6c0 Level-0 table #220: 0 bytes OK
2025/08/12-23:24:44.434222 7f12edffb6c0 Delete type=0 #218
2025/08/12-23:24:44.453893 7f12edffb6c0 Manual compaction at level-0 from '!items!19r9ijZUyvnlIqgm' @ 72057594037927935 : 1 .. '!items!zON0h5SjFyANjPnA' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.453937 7f12edffb6c0 Manual compaction at level-1 from '!items!19r9ijZUyvnlIqgm' @ 72057594037927935 : 1 .. '!items!zON0h5SjFyANjPnA' @ 0 : 0; will stop at (end)

8
packs/pouvoirs/LOG.old Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:12.180814 7f12effff6c0 Recovering log #211
2025/08/12-22:03:12.231131 7f12effff6c0 Delete type=3 #209
2025/08/12-22:03:12.231199 7f12effff6c0 Delete type=0 #211
2025/08/12-23:20:29.710650 7f12edffb6c0 Level-0 table #216: started
2025/08/12-23:20:29.710689 7f12edffb6c0 Level-0 table #216: 0 bytes OK
2025/08/12-23:20:29.716754 7f12edffb6c0 Delete type=0 #214
2025/08/12-23:20:29.716942 7f12edffb6c0 Manual compaction at level-0 from '!items!19r9ijZUyvnlIqgm' @ 72057594037927935 : 1 .. '!items!zON0h5SjFyANjPnA' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.728846 7f12edffb6c0 Manual compaction at level-1 from '!items!19r9ijZUyvnlIqgm' @ 72057594037927935 : 1 .. '!items!zON0h5SjFyANjPnA' @ 0 : 0; will stop at (end)

Binary file not shown.

0
packs/profils/000216.log Normal file
View File

1
packs/profils/CURRENT Normal file
View File

@@ -0,0 +1 @@
MANIFEST-000214

0
packs/profils/LOCK Normal file
View File

7
packs/profils/LOG Normal file
View File

@@ -0,0 +1,7 @@
2025/08/12-23:22:10.001919 7f12eeffd6c0 Recovering log #212
2025/08/12-23:22:10.057666 7f12eeffd6c0 Delete type=3 #210
2025/08/12-23:22:10.057755 7f12eeffd6c0 Delete type=0 #212
2025/08/12-23:24:44.454051 7f12edffb6c0 Level-0 table #217: started
2025/08/12-23:24:44.454080 7f12edffb6c0 Level-0 table #217: 0 bytes OK
2025/08/12-23:24:44.461512 7f12edffb6c0 Delete type=0 #215
2025/08/12-23:24:44.481330 7f12edffb6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end)

7
packs/profils/LOG.old Normal file
View File

@@ -0,0 +1,7 @@
2025/08/12-22:03:12.277655 7f12ee7fc6c0 Recovering log #208
2025/08/12-22:03:12.314903 7f12ee7fc6c0 Delete type=3 #206
2025/08/12-22:03:12.314992 7f12ee7fc6c0 Delete type=0 #208
2025/08/12-23:20:29.734915 7f12edffb6c0 Level-0 table #213: started
2025/08/12-23:20:29.734943 7f12edffb6c0 Level-0 table #213: 0 bytes OK
2025/08/12-23:20:29.741871 7f12edffb6c0 Delete type=0 #211
2025/08/12-23:20:29.748380 7f12edffb6c0 Manual compaction at level-0 from 'undefined' @ 72057594037927935 : 1 .. 'undefined' @ 0 : 0; will stop at (end)

Binary file not shown.

BIN
packs/scenes/000148.ldb Normal file

Binary file not shown.

0
packs/scenes/000187.log Normal file
View File

1
packs/scenes/CURRENT Normal file
View File

@@ -0,0 +1 @@
MANIFEST-000185

0
packs/scenes/LOCK Normal file
View File

8
packs/scenes/LOG Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-23:22:10.060101 7f12effff6c0 Recovering log #183
2025/08/12-23:22:10.105374 7f12effff6c0 Delete type=3 #181
2025/08/12-23:22:10.105452 7f12effff6c0 Delete type=0 #183
2025/08/12-23:24:44.474162 7f12edffb6c0 Level-0 table #188: started
2025/08/12-23:24:44.474198 7f12edffb6c0 Level-0 table #188: 0 bytes OK
2025/08/12-23:24:44.481148 7f12edffb6c0 Delete type=0 #186
2025/08/12-23:24:44.481384 7f12edffb6c0 Manual compaction at level-0 from '!scenes!8DjkNeeujp2qff1N' @ 72057594037927935 : 1 .. '!scenes!ypDutqjqZcr7lx6I' @ 0 : 0; will stop at (end)
2025/08/12-23:24:44.481409 7f12edffb6c0 Manual compaction at level-1 from '!scenes!8DjkNeeujp2qff1N' @ 72057594037927935 : 1 .. '!scenes!ypDutqjqZcr7lx6I' @ 0 : 0; will stop at (end)

8
packs/scenes/LOG.old Normal file
View File

@@ -0,0 +1,8 @@
2025/08/12-22:03:12.317962 7f12effff6c0 Recovering log #179
2025/08/12-22:03:12.360482 7f12effff6c0 Delete type=3 #177
2025/08/12-22:03:12.360548 7f12effff6c0 Delete type=0 #179
2025/08/12-23:20:29.742044 7f12edffb6c0 Level-0 table #184: started
2025/08/12-23:20:29.742079 7f12edffb6c0 Level-0 table #184: 0 bytes OK
2025/08/12-23:20:29.748132 7f12edffb6c0 Delete type=0 #182
2025/08/12-23:20:29.758795 7f12edffb6c0 Manual compaction at level-0 from '!scenes!8DjkNeeujp2qff1N' @ 72057594037927935 : 1 .. '!scenes!ypDutqjqZcr7lx6I' @ 0 : 0; will stop at (end)
2025/08/12-23:20:29.764960 7f12edffb6c0 Manual compaction at level-1 from '!scenes!8DjkNeeujp2qff1N' @ 72057594037927935 : 1 .. '!scenes!ypDutqjqZcr7lx6I' @ 0 : 0; will stop at (end)

Binary file not shown.

19
srcdata/normalize.py Normal file
View File

@@ -0,0 +1,19 @@
import json
FIELDS = ["name", "competence", "description", "duree", "portee", "concentration", "critique", "ingredients", "resistance"]
def capitalize_first_letter(s):
if isinstance(s, str) and s:
return s[0].upper() + s[1:]
return s
with open("../srcdata/sort_magieduclan.json", "r", encoding="utf-8") as f:
data = json.load(f)
for spell in data:
for field in FIELDS:
if field in spell:
spell[field] = capitalize_first_letter(spell[field])
with open("../srcdata/sort_magieduclan.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)

287
srcdata/sort_druidisme.json Normal file
View File

@@ -0,0 +1,287 @@
[
{
"name": "Rituels de purification",
"description": "Les rituels de purification consistent à laver une souillure le plus souvent physique et/ou spirituelle, quand le mauvais œil (ou une malédiction) a été placé sur un lieu ou un objet : sang et tripes de cochon versés dans un puits, chouette aux yeux crevés clouée à une porte, crucifix figé dans un chêne, poupée de paille percée daiguilles, etc. Outre le nettoyage des éléments physiques, le rituel consiste à recourir aux plantes purgatives et aux fumigations naturelles appropriées, associées à une récitation de formules consacrées à la Grande Mère. Note : un individu changé en animal contre son gré à laide dune amulette druidique ou dune potion de faëomancie est considéré comme « maudit » et peut être purifié à laide de ce rituel pour retrouver sa forme dorigine. Le SD du rituel sera égal au SD du niveau de maîtrise du sort qui a servi à la métamorphose, ou bien fixé par le Docte.",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "pres",
"duree": "",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Plantes purgatives et aux fumigations naturelles appropriées",
"resistance": "",
"coutactivation": ""
},
{
"name": "Philtre de veille",
"description": "Faire macérer des poignées de ronces et dorties dans de leau salée du lever jusquau coucher de soleil. Pour chaque poignée macérée est produite une dose de philtre. Égoutter les ronces et les orties, les calciner et réduire le charbon ainsi obtenu en une fine poudre. Incorporer celle-ci au liquide issu de la macération ; faire chauffer jusquà ébullition. Réciter les paroles propitiatoires. Ce philtre doit être bu à la nuit tombée. Son utilisateur ne ressent pas les effets de la fatigue pendant la nuit et le jour qui suit, et il ne subit pas les désagréments du manque de sommeil (battements de cœur, troubles de la perception, somnolence…). En revanche, il seffondrera de sommeil au prochain coucher de soleil.",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "prec",
"duree": "Jusquau prochain coucher de soleil",
"portee": "",
"concentration": "Macération (du lever au coucher de soleil) + préparation",
"critique": "Le philtre dure une journée de plus",
"ingredients": "Ronces et orties",
"resistance": "",
"coutactivation": ""
},
{
"name": "Philtre de sommeil",
"description": "Faire bouillir les fleurs de belladone et de passiflore ensemble. Ajouter à la décoction une pointe de miel pour le goût. Renforce le pouvoir soporifique et apaisant des plantes par les paroles rituelles. Le goût sucré rend ce philtre facile à dissimuler dans une pâtisserie ou dans un fruit qui en serait imbibé pendant une nuit. Celui qui absorbe ce philtre commencera à sassoupir une minute après, et dormira sur ses deux oreilles dans les trois minutes qui suivent.",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "prec",
"duree": "6 heures, sauf si le dormeur est réveillé de manière active par autrui",
"portee": "",
"concentration": "Préparation",
"critique": "La durée est doublée",
"ingredients": "Belladone, passiflore et miel",
"resistance": "Physique contre le résultat du sort (ou SD 14) pour ne pas sassoupir",
"coutactivation": ""
},
{
"name": "Philtre damour",
"description": "Faire macérer les pétales dune rose dans de leau. Piler les racines dune mandragore et les incorporer à la macération. Intégrer au mélange trois larmes en prononçant le poème rituel, puis filtrer le tout. Boire ce philtre rend surnaturellement attirant : il fait bénéficier dun bonus de + 2 à toute tentative de séduction. Ce bonus est cumulable avec dautres de même nature (par exemple issus davantages ou datouts féériques).",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "prec",
"duree": "Jusquà ce que lutilisateur se lave",
"portee": "",
"concentration": "Préparation",
"critique": "Lutilisateur bénéficie non plus dun bonus de + 2, mais dun bonus de + 3 à toutes ses tentatives de séduction",
"ingredients": "Mandragore et rose",
"resistance": "",
"coutactivation": ""
},
{
"name": "Philtre de descendance féérique",
"description": "Réduire les fleurs dacacia et clous de girofle en poudre, mélanger aux larmes et réciter les paroles sacrées au moment dune pleine lune. Quelques gouttes de ce philtre bues par une femme ou une fée avant de faire lamour garantiront que, si elle conçoit un enfant avec un Faux-Semblant, celui-ci sera également un Faux-Semblant (de lespèce du père ou de la mère). Un philtre équivalent élaboré avec des larmes humaines garantit que lenfant éventuel sera humain.",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "prec",
"duree": "2 heures",
"portee": "",
"concentration": "Préparation",
"critique": "Le philtre accroît considérablement les chances de fécondation",
"ingredients": "Fleurs dacacia, clous de girofle, larmes de pixie ou dhumain",
"resistance": "",
"coutactivation": ""
},
{
"name": "Philtre de bienveillance végétale",
"description": "Écraser les feuilles de lierre et en retirer le jus. Mélanger celui-ci à du jus de sureau. Le mélange doit être exposé pendant une nuit à la lueur de la pleine lune, au cours de laquelle le druide répétera le poème consacré. La préparation ainsi obtenue rendra la présence de la personne qui la boit très agréable à la végétation environnante. Celle-ci sécartera sur son passage, même sil sagit de ronciers inextricables, et se refermera derrière elle comme si personne nétait passé, facilitant les trajets à travers les broussailles, sous-bois ou jungles comme si elle marchait sur un chemin pédestre. Si un test de Survie est requis, un bonus reflétant cette collaboration des plantes sera fixé par le Docte.",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "prec",
"duree": "6 heures",
"portee": "",
"concentration": "Exposition pendant une nuit + préparation",
"critique": "La durée est doublée",
"ingredients": "Feuilles de lierre, baies de sureau",
"resistance": "",
"coutactivation": ""
},
{
"name": "Philtre tord-boyaux",
"description": "Envelopper les baies de houx dans les feuilles dhortensia, écraser le tout et en extraire le jus. Diluer le liquide dans sept gouttes de rosée et réciter les paroles consacrées afin que le philtre soit presque indétectable et que son goût normalement très amer devienne douceâtre. Descriptif du poison : Voies dadministration : ingestion. Toxicité : 5 Virulence : 15 Signature : 15. Lavantage de ce philtre est quil passera beaucoup plus facilement pour une simple intoxication alimentaire quun empoisonnement à larsenic. Complexité : n. a. Temps de fabrication : une heure avant laube Effets : le philtre tord-boyaux est un liquide translucide au goût douceâtre. Ce goût subtil est aisément dissimulé dans un plat à la saveur prononcée. La substance a linsigne avantage de ne pas perdre ses effets lors dune éventuelle cuisson. Ingéré, le poison commence à faire effet au cours de la digestion (deux heures). Il cause des maux de ventre, des vomissements et des diarrhées si aigus que celui qui lingère se tordra de douleur pendant 4 heures. En cas déchec à un test de Ressort (Endurer) + CON SD 15 pour Résister à la Virulence du poison, la victime perdra Toxicité + ME points de vie en plus de subir un malus de - 5 à toutes ses actions. En cas de réussite, ce malus est réduit à - 2. Note : voir le supplément Drogues délicieuses et effroyables poisons pour plus de détails sur le fonctionnement des poisons, et sur la terminologie propre à ces derniers qui est employée ici pour ce philtre. Notez que deux des poisons qui sont décrits dans cet opuscule, « Humeur de basilic » et « Sang de manticore », sont aussi des poisons fabriqués par les druides la plupart du temps.",
"niveau": 1,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "prec",
"duree": "4 heures (effets)",
"portee": "",
"concentration": "Une heure avant laube (fabrication)",
"critique": "La Signature est augmentée de 2",
"ingredients": "Baies de houx et feuilles dhortensia",
"resistance": "Physique (ressort + con)",
"coutactivation": ""
},
{
"name": "Le rituel des Portes dAvalon",
"description": "Voilà bien longtemps déjà que, pour les profanes, les chemins dAvalon se sont perdus dans les brumes de loubli. Désormais, seuls les druides sont en mesure de sy rendre grâce au rituel des portes dAvalon. Ce rituel est un secret bien gardé parmi les druides, car nombreux sont les ennemis de la Grande Mère et des fées qui aimeraient trouver le chemin de la contrée féérique pour en voler les ressources ou pire, la pervertir voire la détruire. Pour se rendre en Avalon, le druide doit trouver un portail, cest-à-dire deux chênes aux ramures inextricablement entrelacées. Il peut également, grâce à ses pouvoirs, le fabriquer. Puis il doit réciter le poème rituel tout en avalant un gland issu dun chêne avalonien. Lorsque sa litanie prend fin, le brouillard se lève et entoure les chênes dont on naperçoit plus que les troncs. En regardant attentivement, on peut discerner une fine pellicule deau ruisseler des ramures qui finissent par disparaître. Le passage est ouvert. Les invités du druide peuvent passer, mais attention ! Le passage se referme dès lors que le druide est passé, à moins que ce dernier ne le maintienne ouvert à dessein, ce qui est normalement interdit. Note : ce rituel est enseigné à tous les Saronides et ne requiert pas lhabituelle dépense de 5 points dXP accompagnant lapprentissage dun nouveau sort.",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "pres",
"duree": "",
"portee": "Au portail",
"concentration": "Réciter le poème rituel",
"critique": "",
"ingredients": "Gland issu dun chêne avalonien",
"resistance": "",
"coutactivation": ""
},
{
"name": "Rituel de malédiction",
"description": "Les druides sont censés déjouer les malédictions, purifier les lieux souillés ou les victimes du mauvais œil. Cependant, à partir du deuxième cercle, ils sont capables eux-mêmes de maudire un objet, une personne ou un lieu. Un rituel de malédiction fonctionne à rebours dun rituel de purification. Le druide trouve un objet ou une substance comme support de sa malédiction et récite des paroles rituelles désignant la cible, le plus souvent à minuit, à la faveur de la lune. Leffet de ce type de malédiction mineure est ouvert aux fantaisies du Docte (ou des joueurs) : pustules sur la langue, chute de cheveux (saccompagnant dun malus de -1 en Prestance), maladresse chronique (malus de -1 en Précision ou Agilité), etc. Si vous songez à une malédiction majeure aux effets bien plus redoutables, augmentez le SD et exigez un rituel collectif. Dans tous les cas, une malédiction majeure sera très mal vue au sein de la communauté druidique et le responsable risque lexclusion. Note : le fonctionnement de ce rituel est enseigné à tous les Saronides et ne requiert pas lhabituelle dépense de 5 points dXP accompagnant lapprentissage dun nouveau sort.",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "pres",
"duree": "",
"portee": "",
"concentration": "Récite des paroles rituelles",
"critique": "",
"ingredients": "Objet ou une substance comme support",
"resistance": "Psychique ou physique (la plus élevée de la cible)",
"coutactivation": ""
},
{
"name": "Semence de lierre rouge",
"description": "Enduire la graine de lierre avec du crottin dhippogriffe et attendre trois jours. Après cela, une pousse sortira de la graine et il faudra chanter une strophe dépopée en celte transformant la graine germée en arme. Lorsque celle-ci touchera le sol à proximité dun ennemi de celui qui la lancée, des lianes de lierre rouge enchevêtreront ses pieds jusquaux genoux et conféreront un malus de -1 aux actions de la cible impliquant un mouvement. Ensuite, à chaque tour, le lierre montera davantage et deviendra plus vigoureux et difficile à trancher. La victime pourra se battre, mais non bouger ses jambes et souffrira donc dun malus de -1 supplémentaire pour toute attaque de Mêlée, parade ou bien esquive. Le malus maximum est -5, quand la cible est presque immobilisée par un lierre rouge qui lui enserre tout le corps jusquà la tête. Il faudra trancher ou brûler le lierre en causant 10 points de dégâts grâce à une arme tranchante ou un jet de flamme avec un SD de 13 (un échec critique signifiera que le coup a touché la cible). Sans arme tranchante ou flamme, le lierre rouge continuera à pousser et se renforcer, mais finira par se flétrir au bout de 15 minutes.",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "",
"duree": "15 minutes (si non coupée/brûlée)",
"portee": "À proximité",
"concentration": "Préparation sur trois jours + chant",
"critique": "Les points de vie du lierre sont doublés pour déterminer combien de coups darmes tranchantes sont nécessaires afin de le couper",
"ingredients": "Une graine de lierre avalonien et du crottin dhippogriffe",
"resistance": "",
"coutactivation": ""
},
{
"name": "Semence de ronce curatrice",
"description": "Cracher sur la ronce et lenduire de pollen déglantine avalonien. Lenterrer en récitant le poème consacré et attendre trois jours. Le troisième jour, alors que laube étend son voile de rosée sur le monde, déterrer la ronce qui émet une très légère aura verte et pulsatile. Une fois que la ronce a été bénie, le druide doit lappliquer sur la blessure quil veut guérir. Ses épines senfoncent douloureusement dans la peau du blessé afin de la refermer, tels des points de suture. Le blessé regagne instantanément 1d8 points de vie car la ronce sert de cicatrisant. Lorsque la cicatrisation naturelle du blessé est terminée, la ronce sèche et tombe delle-même. Cette semence permet de cicatriser, mais aussi dôter les corps étrangers (comme les balles) : la ronce sintroduit jusquau matériau, lenserre et le repousse, non sans causer une douleur extrême si aucun anesthésiant nest utilisé (test de Ressort + CON SD 14 pour ne pas sévanouir pendant ME minutes).",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "",
"duree": "Jusquà cicatrisation naturelle",
"portee": "Contact",
"concentration": "Préparation sur trois jours + poème",
"critique": "La semence rend directement 8 points de vie au lieu d1d8",
"ingredients": "Ronce du pays dhiver et pollen déglantine avalonienne",
"resistance": "Ressort (con)",
"coutactivation": ""
},
{
"name": "Semence de force",
"description": "Faire bouillir une poignée de fleurs de trèfle rouge pendant une journée, puis faire macérer la feuille de chêne avalonien dans le liquide obtenu. Faire sécher la feuille de chêne avalonien au soleil jusquà ce que des petits piquants poussent sur cette dernière. Après bénédiction du druide, lutilisateur doit poser la feuille de chêne enchantée sur son plexus solaire et appuyer jusquà ce que les petits piquants lui rentrent dans la peau. La feuille sintègre alors temporairement au corps de lutilisateur. Elle lui confère un bonus temporaire de + 2 en Force. Ce bonus est cumulable avec celui de la Capacité naturelle Force accrue (mais aucune créature naturelle ne peut avoir plus de 10 en Force). Au prochain lever de soleil, la feuille de chêne tombe delle-même, jaunie et racornie.",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "",
"duree": "Jusquau prochain lever de soleil",
"portee": "Contact",
"concentration": "Préparation d'une journée + séchage + bénédiction",
"critique": "Bonus de + 3 au lieu de + 2",
"ingredients": "Feuille de chêne avalonien et fleurs de trèfle rouge",
"resistance": "",
"coutactivation": ""
},
{
"name": "Semence de chêne",
"description": "Écraser les baies du gui avalonien et recouvrir le gland de la substance obtenue. Exposer le gland sur un lit de gui avalonien au soleil pendant sept jours, puis réciter le poème consacré et cracher sur le gland. Après bénédiction, lorsque la semence de chêne touche la terre, cest-à-dire le berceau de la Grande Mère, elle se met à pousser à une vitesse fulgurante. En cinq tours, la plante a atteint sa pleine maturité. Elle suit, ensuite, son cycle naturel. Note : dans lintention dutiliser la semence de chêne pour le rituel des Portes dAvalon, il suffit den planter deux à trois mètres de distance lune de lautre et dattendre sept jours.",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "",
"duree": "Suit son cycle naturel après 5 tours de croissance",
"portee": "",
"concentration": "Préparation sur sept jours + poème",
"critique": "Le chêne généré grâce à cette semence est sage, et prodiguera des conseils avisés à ceux qui peuvent communiquer avec lui. Cest un miracle de la Grande Mère qui deviendra objet de soin des druides et des fées proches de la nature",
"ingredients": "Gland et gui avalonien",
"resistance": "",
"coutactivation": ""
},
{
"name": "Semence de haricot magique",
"description": "Le druide doit cueillir lui-même des haricots, les écosser et en faire sécher les semences pendant deux jours. Ensuite, il lui faut demander à une licorne quelle crache dessus, et réciter les paroles rituelles. Au contact de la terre, comme dans le conte, le haricot poussera à une vitesse prodigieuse et atteindra une hauteur de 12 mètres. Il est possible de grimper aux branches comme à une échelle ou bien de se placer au-dessus de la graine pour être hissé au moment de la croissance. Le haricot supporte un poids de 500 kg. Pour atteindre sa pleine hauteur, 3 minutes sont requises. Le Pouvoir Main verte du sylve sur un haricot magique divisera par 3 le temps de croissance, multipliera par 3 sa capacité de charge ainsi que la hauteur atteinte, jusquà 36 mètres (46 mètres avec une réussite critique). Note : la durée de vie de cette plante géante est limitée. Au bout de 6 heures, elle se flétrira puis pourrira.",
"niveau": 2,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "",
"duree": "6 heures",
"portee": "",
"concentration": "Préparation sur deux jours + salive de licorne + poème",
"critique": "La hauteur du haricot est augmentée de 10 mètres",
"ingredients": "Haricot avalonien du pays dété et salive de licorne",
"resistance": "",
"coutactivation": ""
},
{
"name": "Amulette de métamorphose en aigle",
"description": "Le druide doit préparer une pâte en mélangeant largile et leau de rosée. Lorsque la pâte atteint la bonne consistance, il doit la modeler en forme daigle et y planter la plume daigle en prononçant les paroles rituelles. Pour utiliser son amulette, le druide doit la briser sur sa cible en récitant à nouveau les paroles rituelles. Il transforme alors sa cible en aigle. Il peut, bien entendu, être la cible de son propre sort. Certains druides punissent leurs ennemis en les transformant pour longtemps, ou à jamais, en animaux. Cette amulette est déclinable en toutes sortes danimaux et de plantes, et les druides les plus cruels nhésitent pas à transformer leur cible en crapaud… Cependant, notez que le druide doit apprendre un nouveau sort pour chaque animal en lequel il peut se changer ou transformer autrui. En effet, ces amulettes reposent sur la connaissance des noms secrets des choses et des êtres qui nécessitent un apprentissage complexe. Note : il existe des amulettes similaires permettant doctroyer temporairement le Pouvoir de Communication animale ou végétale à la cible pendant une semaine. Elles constituent un sort différent et requièrent un apprentissage dédié (et donc la dépense dXP afférente).",
"niveau": 3,
"competence": "Druidisme",
"carac1": "prec",
"carac2": "",
"duree": "Une semaine, ou jusquà ce que le druide désire faire cesser la métamorphose",
"portee": "Contact",
"concentration": "Préparation",
"critique": "Celui qui est métamorphosé en aigle peut parler aux animaux, quels quils soient",
"ingredients": "Plume daigle, eau de rosée avalonienne, argile avalonien",
"resistance": "Physique pour une cible récalcitrante au pouvoir de lamulette",
"coutactivation": ""
},
{
"name": "Amulette de cercle de protection",
"description": "LOvate doit baigner la large pierre dans de leau vive, la sécher et la faire chauffer pendant une journée. À ce moment, il doit appliquer la feuille de chêne sur la pierre de manière quelle sincruste dedans en récitant les paroles rituelles. Pour activer le cercle de protection, le druide doit délimiter de manière continue son aire daction (fil enterré, frise dans la pierre, canalisation deau…), qui a pour rayon maximal la valeur de Prestance du druide x 100 m. Puis il enterre son amulette au centre du cercle en répétant les paroles rituelles. Dès lors, le cercle de protection est actif, ce qui signifie que nul ne peut y entrer sans y avoir été invité à voix haute par le druide. Les contrevenants sont bloqués par une force invisible. Forcer le passage est possible en réussissant un test de Ressort + FOR ou Fortitude + ESP contre un SD égal au score du test du druide. En cas déchec, le contrevenant sévanouira si sa Résistance psychique est inférieure au SD du sort, sinon, il souffrira dune migraine causant un malus de - 2.",
"niveau": 3,
"competence": "Druidisme",
"carac1": "prec",
"carac2": "",
"duree": "Tant que le cercle est brisé ou que lamulette est déterrée",
"portee": "Valeur de Prestance du druide x 100 m (rayon)",
"concentration": "Préparation d'une journée + délimitation",
"critique": "Le cercle ne peut être brisé que si lamulette est déterrée",
"ingredients": "Large pierre, feuille de chêne avalonien",
"resistance": "Ressort (for / fort / esp)",
"coutactivation": ""
},
{
"name": "Amulette dorage",
"description": "Le druide doit tailler la branche de frêne avalonien en une baguette de la taille de son avant-bras, et limmerger pendant une semaine dans leau de pluie dAvalon. Lorsquil la sort de leau, il doit y graver une rune secrète et prononcer les paroles rituelles. Pour utiliser son amulette, le druide doit voir le ciel et psalmodier les paroles rituelles en le contemplant. Puis, sur la dernière syllabe, il brise la baguette. À partir de ce moment, les nuées commencent à converger et le ciel sobscurcit ; au bout de dix minutes, la pluie se met à tomber. Dix minutes plus tard, lorage est là. Le druide peut manipuler lorage, le déplacer, faire varier lintensité de ses pluies et, surtout, manier la foudre. Pour ce faire, il doit réussir un test de Druidisme + PER SD 15. En cas de réussite, il foudroie sa cible qui subit [MR + 6] points de dégâts. Il peut manier la foudre une fois par tour (une Action), mais cela lui coûte 3 points dÂme à chaque fois (et un point temporaire dEsprit quand sa réserve dÂme est épuisée). Note : il existe dautres amulettes pour contrôler le temps quil fait (invoquer le soleil ou la pluie, etc.). Ce sont des sorts différents, requérant un apprentissage à part entière, mais qui fonctionnent de manière équivalente. Par exemple, une amulette de blizzard fera venir une tempête de neige et le druide pourra diriger de violentes bourrasques à la place de la foudre, ne causant pas de dégâts mais faisant tomber les cibles. Cependant, la température extérieure ne doit pas excéder 10 degrés pour que cette amulette fonctionne.",
"niveau": 3,
"competence": "Druidisme",
"carac1": "prec",
"carac2": "",
"duree": "Jusquà ce que le druide sendorme ou perde conscience",
"portee": "",
"concentration": "Préparation d'une semaine + activation",
"critique": "Lorage se déclenche en une minute",
"ingredients": "Branche de frêne avalonien et eau de pluie avalonienne",
"resistance": "",
"coutactivation": "3 points dÂme (et 1 point temporaire dEsprit si jauge dÂme épuisée) par utilisation de la foudre"
},
{
"name": "Amulette de renaissance du Bras",
"description": "LOvate doit enrouler la patte dours dans les feuilles de marronnier avalonien en prononçant les paroles rituelles. Puis il fait sécher le tout au soleil (la patte ne pourrit pas car elle est déjà enchantée). Pour utiliser son amulette, le druide doit humecter la patte dours avec le sang du manchot. Puis il la jette dans un grand feu en répétant les paroles rituelles tout au long de la combustion. La cible sent alors son bras repousser au fur et à mesure que la patte dours se consume. Lorsquelle est complètement consumée, la cible a complètement récupéré son bras et lusage de celui-ci. Le druide peut être la cible de son propre sort.",
"niveau": 3,
"competence": "Druidisme",
"carac1": "prec",
"carac2": "",
"duree": "Définitive",
"portee": "Contact",
"concentration": "Préparation + séchage + combustion",
"critique": "Le bras nouvellement acquis est irrigué par les forces de la Grande Mère. Il bénéficie dun bonus perpétuel de + 1 en Force (dans la limite du rang 10). Ce bonus est invisible : la cible na pas un bras plus gros que lautre",
"ingredients": "Feuilles de marronnier avalonien et patte dours décédé de mort naturelle",
"resistance": "",
"coutactivation": ""
},
{
"name": "Rituel de réenchantement",
"description": "Un Ovate aidé dau moins trois confrères est capable de restaurer la Féérie dun Faux-Semblant qui aurait été victime du Grand Rituel dexorcisme (voir p. 67) (léquivalent dune malédiction majeure pour sa victime). Le druide peut, en récitant les paroles rituelles pendant trois heures et surtout, au moyen dune quantité invraisemblable de poudre daile de pixie, réenchanter la victime de lexorcisme et lui restituer un rang de Féérie. La victime devra ensuite séjourner un an en Avalon pour redévelopper sa Féérie et pouvoir à nouveau se démasquer. Elle sera de la même espèce et conservera la même apparence quavant, mais devra réapprendre progressivement à utiliser ses Pouvoirs. Note : ce sort difficile, et rarement pratiqué, est connu des Ovates sans quils aient besoin de dépenser dXP pour lapprendre. Il fait partie de leur initiation druidique.",
"niveau": 3,
"competence": "Druidisme",
"carac1": "esp",
"carac2": "",
"duree": "Permanente (restauration), un an (redéveloppement de Féérie)",
"portee": "",
"concentration": "Trois heures de récitation",
"critique": "",
"ingredients": "Poudre daile de pixie",
"resistance": "",
"coutactivation": ""
}
]

View File

@@ -0,0 +1,137 @@
[
{
"name": "Potion de faiblesse à largent",
"description": "Le bénéficiaire est vulnérable à largent comme sil était une fée pendant les trente prochaines minutes sil a bu la potion, pendant 10 tours sil la respirée, pendant les deux prochains tours sil en a seulement été aspergé. Cette préparation est sans effet sur une fée.",
"niveau": "1",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "Trente prochaines minutes sil a bu la potion, pendant 10 tours sil la respirée, pendant les deux prochains tours sil en a seulement été aspergé",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Cinq litres de sang de fée.",
"resistance": "",
"coutactivation": ""
},
{
"name": "Émincé de force dogre",
"description": "Le bénéficiaire obtient + 1 en Force. Note : cest là un exemple de recette où des ingrédients alternatifs sont faciles à substituer ; en loccurrence, les biceps de toute fée disposant de la Capacité naturelle Force accrue pourront constituer un ingrédient alternatif.",
"niveau": "1",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Un biceps dogre, une pincée de poivre fort, une livre de saindoux dogre.",
"resistance": "",
"coutactivation": ""
},
{
"name": "Masque de fée",
"description": "Le visage et la peau du bénéficiaire sont profondément modifiés pour lui donner lapparence dune fée de lespèce de celle quil a dépecée, quil soit lui-même un humain ou un Faux-Semblant. Un bonus égal à la MR/2 du test de Faëomancie sera appliqué aux éventuels tests de Comédie + PRES requis pour ne pas être percé à jour. Leffet perdure pendant MR jours. Note : la taille du bénéficiaire nest pas changée et il ne lui poussera pas dailes ou autres appendices, même si lespèce féérique imitée est censée en avoir. Ces détails peuvent trahir son déguisement qui devra donc être complété soit grâce à une autre préparation faëomantique, soit par des appendices postiches, sil ne veut pas quun Faux-Semblant le perce à jour aisément grâce à un test dObservation + PER au SD en opposition au test de Comédie + PRES.",
"niveau": "2",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "MR jours",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Peau du visage dun Faux-Semblant démasqué ou dune fée encore en vie ; poudre daile de pixie ; sang de la victime ; graisse de protys",
"resistance": "",
"coutactivation": ""
},
{
"name": "CromesQuis de calme angÉliQue",
"description": "Le bénéficiaire obtient le Pouvoir Calme pendant au moins trente minutes.",
"niveau": "2",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "Au moins trente minutes",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Un os de mâchoire dange, sept cheveux dange, un cœur dange.",
"resistance": "",
"coutactivation": ""
},
{
"name": "Ragoût de ramollissement du mÉtal",
"description": "Le bénéficiaire obtient le Pouvoir profond Ramollissement du métal pendant au moins trente minutes.",
"niveau": "3",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "Au moins trente minutes",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "100 g de moelle dos de gnome profond, un demi-litre de bile de gnome profond, un litre de vin rouge (pour le goût), 2 kg de chair de gnome profond, 10 g de poudre daile de pixie. Note : un Maître-queux na pas besoin que le gnome soit un gnome profond.",
"resistance": "",
"coutactivation": ""
},
{
"name": "Baguette de main verte",
"description": "Il suffit ensuite de planter la baguette imbibée de sang dans le sol ou sur un végétal pour activer le Pouvoir Main verte.",
"niveau": "3",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "1 heure",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Doigts/racines de sylve, sève de hêtre, cendres de cocatrice, poudre daile de pixie et baguette dosier creuse",
"resistance": "",
"coutactivation": "2 points de vie"
},
{
"name": "Canne de souffle enflammÉ",
"description": "La main du porteur doit être au contact du tissu imbibé de sang pour déclencher le jet de Souffle enflammé.",
"niveau": "3",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "1 heure",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Gésier de phénix, humeur de basilic, poudre de silex, poudre daile de pixie, canne, tissu et pommeau métallique",
"resistance": "",
"coutactivation": "3 points de vie"
},
{
"name": "Diadème de penséces impures",
"description": "Une fois coiffé du diadème, pressez ce dernier sur le front pour faire perler le sang. Le Pouvoir Pensées impures sera utilisable par le porteur 5 minutes plus tard.",
"niveau": "3",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "1 heure",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "Cœur de succube, humeur de basilic, poudre daile de pixie, diadème et trois joyaux creux",
"resistance": "",
"coutactivation": "3 points de vie"
},
{
"name": "Lunettes de vision nocturne",
"description": "Une fois les lunettes prêtes, verser plusieurs gouttes de sang sur chaque verre. Leur usage sera possible 5 minutes plus tard.",
"niveau": "3",
"competence": "Faëomancie",
"carac1": "precision",
"carac2": "sang-froid",
"duree": "1 heure",
"portee": "",
"concentration": "",
"critique": "",
"ingredients": "8 iris et cornées de Faux-Semblants ayant accès à la vision nocturne (4 par verre), salive de troll, poudre daile de pixie, lunettes de verre très fin (cristal)",
"resistance": "",
"coutactivation": "1 point de vie"
}
]

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