mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 07:36:26 +01:00
Merged with main
This commit is contained in:
commit
ce4a08d318
27 changed files with 414 additions and 170 deletions
25
lang/en.json
25
lang/en.json
|
|
@ -686,11 +686,11 @@
|
|||
}
|
||||
},
|
||||
"RollTypes": {
|
||||
"ability": {
|
||||
"name": "Ability"
|
||||
"trait": {
|
||||
"name": "Trait"
|
||||
},
|
||||
"weapon": {
|
||||
"name": "Weapon"
|
||||
"attack": {
|
||||
"name": "Attack"
|
||||
},
|
||||
"spellcast": {
|
||||
"name": "SpellCast"
|
||||
|
|
@ -1015,8 +1015,8 @@
|
|||
"allDamage": "All Damage",
|
||||
"physicalDamage": "Physical Damage",
|
||||
"magicalDamage": "Magical Damage",
|
||||
"primaryDamageBonus": "Primary Weapon Damage",
|
||||
"primaryDamageDice": "Primary Weapon Extra Damage Dice"
|
||||
"primaryWeapon": "Primary Weapon Damage",
|
||||
"secondaryWeapon": "Secondary Weapon Damage"
|
||||
},
|
||||
"DamageResistance": {
|
||||
"none": "None",
|
||||
|
|
@ -1093,10 +1093,18 @@
|
|||
"single": "Experience",
|
||||
"plural": "Experiences"
|
||||
},
|
||||
"Healing": {
|
||||
"healingAmount": "Healing Amount"
|
||||
},
|
||||
"Neutral": {
|
||||
"full": "None",
|
||||
"short": "no"
|
||||
},
|
||||
"Range": {
|
||||
"other": "Range Increase: Other",
|
||||
"spell": "Range Increase: Spell",
|
||||
"weapon": "Range Increase: Weapon"
|
||||
},
|
||||
"RefreshType": {
|
||||
"session": "Session",
|
||||
"shortrest": "Short Rest",
|
||||
|
|
@ -1109,9 +1117,11 @@
|
|||
"Roll": {
|
||||
"attack": "Attack Roll",
|
||||
"primaryWeaponAttack": "Primary Weapon Attack Roll",
|
||||
"secondaryWeaponAttack": "Secondary Weapon Attack Roll",
|
||||
"spellcast": "Spellcast Roll",
|
||||
"trait": "Trait Roll",
|
||||
"action": "Action Roll",
|
||||
"hopeOrFear": "Hope Or Fear Roll"
|
||||
"reaction": "Reaction Roll"
|
||||
},
|
||||
"Rules": {
|
||||
"damageReduction": {
|
||||
|
|
@ -1186,6 +1196,7 @@
|
|||
},
|
||||
"armorScore": "Armor Score",
|
||||
"activeEffects": "Active Effects",
|
||||
"armorSlots": "Armor Slots",
|
||||
"attack": "Attack",
|
||||
"basics": "Basics",
|
||||
"bonus": "Bonus",
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
|
||||
element.addEventListener('change', this.updateItemQuantity.bind(this));
|
||||
});
|
||||
|
||||
// Add listener for armor marks input
|
||||
htmlElement.querySelectorAll('.armor-marks-input').forEach(element => {
|
||||
element.addEventListener('change', this.updateArmorMarks.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
|
@ -515,6 +520,16 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
this.render();
|
||||
}
|
||||
|
||||
async updateArmorMarks(event) {
|
||||
const armor = this.document.system.armor;
|
||||
if (!armor) return;
|
||||
|
||||
const maxMarks = this.document.system.armorScore;
|
||||
const value = Math.min(Math.max(Number(event.currentTarget.value), 0), maxMarks);
|
||||
await armor.update({ 'system.marks.value': value });
|
||||
this.render();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Application Clicks Actions */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
}
|
||||
|
||||
addChatListeners = async (app, html, data) => {
|
||||
super.addChatListeners(app, html, data);
|
||||
|
||||
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -388,17 +388,17 @@ export const countdownTypes = {
|
|||
}
|
||||
};
|
||||
export const rollTypes = {
|
||||
weapon: {
|
||||
id: 'weapon',
|
||||
label: 'DAGGERHEART.CONFIG.RollTypes.weapon.name'
|
||||
attack: {
|
||||
id: 'attack',
|
||||
label: 'DAGGERHEART.CONFIG.RollTypes.attack.name'
|
||||
},
|
||||
spellcast: {
|
||||
id: 'spellcast',
|
||||
label: 'DAGGERHEART.CONFIG.RollTypes.spellcast.name'
|
||||
},
|
||||
ability: {
|
||||
id: 'ability',
|
||||
label: 'DAGGERHEART.CONFIG.RollTypes.ability.name'
|
||||
trait: {
|
||||
id: 'trait',
|
||||
label: 'DAGGERHEART.CONFIG.RollTypes.trait.name'
|
||||
},
|
||||
diceSet: {
|
||||
id: 'diceSet',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
static extraSchemas = [...super.extraSchemas, ...['roll', 'save']];
|
||||
|
||||
static getRollType(parent) {
|
||||
return parent.type === 'weapon' ? 'weapon' : 'spellcast';
|
||||
return parent.type === 'weapon' ? 'attack' : 'spellcast';
|
||||
}
|
||||
|
||||
get chatTemplate() {
|
||||
|
|
@ -21,7 +21,7 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
}
|
||||
if (this.roll.useDefault) {
|
||||
this.roll.trait = this.item.system.attack.roll.trait;
|
||||
this.roll.type = 'weapon';
|
||||
this.roll.type = 'attack';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,4 +37,8 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
base: true
|
||||
};
|
||||
}
|
||||
|
||||
// get modifiers() {
|
||||
// return [];
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
static getRollType(parent) {
|
||||
return 'ability';
|
||||
return 'trait';
|
||||
}
|
||||
|
||||
static getSourceConfig(parent) {
|
||||
|
|
@ -308,7 +308,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
|
||||
prepareRoll() {
|
||||
const roll = {
|
||||
modifiers: [],
|
||||
modifiers: this.modifiers,
|
||||
trait: this.roll?.trait,
|
||||
label: 'Attack',
|
||||
type: this.actionType,
|
||||
|
|
@ -362,6 +362,13 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
get hasRoll() {
|
||||
return !!this.roll?.type || !!this.roll?.bonus;
|
||||
}
|
||||
|
||||
get modifiers() {
|
||||
if (!this.actor) return [];
|
||||
const modifiers = [];
|
||||
/** Placeholder for specific bonuses **/
|
||||
return modifiers;
|
||||
}
|
||||
/* ROLL */
|
||||
|
||||
/* SAVE */
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
hasSave: this.hasSave,
|
||||
isCritical: data.system?.roll?.isCritical ?? false,
|
||||
source: data.system?.source,
|
||||
data: this.getRollData(),
|
||||
damageTypes,
|
||||
event
|
||||
};
|
||||
|
|
@ -39,4 +40,8 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
|
||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||
}
|
||||
|
||||
// get modifiers() {
|
||||
// return [];
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,4 +39,8 @@ export default class DHHealingAction extends DHBaseAction {
|
|||
get chatTemplate() {
|
||||
return 'systems/daggerheart/templates/ui/chat/healing-roll.hbs';
|
||||
}
|
||||
|
||||
get modifiers() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
const resourceField = label =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true, label }),
|
||||
max: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
isReversed: new foundry.data.fields.BooleanField({ initial: true })
|
||||
});
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhpAdversary extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Adversary'];
|
||||
|
|
@ -58,8 +52,8 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
})
|
||||
}),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: resourceField('DAGGERHEART.GENERAL.hitPoints'),
|
||||
stress: resourceField('DAGGERHEART.GENERAL.stress')
|
||||
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
|
||||
stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true)
|
||||
}),
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
|
|
@ -74,7 +68,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
amount: 1
|
||||
},
|
||||
roll: {
|
||||
type: 'weapon'
|
||||
type: 'attack'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
|
|
@ -95,17 +89,14 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
})
|
||||
),
|
||||
bonuses: new fields.SchemaField({
|
||||
difficulty: new fields.SchemaField({
|
||||
all: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Difficulty.all'
|
||||
}),
|
||||
reaction: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Difficulty.reaction'
|
||||
})
|
||||
roll: new fields.SchemaField({
|
||||
attack: bonusField('DAGGERHEART.GENERAL.Roll.attack'),
|
||||
action: bonusField('DAGGERHEART.GENERAL.Roll.action'),
|
||||
reaction: bonusField('DAGGERHEART.GENERAL.Roll.reaction')
|
||||
}),
|
||||
damage: new fields.SchemaField({
|
||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage')
|
||||
})
|
||||
})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,29 +2,7 @@ import { burden } from '../../config/generalConfig.mjs';
|
|||
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
||||
import DhLevelData from '../levelData.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
|
||||
const attributeField = label =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true, label: label }),
|
||||
tierMarked: new foundry.data.fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = (max, label, reverse = false) =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true, label: label }),
|
||||
max: new foundry.data.fields.NumberField({ initial: max, integer: true }),
|
||||
isReversed: new foundry.data.fields.BooleanField({ initial: reverse })
|
||||
});
|
||||
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
enabled: new foundry.data.fields.BooleanField({ required: true, initial: false }),
|
||||
cost: new foundry.data.fields.NumberField({
|
||||
integer: true,
|
||||
label: `${localizationPath}.label`,
|
||||
hint: `${localizationPath}.hint`
|
||||
})
|
||||
});
|
||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhCharacter extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
|
||||
|
|
@ -112,59 +90,36 @@ export default class DhCharacter extends BaseDataActor {
|
|||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
attack: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Roll.attack'
|
||||
}),
|
||||
primaryWeapon: new fields.SchemaField({
|
||||
attack: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Roll.primaryWeaponAttack'
|
||||
})
|
||||
}),
|
||||
spellcast: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Roll.spellcast'
|
||||
}),
|
||||
action: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Roll.action'
|
||||
}),
|
||||
hopeOrFear: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Roll.hopeOrFear'
|
||||
})
|
||||
attack: bonusField('DAGGERHEART.GENERAL.Roll.attack'),
|
||||
spellcast: bonusField('DAGGERHEART.GENERAL.Roll.spellcast'),
|
||||
trait: bonusField('DAGGERHEART.GENERAL.Roll.trait'),
|
||||
action: bonusField('DAGGERHEART.GENERAL.Roll.action'),
|
||||
reaction: bonusField('DAGGERHEART.GENERAL.Roll.reaction'),
|
||||
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Roll.primaryWeaponAttack'),
|
||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Roll.secondaryWeaponAttack')
|
||||
}),
|
||||
damage: new fields.SchemaField({
|
||||
all: new fields.NumberField({
|
||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage'),
|
||||
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon'),
|
||||
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon')
|
||||
}),
|
||||
healing: bonusField('DAGGERHEART.GENERAL.Healing.healingAmount'),
|
||||
range: new fields.SchemaField({
|
||||
weapon: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Damage.allDamage'
|
||||
label: 'DAGGERHEART.GENERAL.Range.weapon'
|
||||
}),
|
||||
physical: new fields.NumberField({
|
||||
spell: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Damage.physicalDamage'
|
||||
label: 'DAGGERHEART.GENERAL.Range.spell'
|
||||
}),
|
||||
magic: new fields.NumberField({
|
||||
other: new fields.NumberField({
|
||||
integer: true,
|
||||
initial: 0,
|
||||
label: 'DAGGERHEART.GENERAL.Damage.magicalDamage'
|
||||
}),
|
||||
primaryWeapon: new fields.SchemaField({
|
||||
bonus: new fields.NumberField({
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.Damage.primaryDamageBonus'
|
||||
}),
|
||||
extraDice: new fields.NumberField({
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.Damage.primaryDamageDice'
|
||||
})
|
||||
label: 'DAGGERHEART.GENERAL.Range.other'
|
||||
})
|
||||
})
|
||||
}),
|
||||
|
|
@ -246,6 +201,11 @@ export default class DhCharacter extends BaseDataActor {
|
|||
return !this.class.value || !this.class.subclass;
|
||||
}
|
||||
|
||||
get spellcastModifier() {
|
||||
const subClasses = this.parent.items.filter(x => x.type === 'subclass') ?? [];
|
||||
return Math.max(subClasses?.map(sc => this.traits[sc.system.spellcastingTrait]?.value));
|
||||
}
|
||||
|
||||
get spellcastingModifiers() {
|
||||
return {
|
||||
main: this.class.subclass?.system?.spellcastingTrait,
|
||||
|
|
@ -388,6 +348,8 @@ export default class DhCharacter extends BaseDataActor {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
this.evasion = this.class.value?.system?.evasion ?? 0;
|
||||
|
||||
const currentLevel = this.levelData.level.current;
|
||||
const currentTier =
|
||||
currentLevel === 1
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import ActionField from '../fields/actionField.mjs';
|
||||
import { adjustDice, adjustRange } from '../../helpers/utils.mjs';
|
||||
import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs';
|
||||
import { resourceField, bonusField } from '../fields/actorField.mjs';
|
||||
|
||||
export default class DhCompanion extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion'];
|
||||
|
|
@ -23,20 +24,9 @@ export default class DhCompanion extends BaseDataActor {
|
|||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ initial: 3, integer: true }),
|
||||
isReversed: new foundry.data.fields.BooleanField({ initial: true })
|
||||
}),
|
||||
stress: resourceField(3, 'DAGGERHEART.GENERAL.stress', true),
|
||||
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
|
||||
}),
|
||||
evasion: new fields.NumberField({
|
||||
required: true,
|
||||
min: 1,
|
||||
initial: 10,
|
||||
integer: true,
|
||||
label: 'DAGGERHEART.GENERAL.evasion'
|
||||
}),
|
||||
experiences: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({}),
|
||||
|
|
@ -62,7 +52,7 @@ export default class DhCompanion extends BaseDataActor {
|
|||
amount: 1
|
||||
},
|
||||
roll: {
|
||||
type: 'weapon',
|
||||
type: 'attack',
|
||||
bonus: 0,
|
||||
trait: 'instinct'
|
||||
},
|
||||
|
|
@ -80,7 +70,13 @@ export default class DhCompanion extends BaseDataActor {
|
|||
}
|
||||
}),
|
||||
actions: new fields.ArrayField(new ActionField()),
|
||||
levelData: new fields.EmbeddedDataField(DhLevelData)
|
||||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
|
||||
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage')
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +91,7 @@ export default class DhCompanion extends BaseDataActor {
|
|||
}
|
||||
|
||||
prepareBaseData() {
|
||||
const partnerSpellcastingModifier = this.partner?.system?.spellcastingModifiers?.main;
|
||||
const partnerSpellcastingModifier = this.partner?.system?.spellcastModifier;
|
||||
const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.value;
|
||||
this.attack.roll.bonus = spellcastingModifier ?? 0; // Needs to expand on which modifier it is that should be used because of multiclassing;
|
||||
|
||||
|
|
|
|||
32
module/data/fields/actorField.mjs
Normal file
32
module/data/fields/actorField.mjs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
const attributeField = label =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true, label }),
|
||||
tierMarked: new fields.BooleanField({ initial: false })
|
||||
});
|
||||
|
||||
const resourceField = (max = 0, label, reverse = false) =>
|
||||
new fields.SchemaField({
|
||||
value: new fields.NumberField({ initial: 0, integer: true, label }),
|
||||
max: new fields.NumberField({ initial: max, integer: true }),
|
||||
isReversed: new fields.BooleanField({ initial: reverse })
|
||||
});
|
||||
|
||||
const stressDamageReductionRule = localizationPath =>
|
||||
new fields.SchemaField({
|
||||
enabled: new fields.BooleanField({ required: true, initial: false }),
|
||||
cost: new fields.NumberField({
|
||||
integer: true,
|
||||
label: `${localizationPath}.label`,
|
||||
hint: `${localizationPath}.hint`
|
||||
})
|
||||
});
|
||||
|
||||
const bonusField = label =>
|
||||
new fields.SchemaField({
|
||||
bonus: new fields.NumberField({ integer: true, initial: 0, label }),
|
||||
dice: new fields.ArrayField(new fields.StringField())
|
||||
});
|
||||
|
||||
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
|
||||
|
|
@ -50,7 +50,7 @@ export default class DHWeapon extends AttachableItem {
|
|||
},
|
||||
roll: {
|
||||
trait: 'agility',
|
||||
type: 'weapon'
|
||||
type: 'attack'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ export default class D20Roll extends DHRoll {
|
|||
}
|
||||
|
||||
constructFormula(config) {
|
||||
// this.terms = [];
|
||||
this.createBaseDice();
|
||||
this.configureModifiers();
|
||||
this.resetFormula();
|
||||
|
|
@ -91,7 +90,10 @@ export default class D20Roll extends DHRoll {
|
|||
|
||||
configureModifiers() {
|
||||
this.applyAdvantage();
|
||||
this.applyBaseBonus();
|
||||
|
||||
this.baseTerms = foundry.utils.deepClone(this.terms);
|
||||
|
||||
this.options.roll.modifiers = this.applyBaseBonus();
|
||||
|
||||
this.options.experiences?.forEach(m => {
|
||||
if (this.options.data.experiences?.[m])
|
||||
|
|
@ -101,12 +103,7 @@ export default class D20Roll extends DHRoll {
|
|||
});
|
||||
});
|
||||
|
||||
this.options.roll.modifiers?.forEach(m => {
|
||||
this.terms.push(...this.formatModifier(m.value));
|
||||
});
|
||||
|
||||
this.baseTerms = foundry.utils.deepClone(this.terms);
|
||||
|
||||
this.addModifiers();
|
||||
if (this.options.extraFormula) {
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
|
|
@ -125,13 +122,20 @@ export default class D20Roll extends DHRoll {
|
|||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
this.options.roll.modifiers = [];
|
||||
if (!this.options.roll.bonus) return;
|
||||
this.options.roll.modifiers.push({
|
||||
label: 'Bonus to Hit',
|
||||
value: this.options.roll.bonus
|
||||
// value: Roll.replaceFormulaData('@attackBonus', this.data)
|
||||
});
|
||||
const modifiers = [];
|
||||
|
||||
if (this.options.roll.bonus)
|
||||
modifiers.push({
|
||||
label: 'Bonus to Hit',
|
||||
value: this.options.roll.bonus
|
||||
});
|
||||
|
||||
modifiers.push(...this.getBonus(`roll.${this.options.type}`, `${this.options.type.capitalize()} Bonus`));
|
||||
modifiers.push(
|
||||
...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`)
|
||||
);
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,26 @@ export default class DamageRoll extends DHRoll {
|
|||
}
|
||||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
const modifiers = [],
|
||||
type = this.options.messageType ?? 'damage';
|
||||
|
||||
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
||||
this.options.damageTypes?.forEach(t => {
|
||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||
});
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
constructFormula(config) {
|
||||
super.constructFormula(config);
|
||||
|
||||
if (config.isCritical) {
|
||||
const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }),
|
||||
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export default class DHRoll extends Roll {
|
|||
baseTerms = [];
|
||||
constructor(formula, data, options) {
|
||||
super(formula, data, options);
|
||||
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
|
||||
}
|
||||
|
||||
static messageType = 'adversaryRoll';
|
||||
|
|
@ -99,11 +100,44 @@ export default class DHRoll extends Roll {
|
|||
}
|
||||
|
||||
formatModifier(modifier) {
|
||||
const numTerm = modifier < 0 ? '-' : '+';
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||
];
|
||||
if (Array.isArray(modifier)) {
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...this.constructor.parse(modifier.join(' + '), this.options.data)
|
||||
];
|
||||
} else {
|
||||
const numTerm = modifier < 0 ? '-' : '+';
|
||||
return [
|
||||
new foundry.dice.terms.OperatorTerm({ operator: numTerm }),
|
||||
new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) })
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
return [];
|
||||
}
|
||||
|
||||
addModifiers() {
|
||||
this.options.roll.modifiers?.forEach(m => {
|
||||
this.terms.push(...this.formatModifier(m.value));
|
||||
});
|
||||
}
|
||||
|
||||
getBonus(path, label) {
|
||||
const bonus = foundry.utils.getProperty(this.data.bonuses, path),
|
||||
modifiers = [];
|
||||
if (bonus?.bonus)
|
||||
modifiers.push({
|
||||
label: label,
|
||||
value: bonus?.bonus
|
||||
});
|
||||
if (bonus?.dice?.length)
|
||||
modifiers.push({
|
||||
label: label,
|
||||
value: bonus?.dice
|
||||
});
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
getFaces(faces) {
|
||||
|
|
@ -113,6 +147,9 @@ export default class DHRoll extends Roll {
|
|||
constructFormula(config) {
|
||||
this.terms = Roll.parse(this.options.roll.formula, config.data);
|
||||
|
||||
this.options.roll.modifiers = this.applyBaseBonus();
|
||||
this.addModifiers();
|
||||
|
||||
if (this.options.extraFormula) {
|
||||
this.terms.push(
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
|
|
|
|||
|
|
@ -119,12 +119,21 @@ export default class DualityRoll extends D20Roll {
|
|||
}
|
||||
|
||||
applyBaseBonus() {
|
||||
this.options.roll.modifiers = [];
|
||||
if (!this.options.roll.trait) return;
|
||||
this.options.roll.modifiers.push({
|
||||
label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
|
||||
value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.value`, this.data)
|
||||
const modifiers = super.applyBaseBonus();
|
||||
|
||||
if (this.options.roll.trait && this.data.traits[this.options.roll.trait])
|
||||
modifiers.unshift({
|
||||
label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
|
||||
value: this.data.traits[this.options.roll.trait].value
|
||||
});
|
||||
|
||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||
weapons.forEach(w => {
|
||||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`roll.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
static postEvaluate(roll, config = {}) {
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ export default class DhpActor extends Actor {
|
|||
getRollData() {
|
||||
const rollData = super.getRollData();
|
||||
rollData.prof = this.system.proficiency ?? 1;
|
||||
rollData.cast = this.system.spellcast ?? 1;
|
||||
rollData.cast = this.system.spellcastModifier ?? 1;
|
||||
return rollData;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,9 @@ export default class RegisterHandlebarsHelpers {
|
|||
}
|
||||
|
||||
static damageSymbols(damageParts) {
|
||||
const symbols = new Set();
|
||||
damageParts.forEach(part => symbols.add(...CONFIG.DH.GENERAL.damageTypes[part.type].icon));
|
||||
const symbols = [...new Set(damageParts.reduce((a, c) => a.concat([...c.type]), []))].map(
|
||||
p => CONFIG.DH.GENERAL.damageTypes[p].icon
|
||||
);
|
||||
return new Handlebars.SafeString(Array.from(symbols).map(symbol => `<i class="fa-solid ${symbol}"></i>`));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ export const itemAbleRollParse = (value, actor, item) => {
|
|||
const isItemTarget = value.toLowerCase().startsWith('item.');
|
||||
const slicedValue = isItemTarget ? value.slice(5) : value;
|
||||
try {
|
||||
return Roll.safeEval(Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor));
|
||||
return Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor);
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,9 @@
|
|||
justify-content: center;
|
||||
|
||||
.status-number {
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.status-value {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -174,8 +174,115 @@
|
|||
gap: 5px;
|
||||
justify-content: center;
|
||||
|
||||
.status-bar.armor-slots {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 95px;
|
||||
height: 30px;
|
||||
|
||||
.status-label {
|
||||
padding: 2px 10px;
|
||||
position: relative;
|
||||
top: 30px;
|
||||
height: 22px;
|
||||
width: 95px;
|
||||
border-radius: 3px;
|
||||
background: light-dark(@dark-blue, @golden);
|
||||
|
||||
h4 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
color: light-dark(@beige, @dark-blue);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.status-value {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
padding: 0 6px;
|
||||
font-size: 1.2rem;
|
||||
align-items: center;
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
color: light-dark(@dark-blue, @beige);
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
|
||||
input[type='number'] {
|
||||
background: transparent;
|
||||
font-size: 1.2rem;
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
color: light-dark(@dark-blue, @beige);
|
||||
|
||||
&.bar-input {
|
||||
padding: 0;
|
||||
color: light-dark(@dark-blue, @beige);
|
||||
backdrop-filter: none;
|
||||
background: transparent;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: @semi-transparent-dark-blue;
|
||||
backdrop-filter: blur(9.5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
appearance: none;
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
z-index: 1;
|
||||
background: light-dark(transparent, @dark-blue);
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
|
||||
&::-webkit-progress-bar {
|
||||
border: none;
|
||||
background: light-dark(transparent, @dark-blue);
|
||||
}
|
||||
&::-webkit-progress-value {
|
||||
background: @gradient-stress;
|
||||
}
|
||||
&.stress-color::-webkit-progress-value {
|
||||
background: @gradient-stress;
|
||||
}
|
||||
&::-moz-progress-bar {
|
||||
background: @gradient-stress;
|
||||
}
|
||||
&.stress-color::-moz-progress-bar {
|
||||
background: @gradient-stress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-number {
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
&.armor-slots {
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
position: relative;
|
||||
|
|
@ -192,9 +299,33 @@
|
|||
background: light-dark(transparent, @dark-blue);
|
||||
z-index: 2;
|
||||
|
||||
&.armor-slots {
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
input[type='number'] {
|
||||
background: transparent;
|
||||
font-size: 1.2rem;
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
color: light-dark(@dark-blue, @beige);
|
||||
|
||||
&.bar-input {
|
||||
padding: 0;
|
||||
color: light-dark(@dark-blue, @beige);
|
||||
backdrop-filter: none;
|
||||
background: transparent;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: @semi-transparent-dark-blue;
|
||||
backdrop-filter: blur(9.5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@
|
|||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-items: center;
|
||||
|
||||
.status-label {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@
|
|||
}
|
||||
|
||||
.status-number {
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.status-value {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -44,12 +44,14 @@
|
|||
display: flex;
|
||||
gap: 2px;
|
||||
margin-bottom: 4px;
|
||||
flex-wrap: wrap;
|
||||
.duality-modifier {
|
||||
padding: 2px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid;
|
||||
background: var(--color-dark-6);
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.dice-flavor {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
<ul class="actions-list">
|
||||
{{#each actions}}
|
||||
<li class="action-item">
|
||||
<input type="radio" name="actionId" value="{{_id}}" {{#if (eq @index 0)}}checked{{/if}}>
|
||||
<span class="label">{{ name }}</span>
|
||||
<input type="radio" id="action-{{_id}}" name="actionId" value="{{_id}}" {{#if (eq @index 0)}}checked{{/if}}>
|
||||
<label class="label" for="action-{{_id}}">{{ name }}</label>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -48,18 +48,32 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-number">
|
||||
<div class='status-value armor-slots'>
|
||||
{{#if document.system.armor.system.marks}}
|
||||
<p>{{document.system.armor.system.marks.value}}/{{document.system.armorScore}}</p>
|
||||
{{else}}
|
||||
{{#if document.system.armor.system.marks}}
|
||||
<div class="status-bar armor-slots">
|
||||
<div class='status-value'>
|
||||
<p><input class="bar-input armor-marks-input" value="{{document.system.armor.system.marks.value}}" type="number"></p>
|
||||
<p>/</p>
|
||||
<p class="bar-label">{{document.system.armorScore}}</p>
|
||||
</div>
|
||||
<progress
|
||||
class='progress-bar stress-color'
|
||||
value='{{document.system.armor.system.marks.value}}'
|
||||
max='{{document.system.armorScore}}'
|
||||
></progress>
|
||||
<div class="status-label">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.armorSlots"}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="status-number armor-slots">
|
||||
<div class='status-value'>
|
||||
<p>-</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="status-label">
|
||||
<h4>{{localize "DAGGERHEART.GENERAL.armorSlots"}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-label">
|
||||
<h4>Armor Slots</h4>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="status-number">
|
||||
<div class='status-value'>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue