mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-04-21 23:13:39 +02:00
Added functionality
This commit is contained in:
parent
8808e4646d
commit
5e608dea99
12 changed files with 219 additions and 77 deletions
10
lang/en.json
10
lang/en.json
|
|
@ -161,7 +161,12 @@
|
|||
"rangeDependence": {
|
||||
"title": "Range Dependence"
|
||||
},
|
||||
"stacking": { "title": "Stacking" }
|
||||
"stacking": { "title": "Stacking" },
|
||||
"area": {
|
||||
"title": "Area",
|
||||
"shape": "Shape",
|
||||
"size": "Size"
|
||||
}
|
||||
},
|
||||
"RangeDependance": {
|
||||
"hint": "Settings for an optional distance at which this effect should activate",
|
||||
|
|
@ -1967,6 +1972,9 @@
|
|||
"passive": "Passive",
|
||||
"temporary": "Temporary"
|
||||
},
|
||||
"AreaTypes": {
|
||||
"placed": { "name": "Placed Area" }
|
||||
},
|
||||
"Types": {
|
||||
"damage": {
|
||||
"name": "Damage"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
super(options);
|
||||
|
||||
this.changeChoices = DhActiveEffectConfig.getChangeChoices();
|
||||
this.areaDaggerheartRange = true;
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
|
|
@ -162,6 +163,14 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
htmlElement
|
||||
.querySelector('.armor-damage-thresholds-checkbox')
|
||||
?.addEventListener('change', this.armorDamageThresholdToggle.bind(this));
|
||||
|
||||
htmlElement
|
||||
.querySelector('.area-checkbox')
|
||||
?.addEventListener('change', this.areaToggle.bind(this));
|
||||
|
||||
htmlElement
|
||||
.querySelector('.area-range-type-input')
|
||||
?.addEventListener('change', this.areaRangeTypeToggle.bind(this));
|
||||
}
|
||||
|
||||
async _prepareContext(options) {
|
||||
|
|
@ -196,6 +205,8 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
label: _loc(`EFFECT.DURATION.UNITS.${value}`),
|
||||
group: CONST.ACTIVE_EFFECT_TIME_DURATION_UNITS.includes(value) ? groups.time : groups.combat
|
||||
}));
|
||||
partContext.areaDaggerheartRange = this.areaDaggerheartRange;
|
||||
partContext.templateRanges = CONFIG.DH.GENERAL.templateRanges;
|
||||
break;
|
||||
case 'changes':
|
||||
const singleTypes = ['armor'];
|
||||
|
|
@ -260,6 +271,28 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
|
|||
return this.submit({ updateData: { system: { changes } } });
|
||||
}
|
||||
|
||||
areaToggle(event) {
|
||||
const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form));
|
||||
|
||||
if(event.target.checked) {
|
||||
const fields = game.system.api.data.activeEffects.BaseEffect._schema.fields.area.fields;
|
||||
submitData.system.area = {
|
||||
type: fields.type.initial,
|
||||
shape: fields.shape.initial,
|
||||
size: CONFIG.DH.GENERAL.range.veryClose.id,
|
||||
};
|
||||
} else {
|
||||
submitData.system.area = null;
|
||||
}
|
||||
|
||||
return this.submit({ updateData: { system: { area: submitData.system.area } } });
|
||||
}
|
||||
|
||||
areaRangeTypeToggle(_event) {
|
||||
this.areaDaggerheartRange = !this.areaDaggerheartRange;
|
||||
this.render();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
_renderChange(context) {
|
||||
const { change, index, defaultPriority } = context;
|
||||
|
|
|
|||
|
|
@ -95,4 +95,48 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
|
|||
});
|
||||
return inBounds.length === 1 ? inBounds[0] : null;
|
||||
}
|
||||
|
||||
static getTemplateShape({ type, angle, range, direction } = {}) {
|
||||
const { line, rectangle, inFront, cone } = CONFIG.DH.GENERAL.templateTypes;
|
||||
|
||||
const usedAngle =
|
||||
type === cone.id ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === inFront.id ? '180' : undefined;
|
||||
|
||||
const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields;
|
||||
const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial;
|
||||
const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue();
|
||||
const dimensionConstant = sceneGridSize / sceneGridDistance;
|
||||
|
||||
const rangeNumber = Number(range);
|
||||
const settings = canvas.scene?.rangeSettings;
|
||||
const baseDistance = (!Number.isNaN(rangeNumber) ? rangeNumber : (settings ? settings[range] : 0)) * dimensionConstant;
|
||||
|
||||
const length = baseDistance;
|
||||
const radius = length;
|
||||
|
||||
const shapeWidth = type === line.id ? 5 * dimensionConstant : type === rectangle.id ? length : undefined;
|
||||
const shapeType = type === inFront.id ? cone.id : type;
|
||||
|
||||
const { width, height } = game.canvas.scene.dimensions;
|
||||
return {
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
base: {
|
||||
type: 'token',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1
|
||||
},
|
||||
t: shapeType,
|
||||
length: length,
|
||||
width: shapeWidth,
|
||||
height: length,
|
||||
angle: usedAngle,
|
||||
radius: radius,
|
||||
direction: direction ?? 0,
|
||||
type: shapeType
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,3 +62,10 @@ export const effectTypes = {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const areaTypes = {
|
||||
placed: {
|
||||
id: 'placed',
|
||||
label: 'DAGGERHEART.EFFECTS.AreaTypes.placed.name'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,13 +22,6 @@ export const ruleChoice = {
|
|||
};
|
||||
|
||||
export const templateRanges = {
|
||||
self: {
|
||||
id: 'self',
|
||||
short: 's',
|
||||
label: 'DAGGERHEART.CONFIG.Range.self.name',
|
||||
description: 'DAGGERHEART.CONFIG.Range.self.description',
|
||||
distance: 0
|
||||
},
|
||||
melee: {
|
||||
id: 'melee',
|
||||
short: 'm',
|
||||
|
|
@ -80,12 +73,30 @@ export const groupAttackRange = {
|
|||
|
||||
/* circle|cone|rect|ray used to be CONST.MEASURED_TEMPLATE_TYPES. Hardcoded for now */
|
||||
export const templateTypes = {
|
||||
CIRCLE: 'circle',
|
||||
CONE: 'cone',
|
||||
RECTANGLE: 'rectangle',
|
||||
LINE: 'line',
|
||||
EMANATION: 'emanation',
|
||||
INFRONT: 'inFront'
|
||||
circle: {
|
||||
id: 'circle',
|
||||
label: 'Circle'
|
||||
},
|
||||
cone: {
|
||||
id: 'cone',
|
||||
label: 'Cone'
|
||||
},
|
||||
rectangle: {
|
||||
id: 'rectangle',
|
||||
label: 'Rectangle'
|
||||
},
|
||||
line: {
|
||||
id: 'line',
|
||||
label: 'Line'
|
||||
},
|
||||
emanation: {
|
||||
id: 'emanation',
|
||||
label: 'Emanation'
|
||||
},
|
||||
inFront: {
|
||||
id: 'inFront',
|
||||
label: 'In Front'
|
||||
}
|
||||
};
|
||||
|
||||
export const rangeInclusion = {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,26 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
|||
max: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.max' })
|
||||
},
|
||||
{ nullable: true, initial: null }
|
||||
)
|
||||
),
|
||||
area: new fields.SchemaField({
|
||||
type: new fields.StringField({
|
||||
nullable: false,
|
||||
choices: CONFIG.DH.EFFECTS.areaTypes,
|
||||
initial: CONFIG.DH.EFFECTS.areaTypes.placed.id,
|
||||
label: 'DAGGERHEART.GENERAL.type'
|
||||
}),
|
||||
shape: new fields.StringField({
|
||||
nullable: false,
|
||||
choices: CONFIG.DH.GENERAL.templateTypes,
|
||||
initial: CONFIG.DH.GENERAL.templateTypes.circle.id,
|
||||
label: 'DAGGERHEART.ACTIVEEFFECT.Config.area.shape'
|
||||
}),
|
||||
size: new fields.StringField({
|
||||
nullable: false,
|
||||
initial: CONFIG.DH.GENERAL.range.veryClose.id,
|
||||
label: 'DAGGERHEART.ACTIVEEFFECT.Config.area.size'
|
||||
}),
|
||||
}, { nullable: true, initial: null })
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,10 +45,18 @@ export default class EffectsField extends fields.ArrayField {
|
|||
* @param {object[]} targets Array of formatted targets
|
||||
*/
|
||||
static async applyEffects(targets) {
|
||||
if (!this.effects?.length || !targets?.length) return;
|
||||
if (!this.effects?.length) return;
|
||||
|
||||
let effects = this.effects.map(e => (this.item.applyEffects ?? this.item.effects).get(e._id));
|
||||
const targettingRequired = effects.some(x => x.system.area?.type !== CONFIG.DH.EFFECTS.areaTypes.placed.id);
|
||||
if (targettingRequired && !targets?.length)
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));;
|
||||
|
||||
for(const effect of effects.filter(effect => effect.system.area?.type === CONFIG.DH.EFFECTS.areaTypes.placed.id)) {
|
||||
await EffectsField.placeEffectRegion(effect);
|
||||
}
|
||||
|
||||
const conditions = CONFIG.DH.GENERAL.conditions();
|
||||
let effects = this.effects;
|
||||
const messageTargets = [];
|
||||
targets.forEach(async baseToken => {
|
||||
if (this.hasSave && baseToken.saved.success === true) effects = this.effects.filter(e => e.onSave === true);
|
||||
|
|
@ -72,20 +80,24 @@ export default class EffectsField extends fields.ArrayField {
|
|||
: null
|
||||
});
|
||||
|
||||
effects.forEach(async e => {
|
||||
const effect = (this.item.applyEffects ?? this.item.effects).get(e._id);
|
||||
effects.forEach(async effect => {
|
||||
if (!token.actor || !effect) return;
|
||||
await EffectsField.applyEffect(effect, token.actor);
|
||||
if (effect.system.area?.type !== CONFIG.DH.EFFECTS.areaTypes.placed.id)
|
||||
await EffectsField.applyEffect(effect, token.actor);
|
||||
});
|
||||
});
|
||||
|
||||
if (messageTargets.length === 0) return;
|
||||
if (targettingRequired && messageTargets.length === 0)
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));;
|
||||
|
||||
const summaryMessageSettings = game.settings.get(
|
||||
CONFIG.DH.id,
|
||||
CONFIG.DH.SETTINGS.gameSettings.Automation
|
||||
).summaryMessages;
|
||||
if (!summaryMessageSettings.effects) return;
|
||||
const appliedEffects = effects
|
||||
.filter(e => e.system.area?.type !== CONFIG.DH.EFFECTS.areaTypes.placed.id);
|
||||
|
||||
if (!summaryMessageSettings.effects || !appliedEffects.length) return;
|
||||
|
||||
const cls = getDocumentClass('ChatMessage');
|
||||
const msg = {
|
||||
|
|
@ -96,7 +108,7 @@ export default class EffectsField extends fields.ArrayField {
|
|||
content: await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/ui/chat/effectSummary.hbs',
|
||||
{
|
||||
effects: this.effects.map(e => (this.item.applyEffects ?? this.item.effects).get(e._id)),
|
||||
effects: appliedEffects,
|
||||
targets: messageTargets
|
||||
}
|
||||
)
|
||||
|
|
@ -120,6 +132,34 @@ export default class EffectsField extends fields.ArrayField {
|
|||
await ActiveEffect.implementation.create(effectData, { parent: actor });
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static async placeEffectRegion(effect) {
|
||||
const { shape: type, size: range } = effect.system.area;
|
||||
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range });
|
||||
|
||||
await canvas.regions.placeRegion(
|
||||
{
|
||||
name: effect.name,
|
||||
shapes: [shapeData],
|
||||
restriction: { enabled: false, type: 'move', priority: 0 },
|
||||
behaviors: [{
|
||||
name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'),
|
||||
type: 'applyActiveEffect',
|
||||
system: {
|
||||
effects: [effect.uuid]
|
||||
}
|
||||
}],
|
||||
displayMeasurements: true,
|
||||
locked: false,
|
||||
ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE },
|
||||
visibility: CONST.REGION_VISIBILITY.ALWAYS
|
||||
},
|
||||
{ create: true }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the automation setting for execute method for current user role
|
||||
* @returns {boolean} If execute should be triggered automatically
|
||||
|
|
|
|||
|
|
@ -243,8 +243,6 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default function DhTemplateEnricher(match, _options) {
|
|||
)?.id
|
||||
: params.range;
|
||||
|
||||
if (!Object.values(CONFIG.DH.GENERAL.templateTypes).find(x => x === type) || !range) return match[0];
|
||||
if (!CONFIG.DH.GENERAL.templateTypes[type] || !range) return match[0];
|
||||
|
||||
const label = game.i18n.localize(`DAGGERHEART.CONFIG.TemplateTypes.${type}`);
|
||||
const rangeDisplay = Number.isNaN(Number(range))
|
||||
|
|
@ -49,8 +49,6 @@ export default function DhTemplateEnricher(match, _options) {
|
|||
}
|
||||
|
||||
export const renderMeasuredTemplate = async event => {
|
||||
const { LINE, RECTANGLE, INFRONT, CONE } = CONFIG.DH.GENERAL.templateTypes;
|
||||
|
||||
const button = event.currentTarget,
|
||||
type = button.dataset.type,
|
||||
range = button.dataset.range,
|
||||
|
|
@ -59,49 +57,16 @@ export const renderMeasuredTemplate = async event => {
|
|||
|
||||
if (!type || !range || !game.canvas.scene) return;
|
||||
|
||||
const usedType = type === 'inFront' ? 'cone' : type;
|
||||
const usedAngle =
|
||||
type === CONE ? (angle ?? CONFIG.MeasuredTemplate.defaults.angle) : type === INFRONT ? '180' : undefined;
|
||||
|
||||
let baseDistance = getTemplateDistance(range);
|
||||
|
||||
const { grid, distance } = CONFIG.Scene.documentClass.schema.fields.grid.fields;
|
||||
const sceneGridSize = canvas.scene?.grid.size ?? grid.size.initial;
|
||||
const sceneGridDistance = canvas.scene?.grid.distance ?? distance.getInitialValue();
|
||||
const dimensionConstant = sceneGridSize / sceneGridDistance;
|
||||
|
||||
baseDistance *= dimensionConstant;
|
||||
|
||||
const length = baseDistance;
|
||||
const radius = length;
|
||||
|
||||
const shapeWidth = type === LINE ? 5 * dimensionConstant : type === RECTANGLE ? length : undefined;
|
||||
|
||||
const { width, height } = game.canvas.scene.dimensions;
|
||||
const shapeData = {
|
||||
x: width / 2,
|
||||
y: height / 2,
|
||||
base: {
|
||||
type: 'token',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1
|
||||
},
|
||||
t: usedType,
|
||||
length: length,
|
||||
width: shapeWidth,
|
||||
height: length,
|
||||
angle: usedAngle,
|
||||
radius: radius,
|
||||
direction: direction,
|
||||
type: usedType
|
||||
};
|
||||
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({
|
||||
type,
|
||||
angle,
|
||||
range,
|
||||
direction,
|
||||
});
|
||||
|
||||
await canvas.regions.placeRegion(
|
||||
{
|
||||
name: usedType.capitalize(),
|
||||
name: type.capitalize(),
|
||||
shapes: [shapeData],
|
||||
restriction: { enabled: false, type: 'move', priority: 0 },
|
||||
behaviors: [],
|
||||
|
|
@ -113,11 +78,3 @@ export const renderMeasuredTemplate = async event => {
|
|||
{ create: true }
|
||||
);
|
||||
};
|
||||
|
||||
const getTemplateDistance = range => {
|
||||
const rangeNumber = Number(range);
|
||||
if (!Number.isNaN(rangeNumber)) return rangeNumber;
|
||||
|
||||
const settings = canvas.scene?.rangeSettings;
|
||||
return settings ? settings[range] : 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -298,7 +298,6 @@
|
|||
padding-top: 0;
|
||||
padding-bottom: 4px;
|
||||
min-height: auto;
|
||||
row-gap: 0;
|
||||
|
||||
legend {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
}
|
||||
|
||||
.armor-change-container {
|
||||
row-gap: 0;
|
||||
|
||||
header {
|
||||
padding: 0;
|
||||
left: -0.25rem; // TODO: Find why this header is offset 0.25rem to the right so this can be removed.
|
||||
|
|
|
|||
|
|
@ -48,4 +48,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="one-column optional">
|
||||
<legend>
|
||||
{{localize "DAGGERHEART.ACTIVEEFFECT.Config.area.title"}}
|
||||
<input type="checkbox" class="area-checkbox" {{checked source.system.area}} />
|
||||
</legend>
|
||||
|
||||
{{#if document.system.area}}
|
||||
|
||||
{{formGroup systemFields.area.fields.type value=source.system.area.type localize=true blank=false }}
|
||||
{{formGroup systemFields.area.fields.shape value=source.system.area.shape localize=true blank=false }}
|
||||
<div class="form-group">
|
||||
<div class="form-fields">
|
||||
{{#if areaDaggerheartRange}}
|
||||
{{formGroup systemFields.area.fields.size value=source.system.area.size choices=templateRanges blank=false localize=true }}
|
||||
{{else}}
|
||||
{{formGroup systemFields.area.fields.size value=source.system.area.size localize=true }}
|
||||
{{/if}}
|
||||
<label>{{localize "Use Daggerheart Range"}}</label>
|
||||
<input type="checkbox" class="area-range-type-input" {{checked areaDaggerheartRange}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
</section>
|
||||
Loading…
Add table
Add a link
Reference in a new issue