Add D30, spells, miracles and ranged defense roll

This commit is contained in:
2025-01-05 22:14:44 +01:00
parent dbd27b837f
commit 5fc40b2b18
39 changed files with 460 additions and 81 deletions

View File

@@ -566,8 +566,33 @@ i.lethalfantasy {
} }
.lethalfantasy .tab.character-spells .spells .spell { .lethalfantasy .tab.character-spells .spells .spell {
display: flex; display: flex;
align-items: center;
gap: 10px; gap: 10px;
} }
.lethalfantasy .tab.character-spells .spells .spell .item-img {
width: 32px;
height: 32px;
}
.lethalfantasy .tab.character-spells .spells .spell .name {
min-width: 12rem;
}
.lethalfantasy .tab.character-spells .miracles {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.lethalfantasy .tab.character-spells .miracles .miracle {
display: flex;
align-items: center;
gap: 10px;
}
.lethalfantasy .tab.character-spells .miracles .miracle .item-img {
width: 32px;
height: 32px;
}
.lethalfantasy .tab.character-spells .miracles .miracle .name {
min-width: 12rem;
}
.lethalfantasy .tab.character-spells .appris { .lethalfantasy .tab.character-spells .appris {
font-weight: bold; font-weight: bold;
} }

View File

@@ -478,6 +478,10 @@
"Platinums": "Platinum" "Platinums": "Platinum"
}, },
"Label": { "Label": {
"rangeDefenseDialog": "Range defense dialog",
"rangeDefenseRoll": "Range defense roll",
"rangedAttackDefense": "Ranged attack defense",
"miracles": "Miracles",
"biodata": "Biodata", "biodata": "Biodata",
"titleChallenge": "Challenge", "titleChallenge": "Challenge",
"titleSave": "Save", "titleSave": "Save",

View File

@@ -1,4 +1,5 @@
import LethalFantasyActorSheet from "./base-actor-sheet.mjs" import LethalFantasyActorSheet from "./base-actor-sheet.mjs"
import LethalFantasyRoll from "../../documents/roll.mjs"
export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet { export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet {
/** @override */ /** @override */
@@ -13,6 +14,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
}, },
actions: { actions: {
createEquipment: LethalFantasyCharacterSheet.#onCreateEquipment, createEquipment: LethalFantasyCharacterSheet.#onCreateEquipment,
rangedAttackDefense: LethalFantasyCharacterSheet.#onRangedAttackDefense,
}, },
} }
@@ -48,12 +50,15 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
* @returns {Record<string, Partial<ApplicationTab>>} * @returns {Record<string, Partial<ApplicationTab>>}
*/ */
#getTabs() { #getTabs() {
const tabs = { let tabs = {
skills: { id: "skills", group: "sheet", icon: "fa-solid fa-shapes", label: "LETHALFANTASY.Label.skills" }, skills: { id: "skills", group: "sheet", icon: "fa-solid fa-shapes", label: "LETHALFANTASY.Label.skills" },
weapons: { id: "weapons", group: "sheet", icon: "fa-solid fa-swords", label: "LETHALFANTASY.Label.weapons" }, weapons: { id: "weapons", group: "sheet", icon: "fa-solid fa-swords", label: "LETHALFANTASY.Label.weapons" },
spells: { id: "spells", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-wand-magic-sparkles", label: "LETHALFANTASY.Label.spells" },
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "LETHALFANTASY.Label.biography" }, biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "LETHALFANTASY.Label.biography" },
} }
if (this.actor.system.biodata.magicUser) {
tabs.spells = { id: "spells", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-wand-magic-sparkles", label: "LETHALFANTASY.Label.spells" }
}
for (const v of Object.values(tabs)) { for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : "" v.cssClass = v.active ? "active" : ""
@@ -95,6 +100,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
case "spells": case "spells":
context.tab = context.tabs.spells context.tab = context.tabs.spells
context.spells = doc.itemTypes.spell context.spells = doc.itemTypes.spell
context.miracles = doc.itemTypes.miracle
context.hasSpells = context.spells.length > 0 context.hasSpells = context.spells.length > 0
break break
case "weapons": case "weapons":
@@ -135,21 +141,20 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
await this.document.addPath(item) await this.document.addPath(item)
} }
static async #onRangedAttackDefense(event, target) {
const hasTarget = false
let roll = await LethalFantasyRoll.promptRangedDefense({
actorId: this.actor.id,
actorName: this.actor.name,
actorImage: this.actor.img
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
/**
* Creates a new attack item directly from the sheet and embeds it into the document.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
*/
static #onCreateEquipment(event, target) { static #onCreateEquipment(event, target) {
// Création d'une armure
if (event.shiftKey) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("LETHALFANTASY.Label.newArmor"), type: "armor" }])
}
// Création d'une arme
else {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("LETHALFANTASY.Label.newWeapon"), type: "weapon" }])
}
} }
getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) { getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) {
@@ -168,7 +173,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
goodSkill = s goodSkill = s
} }
} }
if (rollType.includes("weapon-damage") ) { if (rollType.includes("weapon-damage")) {
if (s.system.weaponBonus.damage > maxValue) { if (s.system.weaponBonus.damage > maxValue) {
maxValue = Number(s.system.weaponBonus.damage) maxValue = Number(s.system.weaponBonus.damage)
goodSkill = s goodSkill = s
@@ -209,6 +214,14 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
rollTarget.rollDice = event.target.dataset?.rollDice rollTarget.rollDice = event.target.dataset?.rollDice
break break
case "spell":
rollTarget = this.actor.items.find((i) => i.type === "spell" && i.id === rollKey)
rollTarget.rollKey = rollKey
break
case "miracle":
rollTarget = this.actor.items.find((i) => i.type === "miracle" && i.id === rollKey)
rollTarget.rollKey = rollKey
break
case "skill": case "skill":
rollTarget = this.actor.items.find((i) => i.type === "skill" && i.id === rollKey) rollTarget = this.actor.items.find((i) => i.type === "skill" && i.id === rollKey)
rollTarget.rollKey = rollKey rollTarget.rollKey = rollKey
@@ -260,6 +273,11 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType)) ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType))
break break
} }
// In all cases
rollTarget.magicUser = this.actor.system.biodata.magicUser
rollTarget.actorModifiers = foundry.utils.duplicate(this.actor.system.modifiers)
await this.document.system.roll(rollType, rollTarget) await this.document.system.roll(rollType, rollTarget)
} }
// #endregion // #endregion

View File

@@ -42,6 +42,43 @@ export const MONEY = {
} }
} }
export const MOVEMENT_CHOICES = {
"none": {label: "None (D8E)", value: "D8"},
"walk": {label: "Walk (D10E)", value: "D10"},
"jog": {label: "Jog (D12E)", value: "D12"},
"run": {label: "Run (D20E)", value: "D20"},
"incombat": {label: "In Combat (D12E)", value: "D12"}
}
export const MOVE_DIRECTION_CHOICES = {
"none": {label: "None (+0)", value: "0"},
"away": {label: "Away (+4)", value: "+4"},
"toward": {label: "Toward (+0)", value: "0"},
"lateral": {label: "Lateral (+10)", value: "+10"}
}
export const SIZE_CHOICES = {
"tiny": {label: "Tiny (+10)", value: "+10"},
"small": {label: "Small (+5)", value: "+5"},
"medium": {label: "Medium (+0)", value: "0"},
"huge": {label: "Huge (-10)", value: "-10"}
}
export const RANGE_CHOICES = {
"pointblank": {label: "Point Blank (-5)", value: "-5"},
"short": {label: "Short (+0)", value: "0"},
"medium": {label: "Medium (+8)", value: "+8"},
"long": {label: "Long (+15)", value: "+15"},
"extreme": {label: "Extreme (+20)", value: "+20"},
"beyondskill": {label: "Beyond Skill (+25)", value: "+25"}
}
export const ATTACKER_AIM_CHOICES = {
"simple": {label: "Simple (+0)", value: "0"},
"careful": {label: "Careful (-4)", value: "-4"},
"focused": {label: "Focused (-8)", value: "-8"}
}
export const DICE_VALUES = { export const DICE_VALUES = {
"d3": "D3", "d3": "D3",
"d4": "D4", "d4": "D4",
@@ -151,4 +188,9 @@ export const SYSTEM = {
CHOICE_MODIFIERS, CHOICE_MODIFIERS,
CHOICE_DICE, CHOICE_DICE,
DEV_MODE, DEV_MODE,
MOVEMENT_CHOICES,
MOVE_DIRECTION_CHOICES,
SIZE_CHOICES,
RANGE_CHOICES,
ATTACKER_AIM_CHOICES
} }

View File

@@ -88,6 +88,10 @@ export default class LethalFantasyRoll extends Roll {
return this.options.rollTarget return this.options.rollTarget
} }
get D30result() {
return this.options.D30result
}
/** /**
* Prompt the user with a dialog to configure and execute a roll. * Prompt the user with a dialog to configure and execute a roll.
* *
@@ -112,8 +116,11 @@ export default class LethalFantasyRoll extends Roll {
let modifierFormula = "1d0" let modifierFormula = "1d0"
let hasModifier = true let hasModifier = true
let hasChangeDice = false let hasChangeDice = false
let hasD30 = false
if (options.rollType === "challenge" || options.rollType === "save") { if (options.rollType === "challenge" || options.rollType === "save") {
options.rollName = options.rollTarget.rollKey options.rollName = options.rollTarget.rollKey
hasD30 = options.rollType === "save"
if (options.rollTarget.rollKey === "dying") { if (options.rollTarget.rollKey === "dying") {
dice = options.rollTarget.value dice = options.rollTarget.value
maxValue = Number(options.rollTarget.value.match(/\d+/)[0]) maxValue = Number(options.rollTarget.value.match(/\d+/)[0])
@@ -123,6 +130,7 @@ export default class LethalFantasyRoll extends Roll {
dice = "1D20" dice = "1D20"
maxValue = 20 maxValue = 20
} }
} else if (options.rollType === "skill") { } else if (options.rollType === "skill") {
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
dice = "1D100" dice = "1D100"
@@ -131,7 +139,9 @@ export default class LethalFantasyRoll extends Roll {
hasModifier = true hasModifier = true
hasChangeDice = false hasChangeDice = false
options.rollTarget.value = options.rollTarget.system.skillTotal options.rollTarget.value = options.rollTarget.system.skillTotal
} else if (options.rollType === "weapon-attack" || options.rollType === "weapon-defense") { } else if (options.rollType === "weapon-attack" || options.rollType === "weapon-defense") {
hasD30 = true
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
dice = "1D20" dice = "1D20"
baseFormula = "D20" baseFormula = "D20"
@@ -145,6 +155,29 @@ export default class LethalFantasyRoll extends Roll {
options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier
options.rollTarget.charModifier = options.rollTarget.combat.defenseModifier options.rollTarget.charModifier = options.rollTarget.combat.defenseModifier
} }
} else if (options.rollType === "spell") {
hasD30 = true
options.rollName = options.rollTarget.name
dice = "1D20"
baseFormula = "D20"
maxValue = 20
hasModifier = true
hasChangeDice = false
options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier + options.rollTarget.actorModifiers.intSpellModifier
options.rollTarget.charModifier = options.rollTarget.actorModifiers.intSpellModifier
} else if (options.rollType === "miracle") {
hasD30 = true
options.rollName = options.rollTarget.name
dice = "1D20"
baseFormula = "D20"
maxValue = 20
hasModifier = true
hasChangeDice = false
options.rollTarget.value = options.rollTarget.actorModifiers.levelMiracleModifier + options.rollTarget.actorModifiers.chaMiracleModifier
options.rollTarget.charModifier = options.rollTarget.actorModifiers.chaMiracleModifier
} else if (options.rollType.includes("weapon-damage")) { } else if (options.rollType.includes("weapon-damage")) {
options.rollName = options.rollTarget.name options.rollName = options.rollTarget.name
hasModifier = true hasModifier = true
@@ -174,7 +207,6 @@ export default class LethalFantasyRoll extends Roll {
default: "public", default: "public",
}) })
const choiceModifier = SYSTEM.CHOICE_MODIFIERS const choiceModifier = SYSTEM.CHOICE_MODIFIERS
const choiceDice = SYSTEM.CHOICE_DICE const choiceDice = SYSTEM.CHOICE_DICE
@@ -197,6 +229,7 @@ export default class LethalFantasyRoll extends Roll {
dice, dice,
hasTarget: options.hasTarget, hasTarget: options.hasTarget,
modifier, modifier,
saveSpell: false,
targetName targetName
} }
console.log("dialogContext", dialogContext) console.log("dialogContext", dialogContext)
@@ -230,8 +263,10 @@ export default class LethalFantasyRoll extends Roll {
let titleFormula = "" let titleFormula = ""
dice = rollContext.changeDice || dice dice = rollContext.changeDice || dice
if (hasModifier) { if (hasModifier) {
let bonus = Number(options.rollTarget.value) let bonus = Number(options.rollTarget.value)
fullModifier = rollContext.modifier === "" ? 0 : parseInt(rollContext.modifier, 10) + bonus fullModifier = rollContext.modifier === "" ? 0 : parseInt(rollContext.modifier, 10) + bonus
fullModifier += (rollContext.saveSpell) ? options.rollTarget.actorModifiers.saveModifier : 0
if (fullModifier === 0) { if (fullModifier === 0) {
modifierFormula = "0" modifierFormula = "0"
} else { } else {
@@ -282,13 +317,18 @@ export default class LethalFantasyRoll extends Roll {
if (Hooks.call("fvtt-lethal-fantasy.preRoll", options, rollData) === false) return if (Hooks.call("fvtt-lethal-fantasy.preRoll", options, rollData) === false) return
const rollBase = new this(baseFormula, options.data, rollData) const rollBase = new this(baseFormula, options.data, rollData)
await rollBase.evaluate()
const rollModifier = new Roll(modifierFormula, options.data, rollData) const rollModifier = new Roll(modifierFormula, options.data, rollData)
await rollModifier.evaluate() rollModifier.evaluate()
await rollBase.evaluate()
if (hasD30) {
let rollD30 = await new Roll("1D30").evaluate()
options.D30result = rollD30.total
}
let rollTotal = -1 let rollTotal = -1
let diceResults = [] let diceResults = []
let resultType let resultType
let diceResult = rollBase.dice[0].results[0].result let diceResult = rollBase.dice[0].results[0].result
diceResults.push({ dice: `${dice}`, value: diceResult }) diceResults.push({ dice: `${dice}`, value: diceResult })
let diceSum = diceResult let diceSum = diceResult
@@ -314,6 +354,7 @@ export default class LethalFantasyRoll extends Roll {
rollBase.options.diceResults = diceResults rollBase.options.diceResults = diceResults
rollBase.options.rollTarget = options.rollTarget rollBase.options.rollTarget = options.rollTarget
rollBase.options.titleFormula = titleFormula rollBase.options.titleFormula = titleFormula
rollBase.options.D30result = options.D30result
/** /**
* A hook event that fires after the roll has been made. * A hook event that fires after the roll has been made.
@@ -329,6 +370,130 @@ export default class LethalFantasyRoll extends Roll {
return rollBase return rollBase
} }
static async promptRangedDefense(rollTarget) {
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
blank: false,
default: "public",
})
let dialogContext = {
movementChoices : SYSTEM.MOVEMENT_CHOICES,
moveDirectionChoices : SYSTEM.MOVE_DIRECTION_CHOICES,
sizeChoices : SYSTEM.SIZE_CHOICES,
rangeChoices : SYSTEM.RANGE_CHOICES,
attackerAimChoices : SYSTEM.ATTACKER_AIM_CHOICES,
movement: "none",
moveDirection: "none",
size: "medium",
range: "short",
attackerAim: "simple",
fieldRollMode,
rollModes
}
console.log("CTX", dialogContext)
const content = await renderTemplate("systems/fvtt-lethal-fantasy/templates/range-defense-dialog.hbs", dialogContext)
const label = game.i18n.localize("LETHALFANTASY.Label.rangeDefenseRoll")
const rollContext = await foundry.applications.api.DialogV2.wait({
window: { title: "Range Defense" },
classes: ["lethalfantasy"],
content,
buttons: [
{
label: label,
callback: (event, button, dialog) => {
const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value
return obj
}, {})
return output
},
},
],
rejectClose: false // Click on Close button will not launch an error
})
console.log("RollContext", rollContext)
// Build the final modifier
let fullModifier = Number(rollContext.moveDirection) +
Number(rollContext.size) +
Number(rollContext.range) +
Number(rollContext.attackerAim)
console.log("Modifier", fullModifier)
let modifierFormula
if (fullModifier === 0) {
modifierFormula = "0"
} else {
let modAbs = Math.abs(fullModifier)
modifierFormula = `d${modAbs + 1} - 1`
}
// If the user cancels the dialog, exit
if (rollContext === null) return
let rollData = {...rollContext}
let options = {...rollContext}
options.rollName = "Ranged Defense"
const rollBase = new this(rollContext.movement, options.data, rollData)
const rollModifier = new Roll(modifierFormula, options.data, rollData)
rollModifier.evaluate()
await rollBase.evaluate()
let rollD30 = await new Roll("1D30").evaluate()
options.D30result = rollD30.total
let dice = rollContext.movement
let maxValue = Number(dice.match(/\d+$/)[0]) // Update the max value agains
let rollTotal = -1
let diceResults = []
let resultType
let diceResult = rollBase.dice[0].results[0].result
diceResults.push({ dice: `${dice}`, value: diceResult })
let diceSum = diceResult
while (diceResult === maxValue) {
let r = await new Roll(baseFormula).evaluate()
diceResult = r.dice[0].results[0].result
diceResults.push({ dice: `${dice}-1`, value: diceResult - 1 })
diceSum += (diceResult - 1)
}
if (fullModifier !== 0) {
diceResults.push({ dice: `${rollModifier.formula}`, value: rollModifier.total })
if (fullModifier < 0) {
rollTotal = Math.max(diceSum - rollModifier.total, 0)
} else {
rollTotal = diceSum + rollModifier.total
}
} else {
rollTotal = diceSum
}
rollBase.options.resultType = resultType
rollBase.options.rollTotal = rollTotal
rollBase.options.diceResults = diceResults
rollBase.options.rollTarget = options.rollTarget
rollBase.options.titleFormula = `${dice}E + ${modifierFormula}`
rollBase.options.D30result = options.D30result
rollBase.options.rollName = "Ranged Defense"
/**
* A hook event that fires after the roll has been made.
* @function
* @memberof hookEvents
* @param {Object} options Options for the roll.
* @param {Object} rollData All data related to the roll.
@param {LethalFantasyRoll} roll The resulting roll.
* @returns {boolean} Explicitly return `false` to prevent roll to be made.
*/
return rollBase
}
/** /**
* Creates a title based on the given type. * Creates a title based on the given type.
* *
@@ -352,6 +517,8 @@ export default class LethalFantasyRoll extends Roll {
return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-small")}` return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-small")}`
case "weapon-damage-medium": case "weapon-damage-medium":
return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-medium")}` return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-medium")}`
case "spell":
return `${game.i18n.localize("LETHALFANTASY.Label.spell")}`
default: default:
return game.i18n.localize("LETHALFANTASY.Label.titleStandard") return game.i18n.localize("LETHALFANTASY.Label.titleStandard")
} }
@@ -409,6 +576,7 @@ export default class LethalFantasyRoll extends Roll {
hasTarget: this.hasTarget, hasTarget: this.hasTarget,
targetName: this.targetName, targetName: this.targetName,
targetArmor: this.targetArmor, targetArmor: this.targetArmor,
D30result: this.D30result,
isPrivate: isPrivate isPrivate: isPrivate
} }
cardData.cssClass = cardData.css.join(" ") cardData.cssClass = cardData.css.join(" ")

View File

@@ -90,8 +90,18 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }), age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }),
height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 50 }), height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 50 }),
eyes: new fields.StringField({ required: true, nullable: false, initial: "" }), eyes: new fields.StringField({ required: true, nullable: false, initial: "" }),
hair: new fields.StringField({ required: true, nullable: false, initial: "" }) hair: new fields.StringField({ required: true, nullable: false, initial: "" }),
magicUser: new fields.BooleanField({ initial: false }),
}) })
schema.modifiers = new fields.SchemaField({
levelSpellModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
saveModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
levelMiracleModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
intSpellModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
chaMiracleModifier: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
})
schema.developmentPoints = new fields.SchemaField({ schema.developmentPoints = new fields.SchemaField({
total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
@@ -133,25 +143,36 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
grit += this.characteristics[c].value grit += this.characteristics[c].value
} }
} }
this.modifiers.saveModifier = Math.floor((Number(this.biodata.level) / 5))
this.modifiers.levelSpellModifier = Math.floor((Number(this.biodata.level) / 5))
this.modifiers.levelMiracleModifier = Math.floor((Number(this.biodata.level) / 5))
this.grit.starting = Math.round(grit / 6) this.grit.starting = Math.round(grit / 6)
let strDef = SYSTEM.CHARACTERISTICS_TABLES.str.find(s => s.value === this.characteristics.str.value) let strDef = SYSTEM.CHARACTERISTICS_TABLES.str.find(s => s.value === this.characteristics.str.value)
this.challenges.str.value = strDef.challenge this.challenges.str.value = strDef.challenge
let intDef = SYSTEM.CHARACTERISTICS_TABLES.int.find(s => s.value === this.characteristics.int.value)
this.modifiers.intSpellModifier = intDef.arkane_casting_mod
let dexDef = SYSTEM.CHARACTERISTICS_TABLES.dex.find(s => s.value === this.characteristics.dex.value) let dexDef = SYSTEM.CHARACTERISTICS_TABLES.dex.find(s => s.value === this.characteristics.dex.value)
this.challenges.agility.value = dexDef.challenge this.challenges.agility.value = dexDef.challenge
this.saves.dodge.value = dexDef.dodge this.saves.dodge.value = dexDef.dodge + this.modifiers.saveModifier
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find(s => s.value === this.characteristics.wis.value) let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find(s => s.value === this.characteristics.wis.value)
this.saves.will.value = wisDef.willpower_save this.saves.will.value = wisDef.willpower_save + this.modifiers.saveModifier
let chaDef = SYSTEM.CHARACTERISTICS_TABLES.cha.find(s => s.value === this.characteristics.cha.value)
this.modifiers.chaMiracleModifier = chaDef.divine_miracle_bonus
let conDef = SYSTEM.CHARACTERISTICS_TABLES.con.find(s => s.value === this.characteristics.con.value) let conDef = SYSTEM.CHARACTERISTICS_TABLES.con.find(s => s.value === this.characteristics.con.value)
this.saves.pain.value = conDef.pain_save this.saves.pain.value = conDef.pain_save + this.modifiers.saveModifier
this.saves.toughness.value = conDef.toughness_save this.saves.toughness.value = conDef.toughness_save + this.modifiers.saveModifier
this.challenges.dying.value = conDef.stabilization_dice this.challenges.dying.value = conDef.stabilization_dice
this.saves.contagion.value = this.characteristics.con.value this.saves.contagion.value = this.characteristics.con.value + this.modifiers.saveModifier
this.saves.poison.value = this.characteristics.con.value this.saves.poison.value = this.characteristics.con.value + this.modifiers.saveModifier
this.combat.attackModifier = 0 this.combat.attackModifier = 0
for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) { for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
packs/lf-gifts/MANIFEST-000078 LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
packs/lf-skills/MANIFEST-000082 LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -476,7 +476,33 @@
gap: 10px; gap: 10px;
.spell { .spell {
display: flex; display: flex;
align-items: center;
gap: 10px; gap: 10px;
.item-img {
width: 32px;
height: 32px;
}
.name {
min-width: 12rem;
}
}
}
.miracles {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
.miracle {
display: flex;
align-items: center;
gap: 10px;
.item-img {
width: 32px;
height: 32px;
}
.name {
min-width: 12rem;
}
} }
} }

View File

@@ -35,6 +35,25 @@
<span class="name">Hair</span> <span class="name">Hair</span>
{{formInput systemFields.biodata.fields.hair value=system.biodata.hair }} {{formInput systemFields.biodata.fields.hair value=system.biodata.hair }}
</div> </div>
<div class="biodata-elem">
<span class="name">Magic User</span>
{{formInput systemFields.biodata.fields.magicUser value=system.biodata.magicUser }}
</div>
<div class="biodata-elem">
<span class="name">Save bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.saveModifier value=system.modifiers.saveModifier disabled=true}}
</div>
{{#if system.biodata.magicUser}}
<div class="biodata-elem">
<span class="name">Spell bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.levelSpellModifier value=system.modifiers.levelSpellModifier disabled=true}}
</div>
<div class="biodata-elem">
<span class="name">Miracle bonus (1/5levels)</span>
{{formInput systemFields.modifiers.fields.levelMiracleModifier value=system.modifiers.levelMiracleModifier disabled=true}}
</div>
{{/if}}
</div> </div>

View File

@@ -6,11 +6,38 @@
data-action="createSpell"></i></a>{{/if}}</legend> data-action="createSpell"></i></a>{{/if}}</legend>
<div class="spells"> <div class="spells">
{{#each spells as |item|}} {{#each spells as |item|}}
{{!log 'weapon' this}} <div class="spell" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true" >
<div class="spell" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true" <img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
data-drag-type="spell"> <div class="name" >
<div class="name" data-tooltip="{{{item.system.description}}}"> <a class="rollable" data-roll-type="spell" data-roll-key="{{item.id}}">
<i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{item.name}} {{item.name}}
</a>
</div>
<div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
<a data-tooltip="{{localize 'LETHALFANTASY.Delete'}}" data-action="delete" data-item-id="{{item.id}}"
data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "LETHALFANTASY.Label.miracles"}}{{#if isEditMode}}<a class="action" data-tooltip="{{localize "
LETHALFANTASY.Tooltip.addMiracle"}}" data-tooltip-direction="UP"><i class="fas fa-plus"
data-action="createMiracle"></i></a>{{/if}}</legend>
<div class="miracles">
{{#each miracles as |item|}}
<div class="miracle" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true" >
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
<div class="name" >
<a class="rollable" data-roll-type="miracle" data-roll-key="{{item.id}}">
<i class="lf-roll-small fa-solid fa-dice-d20"></i>
{{item.name}}
</a>
</div> </div>
<div class="controls"> <div class="controls">
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}" <a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"

View File

@@ -1,8 +1,9 @@
<section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}"> <section class="tab character-{{tab.id}} {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
<button class="action" data-action="rangedAttackDefense">{{localize "LETHALFANTASY.Label.rangedAttackDefense"}}</button>
<fieldset> <fieldset>
<legend>{{localize "LETHALFANTASY.Label.weapons"}}</legend> <legend>{{localize "LETHALFANTASY.Label.weapons"}}</legend>
<div class="weapons"> <div class="weapons">
{{#each weapons as |item|}} {{#each weapons as |item|}}
<div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true" <div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"

View File

@@ -1,6 +0,0 @@
<div class="tenebris ask-roll">
<h4 class="ask-roll-title">{{title}}<br>{{text}}</h4>
<a class="ask-roll-dice" data-type="{{rollType}}" data-value="{{value}}" data-avantage="{{avantage}}">
<i class="fas fa-dice-d20" title="{{localize 'TENEBRIS.Manager.roll'}}"></i> {{localize 'TENEBRIS.Manager.roll'}}
</a>
</div>

View File

@@ -1,17 +0,0 @@
{{!log 'chat-fortune' this}}
<div class="tenebris fortune">
<div class="intro-chat">
{{#if (ne actingCharImg "icons/svg/mystery-man.svg")}}
<div class="intro-img">
<img src="{{actingCharImg}}" data-tooltip="{{name}}" />
</div>
{{/if}}
<div class="intro-right">
<p class="introText">{{localize "TENEBRIS.Chat.askFortune" name=name}}</p>
</div>
</div>
{{#if isGM}}
<a class="button control" data-action="accept-fortune" data-tooltip="Accepter"><i class="fa-solid fa-check"></i></a>
<div class="fortune-accepted" style="display: none;">Accepté !</div>
{{/if}}
</div>

View File

@@ -45,9 +45,16 @@
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
{{#unless isPrivate}} {{#unless isPrivate}}
<div class="dice-result"> <div class="dice-result">
<h4 class="dice-total">{{total}}</h4> <h4 class="dice-total">{{total}}</h4>
</div> </div>
{{#if D30result}}
<div class="dice-result">
<h4 class="dice-total">D30 result: {{D30result}}</h4>
</div>
{{/if}}
{{/unless}} {{/unless}}
</div> </div>

View File

@@ -0,0 +1,35 @@
<div class="lethalfantasy-range-defense-dialog">
<fieldSet class="">
<legend>{{localize "LETHALFANTASY.Label.rangeDefenseDialog"}}</legend>
<select name="movement" data-tooltip-direction="UP">
{{selectOptions movementChoices selected=movement}}
</select>
<select name="moveDirection" data-tooltip-direction="UP">
{{selectOptions moveDirectionChoices selected=moveDirection}}
</select>
<select name="size" data-tooltip-direction="UP">
{{selectOptions sizeChoices selected=size}}
</select>
<select name="range" data-tooltip-direction="UP">
{{selectOptions rangeChoices selected=range}}
</select>
<select name="attackerAim" data-tooltip-direction="UP">
{{selectOptions attackerAimChoices selected=attackerAim}}
</select>
</fieldSet>
<fieldSet>
<legend>{{localize "LETHALFANTASY.Roll.visibility"}}</legend>
<select name="visibility">
{{selectOptions rollModes selected=visibility}}
</select>
</fieldSet>
</div>

View File

@@ -1,4 +1,3 @@
{{log "roll-dialog" this}}
<div class="lethalfantasy-roll-dialog"> <div class="lethalfantasy-roll-dialog">
<fieldSet class=""> <fieldSet class="">
@@ -16,12 +15,22 @@
{{/if}} {{/if}}
</fieldSet> </fieldSet>
{{#if hasModifier}} {{#if hasModifier}}
<fieldSet class="dialog-modifier"> <fieldSet class="dialog-modifier">
<legend>{{localize "LETHALFANTASY.Roll.modifierBonusMalus"}}</legend> <legend>{{localize "LETHALFANTASY.Roll.modifierBonusMalus"}}</legend>
<select name="modifier" data-tooltip-direction="UP"> <select name="modifier" data-tooltip-direction="UP">
{{selectOptions choiceModifier selected=modifier}} {{selectOptions choiceModifier selected=modifier}}
</select> </select>
{{#if (eq rollType "save")}}
{{#if rollTarget.magicUser}}
<div>
<span>Save against spell (+{{rollTarget.actorModifiers.saveModifier}}) ?</span>
<input type="checkbox" name="saveSpell" value="saveSpell">
</div>
{{/if}}
{{/if}}
</fieldSet> </fieldSet>
{{/if}} {{/if}}