mirror of
https://github.com/em-squared/5e-drs.git
synced 2025-10-29 20:54:19 +00:00
435 lines
13 KiB
JavaScript
435 lines
13 KiB
JavaScript
import {stats} from '../../data/stats'
|
|
import {CHALLENGES} from '../../data/monsters'
|
|
import {armorTypes} from '../../data/armorTypes.js'
|
|
|
|
// Calcul du modificateur de caractéristique
|
|
export function getModifier (score) {
|
|
return Math.floor((score - 10) / 2)
|
|
}
|
|
|
|
// Ajoute un + devant les valeurs positives pour l'affichage
|
|
export function displayBonus (value) {
|
|
if (value >= 0) {
|
|
value = '+' + value
|
|
}
|
|
return value
|
|
}
|
|
|
|
// Affichage d'un score de caractéristiques sous la forme 16 (+3)
|
|
export function displayAbilityScore (score) {
|
|
let modifier = getModifier(score)
|
|
if (modifier >= 0) {
|
|
modifier = '+' + modifier
|
|
}
|
|
return score + ' (' + modifier + ')'
|
|
}
|
|
|
|
// Calcul du bonus de maîtrise en fonction du niveau
|
|
export function getProficiencyBonus (level) {
|
|
if (level <= 1) {
|
|
return 2
|
|
}
|
|
return Math.ceil(level / 4) + 1
|
|
}
|
|
|
|
// Affiche l'indice de dangerosité
|
|
export function displayChallenge (challenge, xp = false) {
|
|
let result = ''
|
|
if (stats.challenges[challenge]) {
|
|
result += stats.challenges[challenge].label
|
|
if (xp) {
|
|
result += ' (PX : ' + stats.challenges[challenge].xp + ')'
|
|
}
|
|
return result
|
|
}
|
|
return challenge
|
|
}
|
|
|
|
// Affiche le type, la taille et l'alignement
|
|
export function displayMonsterTypeSizeAlignment (monster, hideAlignment = false, showChallenge = false) {
|
|
let result = ''
|
|
if (monster.isSwarm) {
|
|
result = 'Nuée de taille '+ monster.size + ' composée ' + stats.monsterTypes[monster.type].swarm
|
|
if (monster.subtype) {
|
|
result += ' (' + monster.subtype + ')'
|
|
}
|
|
result += ' de taille ' + monster.swarmSize
|
|
} else {
|
|
result = monster.type
|
|
if (monster.subtype) {
|
|
result += ' (' + monster.subtype + ')'
|
|
}
|
|
result += ' de taille ' + monster.size
|
|
}
|
|
if (!hideAlignment) {
|
|
if (monster.alignment) {
|
|
result += ', ' + monster.alignment
|
|
} else {
|
|
result += ', non alignée'
|
|
}
|
|
}
|
|
if (showChallenge) {
|
|
result += ', Dangerosité : ' + displayChallenge(monster.challenge)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Affiche la Classe d'armure
|
|
export function displayAC (monster) {
|
|
let ac = 10
|
|
let hasMageArmor = false
|
|
let mageArmorAc = 13
|
|
let mageArmor = ''
|
|
let armor = ''
|
|
// Le monstre n'a pas d'armure.
|
|
// CA = 10 + Dex
|
|
if (!monster.frontmatter.ac.armorType) {
|
|
ac = 10 + getModifier(monster.frontmatter.abilityScores.dex)
|
|
} else {
|
|
// Le type d'armure n'est pas formalisé. On prend la valeur brute
|
|
if (monster.frontmatter.ac.armorType == 'custom') {
|
|
return monster.frontmatter.ac.value
|
|
}
|
|
|
|
// Le monstre a une armure naturelle.
|
|
// CA = 10 + Armure naturelle + Dex
|
|
if (monster.frontmatter.ac.armorType == 'armure naturelle') {
|
|
armor = monster.frontmatter.ac.armorType
|
|
if (parseInt(monster.frontmatter.ac.value)) {
|
|
ac = ac + parseInt(monster.frontmatter.ac.value) + getModifier(monster.frontmatter.abilityScores.dex)
|
|
} else {
|
|
ac = ac + getModifier(monster.frontmatter.abilityScores.dex)
|
|
}
|
|
} else if (monster.frontmatter.ac.armorType == 'armure du mage') {
|
|
hasMageArmor = true
|
|
ac = ac + getModifier(monster.frontmatter.abilityScores.dex)
|
|
mageArmorAc = mageArmorAc + getModifier(monster.frontmatter.abilityScores.dex)
|
|
armor = mageArmorAc + ' avec armure du mage'
|
|
} else {
|
|
// Le monstre a un type d'armure défini.
|
|
// On calcule sa CA selon le type
|
|
let armorType = armorTypes[monster.frontmatter.ac.armorType]
|
|
|
|
// Le type d'armure n'existe pas. On l'ignore.
|
|
// CA = 10 + Dex
|
|
if (!armorType) {
|
|
ac = ac + getModifier(monster.frontmatter.abilityScores.dex)
|
|
} else {
|
|
// L'armure n'impose pas de limite de Dex
|
|
armor = monster.frontmatter.ac.armorType
|
|
if (armorType.maxDex === false) {
|
|
ac = armorType.value + getModifier(monster.frontmatter.abilityScores.dex)
|
|
} else {
|
|
// La limite de Dex de l'armure est inférieure à la Dex du monstre
|
|
if (armorType.maxDex === 0) {
|
|
ac = armorType.value
|
|
} else if ((armorType.maxDex !== 0) && (armorType.maxDex <= getModifier(monster.frontmatter.abilityScores.dex))) {
|
|
ac = armorType.value + armorType.maxDex
|
|
} else {
|
|
ac = armorType.value + getModifier(monster.frontmatter.abilityScores.dex)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Le monstre a un bouclier. Sa CA augmente de 2.
|
|
if (monster.frontmatter.ac.hasShield) {
|
|
ac = ac + 2
|
|
mageArmorAc = mageArmorAc + 2
|
|
if (armor != '') {
|
|
armor += ', '
|
|
}
|
|
armor += 'bouclier'
|
|
|
|
if (hasMageArmor) {
|
|
armor = mageArmorAc + ' avec armure du mage, bouclier'
|
|
}
|
|
}
|
|
|
|
if (armor != '') {
|
|
ac += ' (' + armor + ')'
|
|
}
|
|
|
|
return ac
|
|
}
|
|
|
|
// Affiche les points de vie
|
|
export function displayHP (monster) {
|
|
if (monster.frontmatter.customHP) {
|
|
return monster.frontmatter.customHP
|
|
} else if (monster.frontmatter.hitDiceCount) {
|
|
let hitDieSize = 8 // Dé de vie moyen par défaut
|
|
if (monster.frontmatter.hitDieSize) {
|
|
hitDieSize = monster.frontmatter.hitDieSize
|
|
} else if (monster.frontmatter.size) {
|
|
hitDieSize = stats.sizes[monster.frontmatter.size].hitDie
|
|
}
|
|
let hitPointsBonus = 0
|
|
if (monster.frontmatter.hitDiceCount > 1) {
|
|
hitPointsBonus = Math.floor(monster.frontmatter.hitDiceCount / 2)
|
|
}
|
|
let averageHP = monster.frontmatter.hitDiceCount * (hitDieSize / 2) + monster.frontmatter.hitDiceCount * getModifier(monster.frontmatter.abilityScores.con) + hitPointsBonus
|
|
let conMod = ""
|
|
if (getModifier(monster.frontmatter.abilityScores.con) != 0) {
|
|
conMod = monster.frontmatter.hitDiceCount * getModifier(monster.frontmatter.abilityScores.con)
|
|
conMod = displayBonus(conMod)
|
|
}
|
|
return averageHP + ' (' + monster.frontmatter.hitDiceCount + "d" + hitDieSize + conMod + ')'
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Affiche les vitesses de déplacement
|
|
export function displayMovement (monster) {
|
|
if (monster.frontmatter.customMovement) {
|
|
return monster.frontmatter.customMovement
|
|
}
|
|
let result = ''
|
|
if (monster.frontmatter.movement.walk) {
|
|
result += monster.frontmatter.movement.walk + ' m'
|
|
} else {
|
|
result += '0 m'
|
|
}
|
|
if (monster.frontmatter.movement.climb) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
result += 'escalade ' + monster.frontmatter.movement.climb + ' m'
|
|
}
|
|
if (monster.frontmatter.movement.burrow) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
result += 'fouissement ' + monster.frontmatter.movement.burrow + ' m'
|
|
}
|
|
if (monster.frontmatter.movement.swim) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
result += 'nage ' + monster.frontmatter.movement.swim + ' m'
|
|
}
|
|
if (monster.frontmatter.movement.fly) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
result += 'vol ' + monster.frontmatter.movement.fly + ' m'
|
|
if (monster.frontmatter.movement.hover) {
|
|
result += ' (vol stationnaire)'
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
export function getMonsterProficiencyBonus (monster) {
|
|
if (monster.frontmatter.proficiencyBonus) {
|
|
return parseInt(monster.frontmatter.proficiencyBonus)
|
|
}
|
|
return getProficiencyBonus(monster.frontmatter.challenge)
|
|
}
|
|
|
|
export function displaySavingThrowBonus (monster, ability) {
|
|
let result = stats.abilities[ability].abbr
|
|
let bonus = displayBonus(getModifier(monster.frontmatter.abilityScores[ability]) + getMonsterProficiencyBonus(monster))
|
|
result += ' ' + bonus
|
|
return result
|
|
}
|
|
|
|
export function displaySavingThrows(monster) {
|
|
if (monster.frontmatter.customSavingThrows) {
|
|
return monster.frontmatter.customSavingThrows
|
|
}
|
|
let savingThrows = []
|
|
|
|
if (monster.frontmatter.savingThrows && monster.frontmatter.savingThrows.length > 0) {
|
|
for (var st of monster.frontmatter.savingThrows) {
|
|
savingThrows.push(displaySavingThrowBonus(monster, st))
|
|
}
|
|
}
|
|
|
|
return savingThrows.join(', ')
|
|
}
|
|
|
|
export function displaySkillBonus (monster, skill) {
|
|
if (skill.name == 'custom') {
|
|
return skill.value
|
|
}
|
|
let result = stats.skills[skill.name].label
|
|
if (skill.invalid) {
|
|
result += ' ' + displayBonus(skill.value)
|
|
return result
|
|
}
|
|
let bonus = getModifier(monster.frontmatter.abilityScores[stats.skills[skill.name].ability]) + getMonsterProficiencyBonus(monster)
|
|
if (skill.isExpert) {
|
|
bonus += getMonsterProficiencyBonus(monster) // Bonus de maître doublé pour les experts
|
|
}
|
|
bonus = displayBonus(bonus)
|
|
result += ' ' + bonus
|
|
return result
|
|
}
|
|
|
|
export function displaySkills(monster) {
|
|
if (monster.frontmatter.customSkills) {
|
|
return monster.frontmatter.customSkills
|
|
}
|
|
|
|
let skills = []
|
|
|
|
if (monster.frontmatter.skills && monster.frontmatter.skills.length > 0) {
|
|
for (var skill of monster.frontmatter.skills) {
|
|
skills.push(displaySkillBonus(monster, skill))
|
|
}
|
|
}
|
|
|
|
return skills.join(', ')
|
|
}
|
|
|
|
export function displayDamageTypes (damageTypes) {
|
|
let result = ''
|
|
damageTypes.forEach((damageType, idx) => {
|
|
if (result != '') {
|
|
if (idx == damageTypes.length - 1) {
|
|
result += ' et '
|
|
} else {
|
|
result += ', '
|
|
}
|
|
}
|
|
result += stats.damageTypes[damageType].label
|
|
})
|
|
return result
|
|
}
|
|
|
|
export function displayVulnerabilities(monster) {
|
|
if (monster.frontmatter.customDamageTypeVulnerabilities) {
|
|
return monster.frontmatter.customDamageTypeVulnerabilities
|
|
}
|
|
|
|
let vulnerabilities = ''
|
|
|
|
if (monster.frontmatter.damageTypeVulnerabilities && monster.frontmatter.damageTypeVulnerabilities.length > 0) {
|
|
vulnerabilities = displayDamageTypes(monster.frontmatter.damageTypeVulnerabilities)
|
|
}
|
|
|
|
return vulnerabilities
|
|
}
|
|
|
|
export function displayResistances(monster) {
|
|
|
|
let resistances = ''
|
|
|
|
if (monster.frontmatter.damageTypeResistances && monster.frontmatter.damageTypeResistances.length > 0) {
|
|
resistances = displayDamageTypes(monster.frontmatter.damageTypeResistances)
|
|
}
|
|
|
|
return resistances
|
|
}
|
|
|
|
export function displayImmunities(monster) {
|
|
|
|
let immunities = ''
|
|
|
|
if (monster.frontmatter.damageTypeImmunities && monster.frontmatter.damageTypeImmunities.length > 0) {
|
|
immunities = displayDamageTypes(monster.frontmatter.damageTypeImmunities)
|
|
}
|
|
|
|
return immunities
|
|
}
|
|
|
|
export function displayCondition (condition) {
|
|
return stats.conditions[condition].label
|
|
}
|
|
|
|
export function displayConditionImmunities(monster) {
|
|
|
|
let result = ''
|
|
|
|
if (monster.frontmatter.conditionImmunities && monster.frontmatter.conditionImmunities.length > 0) {
|
|
monster.frontmatter.conditionImmunities.forEach((condition, idx) => {
|
|
if (result != '') {
|
|
if (idx == monster.frontmatter.conditionImmunities.length - 1) {
|
|
result += ' et '
|
|
} else {
|
|
result += ', '
|
|
}
|
|
}
|
|
result += stats.conditions[condition].label
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
export function getMonsterPassivePerception (monster) {
|
|
let result = 10 + getModifier(monster.frontmatter.abilityScores.sag)
|
|
if (monster.frontmatter.skills) {
|
|
monster.frontmatter.skills.forEach((skill, idx) => {
|
|
if (skill.name == 'perception') {
|
|
if (skill.isExpert) {
|
|
result += getMonsterProficiencyBonus(monster) * 2
|
|
} else {
|
|
result += getMonsterProficiencyBonus(monster)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
export function displaySenses (monster) {
|
|
let result = ''
|
|
if (monster.frontmatter.senses) {
|
|
if (monster.frontmatter.senses.tremorsense) {
|
|
result += 'perception des vibrations ' + monster.frontmatter.senses.tremorsense + ' m'
|
|
}
|
|
if (monster.frontmatter.senses.blindsight || monster.frontmatter.senses.customBlindSight) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
if (monster.frontmatter.senses.customBlindSight) {
|
|
result += 'vision aveugle ' + monster.frontmatter.senses.customBlindSight
|
|
} else {
|
|
result += 'vision aveugle ' + monster.frontmatter.senses.blindsight + ' m'
|
|
}
|
|
}
|
|
if (monster.frontmatter.senses.darkvision || monster.frontmatter.senses.customDarkvision) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
if (monster.frontmatter.senses.customDarkvision) {
|
|
result += 'vision dans le noir ' + monster.frontmatter.senses.customDarkvision
|
|
} else {
|
|
result += 'vision dans le noir ' + monster.frontmatter.senses.darkvision + ' m'
|
|
}
|
|
}
|
|
if (monster.frontmatter.senses.truesight || monster.frontmatter.senses.customTrueSight) {
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
if (monster.frontmatter.senses.customTrueSight) {
|
|
result += 'vision parfaite ' + monster.frontmatter.senses.customTrueSight
|
|
} else {
|
|
result += 'vision parfaite ' + monster.frontmatter.senses.truesight + ' m'
|
|
}
|
|
}
|
|
if (result != '') {
|
|
result += ', '
|
|
}
|
|
}
|
|
if (monster.frontmatter.senses && monster.frontmatter.senses.customPassivePerception) {
|
|
result += 'Perception passive ' + monster.frontmatter.senses.customPassivePerception
|
|
} else {
|
|
result += 'Perception passive ' + getMonsterPassivePerception(monster)
|
|
}
|
|
return result
|
|
}
|
|
|
|
|
|
// Retourne le nombre de points de combat pour un indice de dangerosité
|
|
export function getPCbyChallenge(challenge) {
|
|
let challengeIndex = CHALLENGES.findIndex(item => item.value == challenge)
|
|
if (challengeIndex > -1) {
|
|
return CHALLENGES[challengeIndex].pc
|
|
}
|
|
return false
|
|
}
|