[Feature] Armor/Weapon Compendiums (#459)

* Fixed localization

* .

* Added all Secondary Weapons

* Added all armors

* Added Tier1 weapons

* Tier2 Physical

* Tier 2 magical

* Tier 3 Physical

* Tier 3 Magical

* Tier 4 Physical

* Tier 4 Magical

* Added wheelchairs
This commit is contained in:
WBHarry 2025-07-30 15:33:00 +02:00 committed by GitHub
parent 8e516df7cb
commit 2c4d3bd4a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
277 changed files with 34221 additions and 431 deletions

View file

@ -133,7 +133,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
const resource = this.action.parent.resource;
if (resource) {
options[this.action.parent.parent.id] = {
label: "DAGGERHEART.GENERAL.itemResource",
label: 'DAGGERHEART.GENERAL.itemResource',
group: 'Global'
};
}
@ -143,10 +143,10 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
getRollTypeOptions() {
const types = foundry.utils.deepClone(CONFIG.DH.GENERAL.rollTypes);
if(!this.action.actor) return types;
if (!this.action.actor) return types;
Object.values(types).forEach(t => {
if(this.action.actor.type !== 'character' && t.playerOnly) delete types[t.id];
})
if (this.action.actor.type !== 'character' && t.playerOnly) delete types[t.id];
});
return types;
}

View file

@ -1,4 +1,4 @@
import { emitAsGM, GMUpdateEvent } from "../../systemRegistration/socket.mjs";
import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs';
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor(options) {
@ -100,22 +100,24 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.hasSave) return;
action.rollSave(token.actor, event, message).then(result => emitAsGM(
GMUpdateEvent.UpdateSaveMessage,
action.updateSaveMessage.bind(action, result, message, token.id),
{
action: action.uuid,
message: message._id,
token: token.id,
result
}
));
action.rollSave(token.actor, event, message).then(result =>
emitAsGM(
GMUpdateEvent.UpdateSaveMessage,
action.updateSaveMessage.bind(action, result, message, token.id),
{
action: action.uuid,
message: message._id,
token: token.id,
result
}
)
);
}
}
async onRollAllSave(event, message) {
event.stopPropagation();
if(!game.user.isGM) return;
if (!game.user.isGM) return;
const targets = event.target.parentElement.querySelectorAll(
'.target-section > [data-token] .target-save-container'
);
@ -124,16 +126,17 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
targets.forEach(async el => {
const tokenId = el.closest('[data-token]')?.dataset.token,
token = game.canvas.tokens.get(tokenId);
if(!token.actor) return;
if(game.user === token.actor.owner)
el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
if (!token.actor) return;
if (game.user === token.actor.owner) el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
else {
token.actor.owner.query('reactionRoll', {
actionId: action.uuid,
actorId: token.actor.uuid,
event,
message
}).then(result => action.updateSaveMessage(result, message, token.id));
token.actor.owner
.query('reactionRoll', {
actionId: action.uuid,
actorId: token.actor.uuid,
event,
message
})
.then(result => action.updateSaveMessage(result, message, token.id));
}
});
}
@ -172,7 +175,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
return {
isHit,
targets: isHit
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
? message.system.targets
.filter(t => t.hit === true)
.map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
: Array.from(game.user.targets)
};
}
@ -234,10 +239,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
});
}
if(message.system.hasHealing)
target.actor.takeHealing(damages);
else
target.actor.takeDamage(damages);
if (message.system.hasHealing) target.actor.takeHealing(damages);
else target.actor.takeDamage(damages);
}
}

File diff suppressed because it is too large Load diff

View file

@ -203,7 +203,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
async consume(config) {
const usefulResources = foundry.utils.deepClone(this.actor.system.resources);
for (var cost of config.costs) {
if (cost.keyIsID) {
usefulResources[cost.key] = {
@ -224,10 +224,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
keyIsID: resource.keyIsID
};
});
console.log(resources)
console.log(resources);
await this.actor.modifyResource(resources);
if (config.uses?.enabled)
this.update({ 'uses.value': this.uses.value + 1 });
if (config.uses?.enabled) this.update({ 'uses.value': this.uses.value + 1 });
}
/* */

View file

@ -26,19 +26,20 @@ export default class CostField extends fields.ArrayField {
}
static calcCosts(costs) {
console.log(costs, CostField.getResources.call(this, costs))
console.log(costs, CostField.getResources.call(this, costs));
const resources = CostField.getResources.call(this, costs);
return costs.map(c => {
c.scale = c.scale ?? 1;
c.step = c.step ?? 1;
c.total = c.value + ((c.scale - 1) * c.step);
c.total = c.value + (c.scale - 1) * c.step;
c.enabled = c.hasOwnProperty('enabled') ? c.enabled : true;
c.max = c.key === 'fear'
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
: resources[c.key].isReversed
? resources[c.key].max
: resources[c.key].value
if(c.scalable) c.maxStep = Math.floor(c.max / c.step);
c.max =
c.key === 'fear'
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
: resources[c.key].isReversed
? resources[c.key].max
: resources[c.key].value;
if (c.scalable) c.maxStep = Math.floor(c.max / c.step);
return c;
});
}

View file

@ -69,34 +69,34 @@ export class DHActionRollData extends foundry.abstract.DataModel {
getModifier() {
const modifiers = [];
if(!this.parent?.actor) return modifiers;
if (!this.parent?.actor) return modifiers;
switch (this.parent.actor.type) {
case 'character':
const trait = this.useDefault || !this.trait ? (this.parent.item.system.attack.roll.trait ?? 'agility') : this.trait;
if(this.type === CONFIG.DH.GENERAL.rollTypes.attack.id || this.type === CONFIG.DH.GENERAL.rollTypes.trait.id)
modifiers.push(
{
label: `DAGGERHEART.CONFIG.Traits.${trait}.name`,
value: this.parent.actor.system.traits[trait].value
}
)
else if(this.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id)
modifiers.push(
{
label: `DAGGERHEART.CONFIG.RollTypes.spellcast.name`,
value: this.parent.actor.system.spellcastModifier
}
)
const trait =
this.useDefault || !this.trait
? (this.parent.item.system.attack.roll.trait ?? 'agility')
: this.trait;
if (
this.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
this.type === CONFIG.DH.GENERAL.rollTypes.trait.id
)
modifiers.push({
label: `DAGGERHEART.CONFIG.Traits.${trait}.name`,
value: this.parent.actor.system.traits[trait].value
});
else if (this.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id)
modifiers.push({
label: `DAGGERHEART.CONFIG.RollTypes.spellcast.name`,
value: this.parent.actor.system.spellcastModifier
});
break;
case 'companion':
case 'adversary':
if(this.type === CONFIG.DH.GENERAL.rollTypes.attack.id)
modifiers.push(
{
label: 'Bonus to Hit',
value: this.bonus ?? this.parent.actor.system.attack.roll.bonus
}
)
if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id)
modifiers.push({
label: 'Bonus to Hit',
value: this.bonus ?? this.parent.actor.system.attack.roll.bonus
});
break;
default:
break;

View file

@ -1,4 +1,4 @@
import FormulaField from "../formulaField.mjs";
import FormulaField from '../formulaField.mjs';
const fields = foundry.data.fields;
@ -36,7 +36,7 @@ export default class UsesField extends fields.SchemaField {
static hasUses(uses) {
if (!uses) return true;
let max = uses.max ?? 0;
if(isNaN(max)) {
if (isNaN(max)) {
const roll = new Roll(Roll.replaceFormulaData(uses.max, this.getRollData())).evaluateSync();
max = roll.total;
}

View file

@ -71,13 +71,14 @@ export default class DHArmor extends AttachableItem {
for (var feature of added) {
const featureData = armorFeatures[feature.value];
if (featureData.effects?.length > 0) {
const embeddedItems = await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize(featureData.label),
description: game.i18n.localize(featureData.description),
changes: featureData.effects.flatMap(x => x.changes)
}
]);
const embeddedItems = await this.parent.createEmbeddedDocuments(
'ActiveEffect',
featureData.effects.map(effect => ({
...effect,
name: game.i18n.localize(effect.name),
description: game.i18n.localize(effect.description)
}))
);
feature.effectIds = embeddedItems.map(x => x.id);
}
@ -93,6 +94,7 @@ export default class DHArmor extends AttachableItem {
description: game.i18n.localize(effect.description)
}))
);
feature.effectIds = [...(feature.effectIds ?? []), ...embeddedEffects.map(x => x.id)];
const cls = game.system.api.models.actions.actionsTypes[action.type];
const actionId = foundry.utils.randomID();

View file

@ -118,13 +118,14 @@ export default class DHWeapon extends AttachableItem {
for (let weaponFeature of added) {
const featureData = CONFIG.DH.ITEM.weaponFeatures[weaponFeature.value];
if (featureData.effects?.length > 0) {
const embeddedItems = await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize(featureData.label),
description: game.i18n.localize(featureData.description),
changes: featureData.effects.flatMap(x => x.changes)
}
]);
const embeddedItems = await this.parent.createEmbeddedDocuments(
'ActiveEffect',
featureData.effects.map(effect => ({
...effect,
name: game.i18n.localize(effect.name),
description: game.i18n.localize(effect.description)
}))
);
weaponFeature.effectIds = embeddedItems.map(x => x.id);
}
@ -140,6 +141,10 @@ export default class DHWeapon extends AttachableItem {
description: game.i18n.localize(effect.description)
}))
);
weaponFeature.effectIds = [
...(weaponFeature.effectIds ?? []),
...embeddedEffects.map(x => x.id)
];
const cls = game.system.api.models.actions.actionsTypes[action.type];
const actionId = foundry.utils.randomID();

View file

@ -85,9 +85,9 @@ export default class DamageRoll extends DHRoll {
const modifiers = [],
type = this.options.messageType ?? (this.options.isHealing ? 'healing' : 'damage'),
options = part ?? this.options;
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
if(!this.options.isHealing) {
if (!this.options.isHealing) {
options.damageTypes?.forEach(t => {
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
});

View file

@ -194,11 +194,12 @@ export const registerRollDiceHooks = () => {
if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1 });
if (config.rerolledRoll) {
if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1) updates.push({ key: 'hope', value: -1 });
if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1)
updates.push({ key: 'hope', value: -1 });
if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: 1 });
if (config.rerolledRoll.result.duality === -1) updates.push({ key: 'fear', value: -1 });
}
if (updates.length) {
const target = actor.system.partner ?? actor;
if (!['dead', 'unconcious'].some(x => actor.statuses.has(x))) {

View file

@ -481,7 +481,9 @@ export default class DhpActor extends Actor {
this.system.armor &&
this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)
) {
const armorSlotResult = await this.owner.query('armorSlot', {
const armorSlotResult = await this.owner.query(
'armorSlot',
{
actorId: this.uuid,
damage: hpDamage.value,
type: [...hpDamage.damageTypes]
@ -569,7 +571,7 @@ export default class DhpActor extends Actor {
armor: { target: this.system.armor, resources: {} },
items: {}
};
resources.forEach(r => {
if (r.keyIsID) {
updates.items[r.key] = {

View file

@ -52,7 +52,8 @@ export default class DHToken extends TokenDocument {
for (const [name, field] of Object.entries(schema.fields)) {
const p = _path.concat([name]);
if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p);
if (field instanceof foundry.data.fields.BooleanField && field.options.isAttributeChoice) attributes.value.push(p);
if (field instanceof foundry.data.fields.BooleanField && field.options.isAttributeChoice)
attributes.value.push(p);
if (field instanceof foundry.data.fields.StringField) attributes.value.push(p);
if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p);
const isSchema = field instanceof foundry.data.fields.SchemaField;

View file

@ -41,7 +41,7 @@ export default class RegisterHandlebarsHelpers {
}
static formulaValue(formula, item) {
if(isNaN(formula)) {
if (isNaN(formula)) {
const data = item.getRollData.bind(item)(),
roll = new Roll(Roll.replaceFormulaData(formula, data)).evaluateSync();
formula = roll.total;

View file

@ -307,7 +307,7 @@ export function updateLinkedItemApps(options, sheet) {
export const itemAbleRollParse = (value, actor, item) => {
if (!value) return value;
const isItemTarget = value.toLowerCase().startsWith('item.');
const isItemTarget = value.toLowerCase().replace('item.@', '@');
const slicedValue = isItemTarget ? value.slice(5) : value;
try {
return Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor);

View file

@ -60,7 +60,7 @@ export const registerSocketHooks = () => {
case GMUpdateEvent.UpdateSaveMessage:
const action = await fromUuid(data.update.action),
message = game.messages.get(data.update.message);
if(!action || !message) return;
if (!action || !message) return;
action.updateSaveMessage(data.update.result, message, data.update.token);
break;
}
@ -79,7 +79,7 @@ export const registerSocketHooks = () => {
export const registerUserQueries = () => {
CONFIG.queries.armorSlot = DamageReductionDialog.armorSlotQuery;
CONFIG.queries.reactionRoll = game.system.api.models.actions.actionsTypes.base.rollSaveQuery;
}
};
export const emitAsGM = async (eventName, callback, update, uuid = null) => {
if (!game.user.isGM) {