mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
[Feature] 1766 - Group Attack (#1770)
* Implemented group attack logic * Updated all minions in the SRD to use the group attack functionality * . * Renamed groupAttack.nr to groupAttack.numAttackers * Moved the flag vs global setting logic to documents/scene * .
This commit is contained in:
parent
b505e15eb2
commit
ae480157d1
32 changed files with 1286 additions and 220 deletions
|
|
@ -22,6 +22,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
},
|
||||
actions: {
|
||||
toggleSelectedEffect: this.toggleSelectedEffect,
|
||||
updateGroupAttack: this.updateGroupAttack,
|
||||
toggleCritical: this.toggleCritical,
|
||||
submitRoll: this.submitRoll
|
||||
},
|
||||
|
|
@ -64,15 +65,40 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
context.hasSelectedEffects = Boolean(Object.keys(this.selectedEffects).length);
|
||||
context.selectedEffects = this.selectedEffects;
|
||||
|
||||
context.damageOptions = this.config.damageOptions;
|
||||
context.rangeOptions = CONFIG.DH.GENERAL.groupAttackRange;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static updateRollConfiguration(_event, _, formData) {
|
||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||
foundry.utils.mergeObject(this.config.roll, rest.roll);
|
||||
foundry.utils.mergeObject(this.config.modifiers, rest.modifiers);
|
||||
this.config.selectedMessageMode = rest.selectedMessageMode;
|
||||
const data = foundry.utils.expandObject(formData.object);
|
||||
foundry.utils.mergeObject(this.config.roll, data.roll);
|
||||
foundry.utils.mergeObject(this.config.modifiers, data.modifiers);
|
||||
this.config.selectedMessageMode = data.selectedMessageMode;
|
||||
|
||||
if (data.damageOptions) {
|
||||
const numAttackers = data.damageOptions.groupAttack?.numAttackers;
|
||||
if (typeof numAttackers !== 'number' || numAttackers % 1 !== 0) {
|
||||
data.damageOptions.groupAttack.numAttackers = null;
|
||||
}
|
||||
|
||||
foundry.utils.mergeObject(this.config.damageOptions, data.damageOptions);
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static updateGroupAttack() {
|
||||
const targets = Array.from(game.user.targets);
|
||||
if (targets.length === 0)
|
||||
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noTokenTargeted'));
|
||||
|
||||
const actorId = this.roll.data.parent.id;
|
||||
const range = this.config.damageOptions.groupAttack.range;
|
||||
const groupAttackTokens = game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(actorId, range);
|
||||
|
||||
this.config.damageOptions.groupAttack.numAttackers = groupAttackTokens.length;
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,14 @@ export const range = {
|
|||
}
|
||||
};
|
||||
|
||||
export const groupAttackRange = {
|
||||
melee: range.melee,
|
||||
veryClose: range.veryClose,
|
||||
close: range.close,
|
||||
far: range.far,
|
||||
veryFar: range.veryFar
|
||||
};
|
||||
|
||||
/* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */
|
||||
export const templateTypes = {
|
||||
CIRCLE: 'circle',
|
||||
|
|
|
|||
|
|
@ -280,6 +280,26 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
};
|
||||
|
||||
if (this.damage) {
|
||||
config.isDirect = this.damage.direct;
|
||||
|
||||
const groupAttackTokens = this.damage.groupAttack
|
||||
? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(
|
||||
this.actor.id,
|
||||
this.damage.groupAttack
|
||||
)
|
||||
: null;
|
||||
|
||||
config.damageOptions = {
|
||||
groupAttack: this.damage.groupAttack
|
||||
? {
|
||||
numAttackers: Math.max(groupAttackTokens.length, 1),
|
||||
range: this.damage.groupAttack
|
||||
}
|
||||
: null
|
||||
};
|
||||
}
|
||||
|
||||
DHBaseAction.applyKeybindings(config);
|
||||
return config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
action: new fields.StringField()
|
||||
}),
|
||||
damage: new fields.ObjectField(),
|
||||
damageOptions: new fields.ObjectField(),
|
||||
costs: new fields.ArrayField(new fields.ObjectField()),
|
||||
successConsumed: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@ export default class DamageField extends fields.SchemaField {
|
|||
initial: false,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label'
|
||||
}),
|
||||
direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' })
|
||||
direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' }),
|
||||
groupAttack: new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.groupAttackRange,
|
||||
blank: true,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label'
|
||||
})
|
||||
};
|
||||
super(damageFields, options, context);
|
||||
}
|
||||
|
|
@ -224,6 +229,22 @@ export default class DamageField extends fields.SchemaField {
|
|||
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
|
||||
);
|
||||
}
|
||||
|
||||
static getGroupAttackTokens(actorId, range) {
|
||||
if (!canvas.scene) return [];
|
||||
|
||||
const targets = Array.from(game.user.targets);
|
||||
const rangeSettings = canvas.scene?.rangeSettings;
|
||||
if (!rangeSettings) return [];
|
||||
|
||||
const maxDistance = rangeSettings[range];
|
||||
return canvas.scene.tokens.filter(x => {
|
||||
if (x.actor?.id !== actorId) return false;
|
||||
if (targets.every(target => x.object.distanceTo(target) > maxDistance)) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ export default class DamageRoll extends DHRoll {
|
|||
constructFormula(config) {
|
||||
this.options.isCritical = config.isCritical;
|
||||
for (const [index, part] of this.options.roll.entries()) {
|
||||
const isHitpointPart = part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id;
|
||||
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
||||
part.roll.terms = Roll.parse(part.roll.formula, config.data);
|
||||
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
|
|
@ -169,7 +170,16 @@ export default class DamageRoll extends DHRoll {
|
|||
);
|
||||
}
|
||||
|
||||
if (config.isCritical && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||
if (config.damageOptions.groupAttack?.numAttackers > 1 && isHitpointPart) {
|
||||
const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm];
|
||||
for (const term of part.roll.terms) {
|
||||
if (damageTypes.some(type => term instanceof type)) {
|
||||
term.number *= config.damageOptions.groupAttack.numAttackers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isCritical && isHitpointPart) {
|
||||
const total = part.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0);
|
||||
if (total > 0) {
|
||||
part.roll.terms.push(...this.formatModifier(total));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
import DHToken from './token.mjs';
|
||||
|
||||
export default class DhScene extends Scene {
|
||||
get rangeSettings() {
|
||||
const { custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;
|
||||
const sceneMeasurements = this.flags.daggerheart?.rangeMeasurement;
|
||||
const globalMeasurements = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
||||
).rangeMeasurement;
|
||||
return sceneMeasurements?.setting === custom.id ? sceneMeasurements : globalMeasurements;
|
||||
}
|
||||
|
||||
/** A map of `TokenDocument` IDs embedded in this scene long with new dimensions from actor size-category changes */
|
||||
#sizeSyncBatch = new Map();
|
||||
|
||||
|
|
|
|||
|
|
@ -118,13 +118,6 @@ const getTemplateDistance = range => {
|
|||
const rangeNumber = Number(range);
|
||||
if (!Number.isNaN(rangeNumber)) return rangeNumber;
|
||||
|
||||
const { custom } = CONFIG.DH.GENERAL.sceneRangeMeasurementSetting;
|
||||
const sceneMeasurements = canvas.scene?.flags.daggerheart?.rangeMeasurement;
|
||||
const globalMeasurements = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.variantRules
|
||||
).rangeMeasurement;
|
||||
|
||||
const settings = sceneMeasurements?.setting === custom.id ? sceneMeasurements : globalMeasurements;
|
||||
return settings[range];
|
||||
const settings = canvas.scene?.rangeSettings;
|
||||
return settings ? settings[range] : 0;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue