Add rolls and new fixes
All checks were successful
Release Creation / build (release) Successful in 1m2s

This commit is contained in:
2025-05-25 00:01:39 +02:00
parent 4bed84358b
commit 585b9a1ab5
20 changed files with 389 additions and 223 deletions

View File

@@ -73,6 +73,8 @@ export default class HellbornCharacterSheet extends HellbornActorSheet {
const doc = this.document
context.trait = doc.itemTypes['species-trait']?.[0]
context.upright = doc.itemTypes.tarot.find(t => t.system.orientation === "Upright")
context.downright = doc.itemTypes.tarot.find(t => t.system.orientation === "Downright")
return context
}
@@ -108,8 +110,6 @@ export default class HellbornCharacterSheet extends HellbornActorSheet {
case "biography":
context.tab = context.tabs.biography
context.deals = doc.itemTypes.deal
context.deals.sort((a, b) => a.name.localeCompare(b.name))
context.tarot = doc.itemTypes.tarot?.[0]
context.enrichedBackstory = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.backstory, { async: true })
context.enrichedAppearance = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.appearance, { async: true })
context.enrichedScars = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.scars, { async: true })
@@ -199,7 +199,7 @@ export default class HellbornCharacterSheet extends HellbornActorSheet {
const item = await fromUuid(data.uuid)
if (item.type === "tarot") {
// Delete the existing tarot item
const existingTarot = this.document.items.find(i => i.type === "tarot")
const existingTarot = this.document.items.find(i => i.type === "tarot" && i.system.orientation === item.system.orientation)
if (existingTarot) {
await existingTarot.delete()
// Display info message

View File

@@ -6,7 +6,7 @@ export default class HellbornTarotSheet extends HellbornItemSheet {
classes: ["tarot"],
position: {
width: 800,
height: 640
height: 800
},
window: {
contentClasses: ["tarot-content"],
@@ -23,8 +23,9 @@ export default class HellbornTarotSheet extends HellbornItemSheet {
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.enrichedPositiveEffect = await TextEditor.enrichHTML(this.document.system.positiveEffect, { async: true })
context.enrichedNegativeEffect = await TextEditor.enrichHTML(this.document.system.negativeEffect, { async: true })
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedPositiveEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.positiveEffect, { async: true })
context.enrichedNegativeEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.negativeEffect, { async: true })
return context
}
}

View File

@@ -31,6 +31,25 @@ export const STATS = {
"soul": { id: "soul", label: "Soul" }
}
export const DIFFICULTY_CHOICES = {
"unknown": { id: "unknown", label: "Unknown", value: "0" },
"trivial": { id: "trivial", label: "Trivial", value: "7" },
"easy": { id: "easy", label: "Easy", value: "9" },
"normal": { id: "normal", label: "Normal", value: "11" },
"hard": { id: "hard", label: "Hard", value: "15" },
"very-hard": { id: "very-hard", label: "Very Hard", value: "18" },
"impossible": { id: "impossible", label: "Impossible", value: "20" }
}
export const CHOICE_ADVANTAGES_DISADVANTAGES ={
"0": { id: "0", label: "None", value: "0" },
"1": { id: "1", label: "One", value: "1" },
"2": { id: "2", label: "Two", value: "2" },
"3": { id: "3", label: "Three", value: "3" },
"4": { id: "4", label: "Four", value: "4" },
"5": { id: "5", label: "Five", value: "5" },
}
export const PERK_ROLES = {
"abbetor": { id: "abbetor", label: "Abbetor" },
"blade": { id: "blade", label: "Blade" },
@@ -71,34 +90,6 @@ export const WEAPON_TYPES = {
"ranged": { id: "ranged", label: "Ranged" },
}
export const RANGED_SUBTYPES = {
"pistols": { id: "pistols", label: "Pistols" },
"rifles": { id: "rifles", label: "Rifles" },
"shotguns": { id: "shotguns", label: "Shotguns" },
"submachineguns": { id: "submachineguns ", label: "Sub machine guns" },
"grenades": { id: "grenades", label: "Grenades" },
"heavy": { id: "heavy", label: "Heavy Weapons" },
}
export const ATTACK_MODIFIERS = {
"two-attacks": -1,
"aiming": 1,
"dim": -1,
"darkness": -2,
"prone": -1,
"cover": -2,
"recoil-first": -1,
"recoil-third": -2,
"aware": -1
}
export const MODIFIER_CHOICES = {
"easy": { id: "easy", label: "HELLBORN.Label.Easy", value :"1" },
"moderate": { id: "moderate", label: "HELLBORN.Label.Moderate", value: "0" },
"difficult": { id: "difficult", label: "HELLBORN.Label.Difficult", value: "-1" },
"formidable": { id: "formidable", label: "HELLBORN.Label.Formidable", value: "-2" },
"impossible": { id: "impossible", label: "HELLBORN.Label.Impossible", value: "-4" }
}
/**
* Include all constant definitions within the SYSTEM global export
@@ -112,9 +103,8 @@ export const SYSTEM = {
PERK_LEVELS,
MALEFICA_LEVELS,
MALEFICA_DOMAINS,
MODIFIER_CHOICES,
ATTACK_MODIFIERS,
WEAPON_TYPES,
RANGED_SUBTYPES,
DIFFICULTY_CHOICES,
CHOICE_ADVANTAGES_DISADVANTAGES,
ASCII
}

View File

@@ -62,11 +62,7 @@ export default class HellbornRoll extends Roll {
static updateFullFormula(options) {
let fullFormula
if ( options.numericModifier >= 0) {
fullFormula = `${options.formula} + ${options.rollItem.value} + ${options.numericModifier}D`
} else {
fullFormula = `${options.formula} + ${options.rollItem.value} - ${Math.abs(options.numericModifier)}D`
}
fullFormula = `3D6 + ${options.nbAdvantages}D6kh - ${options.nbDisadvantages}D6kh + ${options.rollItem.value}`
$('#roll-dialog-full-formula').text(fullFormula)
options.fullFormula = fullFormula
}
@@ -86,81 +82,68 @@ export default class HellbornRoll extends Roll {
* @returns {Promise<Object|null>} The roll result or null if the dialog was cancelled.
*/
static async prompt(options = {}) {
let formula = "2d6"
let formula = `3D6 + 0D6KH - 0D6KH + ${options?.rollItem?.value}`
switch (options.rollType) {
case "skill":
case "stat":
break
case "damage":
let formula = options.rollItem.system.damage
let damageRoll = new Roll(formula)
await damageRoll.evaluate()
await damageRoll.toMessage({
flavor: `${options.rollItem.name} - Damage Roll`
});
return
{
let formula = options.rollItem.system.damage
let damageRoll = new Roll(formula)
await damageRoll.evaluate()
await damageRoll.toMessage({
flavor: `${options.rollItem.name} - Damage Roll`
});
return
}
case "weapon":
let actor = game.actors.get(options.actorId)
options.weapon = foundry.utils.duplicate(options.rollItem)
options.rollItem = actor.system.skills.combat
{
let actor = game.actors.get(options.actorId)
options.weapon = foundry.utils.duplicate(options.rollItem)
let statKey = "skin"
if (options.weapon.system.weaponType === "melee") {
if ( options.weapon.system.properties.toLowerCase().match("heavy") || options.weapon.system.properties.toLowerCase().match("oversized")) {
statKey = "flesh"
}
}
options.rollItem = actor.system.stats[statKey]
}
break
default:
break
}
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
blank: false,
default: "public",
})
const choiceModifier = SYSTEM.MODIFIER_CHOICES
let choiceRangeModifier = {}
let rangeModifier = 0
if ( options.weapon) {
// Build the range modifiers
let range = SYSTEM.WEAPON_RANGE[options.weapon.system.rangeType]
for (let [key, value] of Object.entries(range.range)) {
choiceRangeModifier[key] = { label: `${key} (${value}D)`, value: value }
if (!rangeModifier && value) {
rangeModifier = value
}
}
}
let modifier = "0"
options.numericModifier = rangeModifier
let fullFormula = `${formula} + ${options.rollItem.value}`
if (options.isEncumbered) {
options.numericModifier += -1
fullFormula += ` - ${options.numericModifier}D`
} else {
options.numericModifier += 0
fullFormula += ` + ${options.numericModifier}D`
}
options.fullFormula = fullFormula
options.formula = formula
options.nbAdvantages = "0"
options.nbDisadvantages = "0"
let dialogContext = {
actorId: options.actorId,
actorName: options.actorName,
rollType: options.rollType,
rollItem: foundry.utils.duplicate(options.rollItem), // Object only, no class
fullFormula,
weapon: options?.weapon,
isEncumbered: options.isEncumbered,
talents: options.talents,
formula: formula,
fullFormula: formula,
rollModes,
fieldRollMode,
choiceModifier,
choiceRangeModifier,
rangeModifier,
formula,
difficultyChoices: SYSTEM.DIFFICULTY_CHOICES,
choiceAdvantages: SYSTEM.CHOICE_ADVANTAGES_DISADVANTAGES,
choiceDisadvantages: SYSTEM.CHOICE_ADVANTAGES_DISADVANTAGES,
hasTarget: options.hasTarget,
modifier,
difficulty: "unknown",
nbAdvantages: "0",
nbDisadvantages: "0",
}
const content = await renderTemplate("systems/fvtt-hellborn/templates/roll-dialog.hbs", dialogContext)
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hellborn/templates/roll-dialog.hbs", dialogContext)
const title = HellbornRoll.createTitle(options.rollType, options.rollTarget)
const label = game.i18n.localize("HELLBORN.Roll.roll")
@@ -184,19 +167,19 @@ export default class HellbornRoll extends Roll {
},
rejectClose: false, // Click on Close button will not launch an error
render: (event, dialog) => {
$(".roll-skill-modifier").change(event => {
options.numericModifier += Number(event.target.value)
$(".roll-stat-advantages").change(event => {
options.nbAdvantages = Number(event.target.value)
HellbornRoll.updateFullFormula(options)
})
$(".roll-skill-range-modifier").change(event => {
options.numericModifier += Number(event.target.value)
$(".roll-stat-disadvantages").change(event => {
options.nbDisadvantages = Number(event.target.value)
HellbornRoll.updateFullFormula(options)
})
$(".select-combat-option").change(event => {
console.log(event)
let field = $(event.target).data("field")
let modifier = SYSTEM.ATTACK_MODIFIERS[field]
if ( event.target.checked) {
if (event.target.checked) {
options.numericModifier += modifier
} else {
options.numericModifier -= modifier
@@ -211,42 +194,55 @@ export default class HellbornRoll extends Roll {
let rollData = foundry.utils.mergeObject(foundry.utils.duplicate(options), rollContext)
rollData.rollMode = rollContext.visibility
// Update target score
rollData.targetScore = 8
if (Hooks.call("fvtt-hellborn.preRoll", options, rollData) === false) return
let diceFormula = `${2+Math.abs(options.numericModifier)}D6`
if ( options.numericModifier > 0 ) {
diceFormula += `kh2 + ${options.rollItem.value}`
} else {
diceFormula += `kl2 + ${options.rollItem.value}`
}
options.nbAdvantages = Number(options.nbAdvantages)
options.nbDisadvantages = Number(options.nbDisadvantages)
let diceFormula = `3D6 + ${options.nbAdvantages}D6kh - ${options.nbDisadvantages}D6kh + ${options.rollItem.value}`
const roll = new this(diceFormula, options.data, rollData)
await roll.evaluate()
console.log("Roll", rollData, roll)
options.difficulty = (rollData.difficulty === "unknown") ? 0 : (Number(rollData.difficulty) || 0)
roll.displayRollResult(roll, options, rollData)
roll.displayRollResult(roll, options, rollData, roll)
if (Hooks.call("fvtt-hellborn.Roll", options, rollData, roll) === false) return
return roll
}
displayRollResult(formula, options, rollData) {
// Compute the result quality
displayRollResult(formula, options, rollData, roll) {
let resultType = "failure"
if (this.total >= 8) {
if (options.difficulty === 0) {
resultType = "unknown"
} else if (this.total >= options.difficulty) {
resultType = "success"
// Detect if decimal == unit in the dire total result
}
// Compute the result quality
this.options.satanicSuccess = false
this.options.fiendishFailure = false
this.options.rollData = foundry.utils.duplicate(rollData)
if (resultType === "success") {
let nb6 = roll.terms[0].results.filter(r => r.result === 6).length
nb6 += roll.terms[3].total === 6 ? 1 : 0
this.options.satanicSuccess = nb6 >= 3
if (this.options.satanicSuccess) {
resultType = "success"
}
}
if (resultType === "failure") {
let nb1 = roll.terms[0].results.filter(r => r.result === 1).length
nb1 += roll.terms[5].total === 1 ? 1 : 0
this.options.fiendishFailure = nb1 >= 3
if (this.options.fiendishFailure) {
resultType = "failure"
}
}
this.options.resultType = resultType
this.options.isSuccess = resultType === "success"
this.options.isFailure = resultType === "failure"
this.options.isEncumbered = rollData.isEncumbered
this.options.rollData = foundry.utils.duplicate(rollData)
}
/**
@@ -258,8 +254,8 @@ export default class HellbornRoll extends Roll {
*/
static createTitle(type, target) {
switch (type) {
case "skill":
return `${game.i18n.localize("HELLBORN.Label.titleSkill")}`
case "stat":
return `${game.i18n.localize("HELLBORN.Label.titleStat")}`
case "weapon":
return `${game.i18n.localize("HELLBORN.Label.titleWeapon")}`
default:
@@ -270,7 +266,7 @@ export default class HellbornRoll extends Roll {
/** @override */
async render(chatOptions = {}) {
let chatData = await this._getChatCardData(chatOptions.isPrivate)
return await renderTemplate(this.constructor.CHAT_TEMPLATE, chatData)
return await foundry.applications.handlebars.renderTemplate(this.constructor.CHAT_TEMPLATE, chatData)
}
/**
@@ -317,7 +313,6 @@ export default class HellbornRoll extends Roll {
cardData.realDamage = this.realDamage
cardData.isPrivate = isPrivate
cardData.weapon = this.weapon
cardData.isEncumbered = this.isEncumbered
cardData.cssClass = cardData.css.join(" ")
cardData.tooltip = isPrivate ? "" : await this.getTooltip()

View File

@@ -56,9 +56,12 @@ export default class HellbornActor extends foundry.abstract.TypeDataModel {
schema.species = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.trait = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.limboes = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.ammo = new fields.StringField({ required: true, nullable: false, initial: "" })
schema.mortality = new fields.SchemaField({
current: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
})
schema.trauma = new fields.SchemaField({
@@ -134,7 +137,6 @@ export default class HellbornActor extends foundry.abstract.TypeDataModel {
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
talents: this.parent.items.filter(i => i.type === "talent" && i.system.isAdvantage),
hasTarget,
target: opponentTarget
})

View File

@@ -5,7 +5,12 @@ export default class HellbornTarot extends foundry.abstract.TypeDataModel {
const fields = foundry.data.fields;
const schema = {};
schema.orientation = new fields.StringField({ required: true, initial: "Upright", choices: {"Upright": {label: "Upright"}, "Reversed": {label: "Reversed"}} });
schema.quote = new fields.StringField({ required: true, nullable: false, initial: "" });
schema.bonus = new fields.StringField({ required: true, nullable: false, initial: "" });
schema.penalty = new fields.StringField({ required: true, nullable: false, initial: "" });
schema.orientation = new fields.StringField({ required: true, initial: "Upright", choices: {"Upright": {label: "Upright"}, "Downright": {label: "Downright"}} });
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.positiveEffect = new fields.HTMLField({ required: true, textSearch: true })
schema.negativeEffect = new fields.HTMLField({ required: true, textSearch: true })
schema.image = new fields.FilePathField({

View File

@@ -168,7 +168,22 @@ export default class HellbornUtils {
Handlebars.registerHelper('isCreature', function (key) {
return key === "creature" || key === "daemon";
})
Handlebars.registerHelper('getRomanLevel', function (level) {
if (level=== "highpowers") return "High";
if (level === "mastery") return "Mastery";
level = parseInt(level);
if (level === 0) return "I";
if (level === 1) return "II";
if (level === 2) return "III";
if (level === 3) return "IV";
if (level === 4) return "V";
if (level === 5) return "VI";
if (level === 6) return "VII";
if (level === 7) return "VIII";
if (level === 8) return "IX";
if (level === 9) return "X";
return level;
})
// Handle v12 removal of this helper
Handlebars.registerHelper('select', function (selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));