Add D30, spells, miracles and ranged defense roll
This commit is contained in:
@@ -566,8 +566,33 @@ i.lethalfantasy {
|
||||
}
|
||||
.lethalfantasy .tab.character-spells .spells .spell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -478,6 +478,10 @@
|
||||
"Platinums": "Platinum"
|
||||
},
|
||||
"Label": {
|
||||
"rangeDefenseDialog": "Range defense dialog",
|
||||
"rangeDefenseRoll": "Range defense roll",
|
||||
"rangedAttackDefense": "Ranged attack defense",
|
||||
"miracles": "Miracles",
|
||||
"biodata": "Biodata",
|
||||
"titleChallenge": "Challenge",
|
||||
"titleSave": "Save",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import LethalFantasyActorSheet from "./base-actor-sheet.mjs"
|
||||
import LethalFantasyRoll from "../../documents/roll.mjs"
|
||||
|
||||
export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet {
|
||||
/** @override */
|
||||
@@ -13,6 +14,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
|
||||
},
|
||||
actions: {
|
||||
createEquipment: LethalFantasyCharacterSheet.#onCreateEquipment,
|
||||
rangedAttackDefense: LethalFantasyCharacterSheet.#onRangedAttackDefense,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,12 +50,15 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
|
||||
* @returns {Record<string, Partial<ApplicationTab>>}
|
||||
*/
|
||||
#getTabs() {
|
||||
const tabs = {
|
||||
let tabs = {
|
||||
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" },
|
||||
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" },
|
||||
}
|
||||
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)) {
|
||||
v.active = this.tabGroups[v.group] === v.id
|
||||
v.cssClass = v.active ? "active" : ""
|
||||
@@ -95,6 +100,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
|
||||
case "spells":
|
||||
context.tab = context.tabs.spells
|
||||
context.spells = doc.itemTypes.spell
|
||||
context.miracles = doc.itemTypes.miracle
|
||||
context.hasSpells = context.spells.length > 0
|
||||
break
|
||||
case "weapons":
|
||||
@@ -135,21 +141,20 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
|
||||
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) {
|
||||
// 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) {
|
||||
@@ -168,7 +173,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
|
||||
goodSkill = s
|
||||
}
|
||||
}
|
||||
if (rollType.includes("weapon-damage") ) {
|
||||
if (rollType.includes("weapon-damage")) {
|
||||
if (s.system.weaponBonus.damage > maxValue) {
|
||||
maxValue = Number(s.system.weaponBonus.damage)
|
||||
goodSkill = s
|
||||
@@ -209,6 +214,14 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
|
||||
rollTarget.rollKey = rollKey
|
||||
rollTarget.rollDice = event.target.dataset?.rollDice
|
||||
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":
|
||||
rollTarget = this.actor.items.find((i) => i.type === "skill" && i.id === 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))
|
||||
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)
|
||||
}
|
||||
// #endregion
|
||||
|
||||
@@ -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 = {
|
||||
"d3": "D3",
|
||||
"d4": "D4",
|
||||
@@ -151,4 +188,9 @@ export const SYSTEM = {
|
||||
CHOICE_MODIFIERS,
|
||||
CHOICE_DICE,
|
||||
DEV_MODE,
|
||||
MOVEMENT_CHOICES,
|
||||
MOVE_DIRECTION_CHOICES,
|
||||
SIZE_CHOICES,
|
||||
RANGE_CHOICES,
|
||||
ATTACKER_AIM_CHOICES
|
||||
}
|
||||
|
||||
+172
-4
@@ -88,6 +88,10 @@ export default class LethalFantasyRoll extends Roll {
|
||||
return this.options.rollTarget
|
||||
}
|
||||
|
||||
get D30result() {
|
||||
return this.options.D30result
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 hasModifier = true
|
||||
let hasChangeDice = false
|
||||
let hasD30 = false
|
||||
|
||||
if (options.rollType === "challenge" || options.rollType === "save") {
|
||||
options.rollName = options.rollTarget.rollKey
|
||||
hasD30 = options.rollType === "save"
|
||||
if (options.rollTarget.rollKey === "dying") {
|
||||
dice = options.rollTarget.value
|
||||
maxValue = Number(options.rollTarget.value.match(/\d+/)[0])
|
||||
@@ -123,6 +130,7 @@ export default class LethalFantasyRoll extends Roll {
|
||||
dice = "1D20"
|
||||
maxValue = 20
|
||||
}
|
||||
|
||||
} else if (options.rollType === "skill") {
|
||||
options.rollName = options.rollTarget.name
|
||||
dice = "1D100"
|
||||
@@ -131,7 +139,9 @@ export default class LethalFantasyRoll extends Roll {
|
||||
hasModifier = true
|
||||
hasChangeDice = false
|
||||
options.rollTarget.value = options.rollTarget.system.skillTotal
|
||||
|
||||
} else if (options.rollType === "weapon-attack" || options.rollType === "weapon-defense") {
|
||||
hasD30 = true
|
||||
options.rollName = options.rollTarget.name
|
||||
dice = "1D20"
|
||||
baseFormula = "D20"
|
||||
@@ -145,6 +155,29 @@ export default class LethalFantasyRoll extends Roll {
|
||||
options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier
|
||||
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")) {
|
||||
options.rollName = options.rollTarget.name
|
||||
hasModifier = true
|
||||
@@ -174,7 +207,6 @@ export default class LethalFantasyRoll extends Roll {
|
||||
default: "public",
|
||||
})
|
||||
|
||||
|
||||
const choiceModifier = SYSTEM.CHOICE_MODIFIERS
|
||||
const choiceDice = SYSTEM.CHOICE_DICE
|
||||
|
||||
@@ -197,6 +229,7 @@ export default class LethalFantasyRoll extends Roll {
|
||||
dice,
|
||||
hasTarget: options.hasTarget,
|
||||
modifier,
|
||||
saveSpell: false,
|
||||
targetName
|
||||
}
|
||||
console.log("dialogContext", dialogContext)
|
||||
@@ -230,8 +263,10 @@ export default class LethalFantasyRoll extends Roll {
|
||||
let titleFormula = ""
|
||||
dice = rollContext.changeDice || dice
|
||||
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.saveSpell) ? options.rollTarget.actorModifiers.saveModifier : 0
|
||||
|
||||
if (fullModifier === 0) {
|
||||
modifierFormula = "0"
|
||||
} else {
|
||||
@@ -282,13 +317,18 @@ export default class LethalFantasyRoll extends Roll {
|
||||
if (Hooks.call("fvtt-lethal-fantasy.preRoll", options, rollData) === false) return
|
||||
|
||||
const rollBase = new this(baseFormula, options.data, rollData)
|
||||
await rollBase.evaluate()
|
||||
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 diceResults = []
|
||||
let resultType
|
||||
|
||||
let diceResult = rollBase.dice[0].results[0].result
|
||||
diceResults.push({ dice: `${dice}`, value: diceResult })
|
||||
let diceSum = diceResult
|
||||
@@ -314,6 +354,7 @@ export default class LethalFantasyRoll extends Roll {
|
||||
rollBase.options.diceResults = diceResults
|
||||
rollBase.options.rollTarget = options.rollTarget
|
||||
rollBase.options.titleFormula = titleFormula
|
||||
rollBase.options.D30result = options.D30result
|
||||
|
||||
/**
|
||||
* A hook event that fires after the roll has been made.
|
||||
@@ -329,6 +370,130 @@ export default class LethalFantasyRoll extends Roll {
|
||||
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.
|
||||
*
|
||||
@@ -352,6 +517,8 @@ export default class LethalFantasyRoll extends Roll {
|
||||
return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-small")}`
|
||||
case "weapon-damage-medium":
|
||||
return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-medium")}`
|
||||
case "spell":
|
||||
return `${game.i18n.localize("LETHALFANTASY.Label.spell")}`
|
||||
default:
|
||||
return game.i18n.localize("LETHALFANTASY.Label.titleStandard")
|
||||
}
|
||||
@@ -409,6 +576,7 @@ export default class LethalFantasyRoll extends Roll {
|
||||
hasTarget: this.hasTarget,
|
||||
targetName: this.targetName,
|
||||
targetArmor: this.targetArmor,
|
||||
D30result: this.D30result,
|
||||
isPrivate: isPrivate
|
||||
}
|
||||
cardData.cssClass = cardData.css.join(" ")
|
||||
|
||||
@@ -90,8 +90,18 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
|
||||
age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }),
|
||||
height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 50 }),
|
||||
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({
|
||||
total: 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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
let strDef = SYSTEM.CHARACTERISTICS_TABLES.str.find(s => s.value === this.characteristics.str.value)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
this.saves.pain.value = conDef.pain_save
|
||||
this.saves.toughness.value = conDef.toughness_save
|
||||
this.challenges.dying.value = conDef.stabilization_dice
|
||||
this.saves.pain.value = conDef.pain_save + this.modifiers.saveModifier
|
||||
this.saves.toughness.value = conDef.toughness_save + this.modifiers.saveModifier
|
||||
this.challenges.dying.value = conDef.stabilization_dice
|
||||
|
||||
this.saves.contagion.value = this.characteristics.con.value
|
||||
this.saves.poison.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.modifiers.saveModifier
|
||||
|
||||
this.combat.attackModifier = 0
|
||||
for (let chaKey of SYSTEM.CHARACTERISTIC_ATTACK) {
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
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.
@@ -476,7 +476,33 @@
|
||||
gap: 10px;
|
||||
.spell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,25 @@
|
||||
<span class="name">Hair</span>
|
||||
{{formInput systemFields.biodata.fields.hair value=system.biodata.hair }}
|
||||
</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>
|
||||
|
||||
@@ -6,11 +6,38 @@
|
||||
data-action="createSpell"></i></a>{{/if}}</legend>
|
||||
<div class="spells">
|
||||
{{#each spells as |item|}}
|
||||
{{!log 'weapon' this}}
|
||||
<div class="spell" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"
|
||||
data-drag-type="spell">
|
||||
<div class="name" data-tooltip="{{{item.system.description}}}">
|
||||
<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}}" />
|
||||
<div class="name" >
|
||||
<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}}
|
||||
</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 class="controls">
|
||||
<a data-tooltip="{{localize 'LETHALFANTASY.Edit'}}" data-action="edit" data-item-id="{{item.id}}"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<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>
|
||||
<legend>{{localize "LETHALFANTASY.Label.weapons"}}</legend>
|
||||
|
||||
<div class="weapons">
|
||||
{{#each weapons as |item|}}
|
||||
<div class="weapon" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-drag="true"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -45,9 +45,16 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#unless isPrivate}}
|
||||
<div class="dice-result">
|
||||
<h4 class="dice-total">{{total}}</h4>
|
||||
</div>
|
||||
{{#if D30result}}
|
||||
<div class="dice-result">
|
||||
<h4 class="dice-total">D30 result: {{D30result}}</h4>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
@@ -1,4 +1,3 @@
|
||||
{{log "roll-dialog" this}}
|
||||
<div class="lethalfantasy-roll-dialog">
|
||||
|
||||
<fieldSet class="">
|
||||
@@ -16,12 +15,22 @@
|
||||
{{/if}}
|
||||
</fieldSet>
|
||||
|
||||
|
||||
{{#if hasModifier}}
|
||||
<fieldSet class="dialog-modifier">
|
||||
<legend>{{localize "LETHALFANTASY.Roll.modifierBonusMalus"}}</legend>
|
||||
<select name="modifier" data-tooltip-direction="UP">
|
||||
{{selectOptions choiceModifier selected=modifier}}
|
||||
</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>
|
||||
{{/if}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user