mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 19:51:08 +01:00
Merge branch 'development' of https://github.com/Foundryborne/daggerheart into feature/party-sheet
This commit is contained in:
commit
1a0c6f46bc
648 changed files with 7471 additions and 3950 deletions
|
|
@ -167,13 +167,11 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
|||
|
||||
if (subclass) {
|
||||
const featureState = subclass.system.featureState;
|
||||
const featureType = subclass
|
||||
? (subclass.system.features.find(x => x.item?.uuid === this.parent.uuid)?.type ?? null)
|
||||
: null;
|
||||
|
||||
if (
|
||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && featureState < 2) ||
|
||||
(featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3)
|
||||
(this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization &&
|
||||
featureState < 2) ||
|
||||
(this.parent.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && featureState < 3)
|
||||
) {
|
||||
this.transfer = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default class DhpActor extends Actor {
|
|||
get owner() {
|
||||
const user =
|
||||
this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active);
|
||||
if (!user) return game.user.isGM ? game.user : null;
|
||||
if (!user) return game.users.activeGM;
|
||||
return user;
|
||||
}
|
||||
|
||||
|
|
@ -167,10 +167,10 @@ export default class DhpActor extends Actor {
|
|||
if (multiclass) {
|
||||
const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid);
|
||||
const multiclassFeatures = this.items.filter(
|
||||
x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass'
|
||||
x => x.system.originItemType === 'class' && x.system.multiclassOrigin
|
||||
);
|
||||
const subclassFeatures = this.items.filter(
|
||||
x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass'
|
||||
x => x.system.originItemType === 'subclass' && x.system.multiclassOrigin
|
||||
);
|
||||
|
||||
this.deleteEmbeddedDocuments(
|
||||
|
|
@ -659,13 +659,22 @@ export default class DhpActor extends Actor {
|
|||
};
|
||||
|
||||
resources.forEach(r => {
|
||||
if (r.keyIsID) {
|
||||
updates.items[r.key] = {
|
||||
target: r.target,
|
||||
resources: {
|
||||
'system.resource.value': r.target.system.resource.value + r.value
|
||||
}
|
||||
};
|
||||
if (r.itemId) {
|
||||
const { path, value } = game.system.api.fields.ActionFields.CostField.getItemIdCostUpdate(r);
|
||||
|
||||
if (
|
||||
r.key === 'quantity' &&
|
||||
r.target.type === 'consumable' &&
|
||||
value === 0 &&
|
||||
r.target.system.destroyOnEmpty
|
||||
) {
|
||||
r.target.delete();
|
||||
} else {
|
||||
updates.items[r.key] = {
|
||||
target: r.target,
|
||||
resources: { [path]: value }
|
||||
};
|
||||
}
|
||||
} else {
|
||||
switch (r.key) {
|
||||
case 'fear':
|
||||
|
|
@ -773,6 +782,28 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
async importFromJSON(json) {
|
||||
if (!this.type === 'character') return await super.importFromJSON(json);
|
||||
|
||||
if (!CONST.WORLD_DOCUMENT_TYPES.includes(this.documentName)) {
|
||||
throw new Error('Only world Documents may be imported');
|
||||
}
|
||||
|
||||
const parsedJSON = JSON.parse(json);
|
||||
if (foundry.utils.isNewerVersion('1.1.0', parsedJSON._stats.systemVersion)) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportTitle')
|
||||
},
|
||||
content: game.i18n.localize('DAGGERHEART.ACTORS.Character.InvalidOldCharacterImportText')
|
||||
});
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
return await super.importFromJSON(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an array of localized tag.
|
||||
* @returns {string[]} An array of localized tag strings.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
|
||||
|
||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||
targetHook = null;
|
||||
|
||||
|
|
@ -103,19 +105,29 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
|
||||
}
|
||||
|
||||
if (!game.user.isGM) {
|
||||
if (!this.isAuthor && !this.speakerActor?.isOwner) {
|
||||
const applyButtons = html.querySelector('.apply-buttons');
|
||||
applyButtons?.remove();
|
||||
if (!this.isAuthor && !this.speakerActor?.isOwner) {
|
||||
const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button');
|
||||
buttons.forEach(b => b.remove());
|
||||
}
|
||||
const buttons = html.querySelectorAll('.ability-card-footer > .ability-use-button');
|
||||
buttons.forEach(b => b.remove());
|
||||
}
|
||||
}
|
||||
|
||||
addChatListeners(html) {
|
||||
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||
element.addEventListener('click', this.onRollDamage.bind(this))
|
||||
);
|
||||
|
||||
html.querySelectorAll('.damage-button').forEach(element =>
|
||||
element.addEventListener('click', this.onDamage.bind(this))
|
||||
element.addEventListener('click', this.onApplyDamage.bind(this))
|
||||
);
|
||||
|
||||
html.querySelectorAll('.target-save').forEach(element =>
|
||||
element.addEventListener('click', this.onRollSave.bind(this))
|
||||
);
|
||||
|
||||
html.querySelectorAll('.roll-all-save-button').forEach(element =>
|
||||
element.addEventListener('click', this.onRollAllSave.bind(this))
|
||||
);
|
||||
|
||||
html.querySelectorAll('.duality-action-effect').forEach(element =>
|
||||
|
|
@ -133,17 +145,21 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
});
|
||||
}
|
||||
|
||||
getTargetList() {
|
||||
const targets = this.system.hitTargets ?? [];
|
||||
return targets.map(target => game.canvas.tokens.documentCollection.find(t => t.actor?.uuid === target.actorId));
|
||||
async onRollDamage(event) {
|
||||
event.stopPropagation();
|
||||
const config = foundry.utils.deepClone(this.system);
|
||||
config.event = event;
|
||||
this.system.action?.workflow.get('damage')?.execute(config, this._id, true);
|
||||
}
|
||||
|
||||
async onDamage(event) {
|
||||
async onApplyDamage(event) {
|
||||
event.stopPropagation();
|
||||
const targets = this.getTargetList();
|
||||
const targets = this.filterPermTargets(this.system.hitTargets),
|
||||
config = foundry.utils.deepClone(this.system);
|
||||
config.event = event;
|
||||
|
||||
if (this.system.onSave) {
|
||||
const pendingingSaves = this.system.hitTargets.filter(t => t.saved.success === null);
|
||||
const pendingingSaves = targets.filter(t => t.saved.success === null);
|
||||
if (pendingingSaves.length) {
|
||||
const confirm = await foundry.applications.api.DialogV2.confirm({
|
||||
window: { title: 'Pending Reaction Rolls found' },
|
||||
|
|
@ -154,62 +170,66 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
}
|
||||
|
||||
if (targets.length === 0)
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||
|
||||
for (let target of targets) {
|
||||
let damages = foundry.utils.deepClone(this.system.damage);
|
||||
if (
|
||||
!this.system.hasHealing &&
|
||||
this.system.onSave &&
|
||||
this.system.hitTargets.find(t => t.id === target.id)?.saved?.success === true
|
||||
) {
|
||||
const mod = CONFIG.DH.ACTIONS.damageOnSave[this.system.onSave]?.mod ?? 1;
|
||||
Object.entries(damages).forEach(([k, v]) => {
|
||||
v.total = 0;
|
||||
v.parts.forEach(part => {
|
||||
part.total = Math.ceil(part.total * mod);
|
||||
v.total += part.total;
|
||||
});
|
||||
});
|
||||
}
|
||||
this.consumeOnSuccess();
|
||||
this.system.action?.workflow.get('applyDamage')?.execute(config, targets, true);
|
||||
}
|
||||
|
||||
this.consumeOnSuccess();
|
||||
if (this.system.hasHealing) target.actor.takeHealing(damages);
|
||||
else target.actor.takeDamage(damages, this.system.isDirect);
|
||||
async onRollSave(event) {
|
||||
event.stopPropagation();
|
||||
const tokenId = event.target.closest('[data-token]')?.dataset.token,
|
||||
token = game.canvas.tokens.get(tokenId);
|
||||
if (!token?.actor || !token.isOwner) return true;
|
||||
if (this.system.source.item && this.system.source.action) {
|
||||
const action = this.system.action;
|
||||
if (!action || !action?.hasSave) return;
|
||||
game.system.api.fields.ActionFields.SaveField.rollSave.call(action, token.actor, event).then(result =>
|
||||
emitAsGM(
|
||||
GMUpdateEvent.UpdateSaveMessage,
|
||||
game.system.api.fields.ActionFields.SaveField.updateSaveMessage.bind(
|
||||
action,
|
||||
result,
|
||||
this,
|
||||
token.id
|
||||
),
|
||||
{
|
||||
action: action.uuid,
|
||||
message: this._id,
|
||||
token: token.id,
|
||||
result
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getAction(actor, itemId, actionId) {
|
||||
const item = actor.items.get(itemId),
|
||||
action =
|
||||
actor.system.attack?._id === actionId
|
||||
? actor.system.attack
|
||||
: item.system.attack?._id === actionId
|
||||
? item.system.attack
|
||||
: item?.system?.actions?.get(actionId);
|
||||
return action;
|
||||
async onRollAllSave(event) {
|
||||
event.stopPropagation();
|
||||
if (!game.user.isGM) return;
|
||||
const targets = this.system.hitTargets,
|
||||
config = foundry.utils.deepClone(this.system);
|
||||
config.event = event;
|
||||
this.system.action?.workflow.get('save')?.execute(config, targets, true);
|
||||
}
|
||||
|
||||
async onApplyEffect(event) {
|
||||
event.stopPropagation();
|
||||
const actor = await foundry.utils.fromUuid(this.system.source.actor);
|
||||
if (!actor || !game.user.isGM) return true;
|
||||
if (this.system.source.item && this.system.source.action) {
|
||||
const action = this.getAction(actor, this.system.source.item, this.system.source.action);
|
||||
if (!action || !action?.applyEffects) return;
|
||||
const targets = this.getTargetList();
|
||||
if (targets.length === 0)
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
||||
this.consumeOnSuccess();
|
||||
await action.applyEffects(event, this, targets);
|
||||
}
|
||||
const targets = this.filterPermTargets(this.system.hitTargets),
|
||||
config = foundry.utils.deepClone(this.system);
|
||||
config.event = event;
|
||||
if (targets.length === 0)
|
||||
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
|
||||
this.consumeOnSuccess();
|
||||
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
|
||||
}
|
||||
|
||||
filterPermTargets(targets) {
|
||||
return targets.filter(t => fromUuidSync(t.actorId)?.canUserModify(game.user, 'update'));
|
||||
}
|
||||
|
||||
consumeOnSuccess() {
|
||||
if (!this.system.successConsumed && !this.targetSelection) {
|
||||
const action = this.system.action;
|
||||
if (action) action.consume(this.system, true);
|
||||
}
|
||||
if (!this.system.successConsumed && !this.targetSelection) this.system.action?.consume(this.system, true);
|
||||
}
|
||||
|
||||
hoverTarget(event) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class DHItem extends foundry.documents.Item {
|
|||
|
||||
/** @inheritDoc */
|
||||
static migrateData(source) {
|
||||
if(source.system?.attack && !source.system.attack.type) source.system.attack.type = "attack";
|
||||
if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
|
||||
return super.migrateData(source);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,10 @@ export default class DhTemplateManager {
|
|||
* @param {wheel Event} event
|
||||
*/
|
||||
#onMouseWheel(event) {
|
||||
if (!event.shiftKey) return;
|
||||
if (!this.#activePreview) {
|
||||
return;
|
||||
}
|
||||
if (!event.shiftKey && !event.ctrlKey) return;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const { moveTime, object } = this.#activePreview;
|
||||
|
|
@ -66,8 +69,10 @@ export default class DhTemplateManager {
|
|||
if (now - (moveTime || 0) <= 16) return;
|
||||
this.#activePreview.moveTime = now;
|
||||
|
||||
const multiplier = event.shiftKey ? 0.2 : 0.1;
|
||||
|
||||
object.document.updateSource({
|
||||
direction: object.document.direction + event.deltaY * 0.2
|
||||
direction: object.document.direction + event.deltaY * multiplier
|
||||
});
|
||||
object.renderFlags.set({ refresh: true });
|
||||
}
|
||||
|
|
@ -77,12 +82,13 @@ export default class DhTemplateManager {
|
|||
* @param {contextmenu Event} event
|
||||
*/
|
||||
#cancelTemplate(event) {
|
||||
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
|
||||
const { mousemove, mousedown, contextmenu, wheel } = this.#activePreview.events;
|
||||
canvas.templates._onDragLeftCancel(event);
|
||||
|
||||
canvas.stage.off('mousemove', mousemove);
|
||||
canvas.stage.off('mousedown', mousedown);
|
||||
canvas.app.view.removeEventListener('contextmenu', contextmenu);
|
||||
canvas.app.view.removeEventListener('wheel', wheel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,9 +97,9 @@ export default class DhTemplateManager {
|
|||
*/
|
||||
#confirmTemplate(event) {
|
||||
event.stopPropagation();
|
||||
this.#cancelTemplate(event);
|
||||
|
||||
canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [this.#activePreview.document.toObject()]);
|
||||
|
||||
this.#cancelTemplate(event);
|
||||
this.#activePreview = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue