[Feature] ActiveEffect Auras (#2049)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run

* Added Attached type to action areas
* Added functionality to Elemental Aura
This commit is contained in:
WBHarry 2026-07-02 01:00:36 +02:00 committed by GitHub
parent d36c141d36
commit cdf6e7fdd0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 213 additions and 114 deletions

View file

@ -113,7 +113,8 @@
"area": {
"sectionTitle": "Areas",
"shape": "Shape",
"size": "Size"
"size": "Size",
"hasHole": "Area Hole"
},
"displayInChat": "Display in chat",
"deleteTriggerTitle": "Delete Trigger",
@ -870,6 +871,10 @@
"bruiser": "for each Bruiser adversary.",
"solo": "for each Solo adversary."
},
"AreaTypes": {
"attached": { "label": "Attached To Token" },
"placed": { "label": "Placed In Scene" }
},
"ArmorInteraction": {
"none": { "label": "Ignores Armor" },
"active": { "label": "Active w/ Armor" },

View file

@ -96,7 +96,7 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
return inBounds.length === 1 ? inBounds[0] : null;
}
static getTemplateShape({ type, angle, range, direction } = {}) {
static getTemplateShape({ shapeType, angle, range, direction, hasHole } = {}) {
const { line, rectangle, inFront, cone, circle, emanation } = CONFIG.DH.GENERAL.templateTypes;
/* Length calculation */
@ -112,11 +112,11 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
const shapeData = {
...canvas.mousePosition,
type: type,
type: shapeType,
direction: direction ?? 0
};
switch (type) {
switch (shapeType) {
case rectangle.id:
shapeData.width = length;
shapeData.height = length;
@ -145,7 +145,8 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
y: 0,
width: 1,
height: 1,
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1
shape: game.canvas.grid.isHexagonal ? CONST.TOKEN_SHAPES.ELLIPSE_1 : CONST.TOKEN_SHAPES.RECTANGLE_1,
hole: hasHole
};
break;
}

View file

@ -119,6 +119,10 @@ export const advantageState = {
export const areaTypes = {
placed: {
id: 'placed',
label: 'Placed Area'
label: 'DAGGERHEART.CONFIG.AreaTypes.placed.label'
},
attached: {
id: 'attached',
label: 'DAGGERHEART.CONFIG.AreaTypes.attached.label'
}
};

View file

@ -33,6 +33,10 @@ export default class AreasField extends fields.ArrayField {
initial: CONFIG.DH.GENERAL.range.veryClose.id,
label: 'DAGGERHEART.ACTIONS.Config.area.size'
}),
hasHole: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.ACTIONS.Config.area.hasHole'
}),
effects: new fields.ArrayField(new fields.DocumentIdField())
});
super(element, options, context);

View file

@ -276,8 +276,12 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
async onCreateAreas(event) {
const createArea = async selectedArea => {
const effects = selectedArea.effects.map(effect => this.system.action.item.effects.get(effect).uuid);
const { shape: type, size: range } = selectedArea;
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range });
const { shape: shapeType, size: range, hasHole } = selectedArea;
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({
shapeType,
range,
hasHole
});
const scene = game.scenes.get(game.user.viewedScene);
const level = scene.levels.find(x => x.isView);
@ -305,7 +309,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
visibility: CONST.REGION_VISIBILITY.ALWAYS
};
const placeRegion = data => {
canvas.regions.placeRegion(data, { create: true });
canvas.regions.placeRegion(data, {
create: true,
attachToToken: selectedArea.type === CONFIG.DH.ACTIONS.areaTypes.attached.id
});
};
// Regions with effects must be placed by the GM

View file

@ -58,10 +58,11 @@ export const renderMeasuredTemplate = async event => {
if (!type || !range || !game.canvas.scene) return;
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({
type,
shapetype: type,
angle,
range,
direction
direction,
hasHole: false
});
await canvas.regions.placeRegion(

View file

@ -52,7 +52,7 @@
"includeBase": false
},
"target": {
"type": "hostile",
"type": "",
"amount": 1
},
"effects": [
@ -63,13 +63,23 @@
],
"name": "Fire",
"img": "icons/magic/fire/barrier-wall-explosion-orange.webp",
"range": "close"
"range": "",
"areas": [
{
"name": "Elemental Aura: Fire",
"type": "attached",
"shape": "emanation",
"size": "close",
"hasHole": false,
"effects": []
}
]
},
"xDBLH5TWidvrYF7z": {
"type": "effect",
"_id": "xDBLH5TWidvrYF7z",
"systemPath": "actions",
"description": "<p>When an adversary marks 1 or more Hit Points, they must also mark a Stress.</p>",
"description": "<p>Your allies gain a +1 bonus to Strength.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
@ -78,19 +88,26 @@
"max": "",
"recovery": null
},
"effects": [
{
"_id": "WRuijfHxmUscAa69",
"onSave": false
}
],
"effects": [],
"target": {
"type": "friendly",
"type": "",
"amount": null
},
"name": "Earth",
"img": "icons/magic/control/buff-strength-muscle-damage-red.webp",
"range": "close"
"range": "",
"areas": [
{
"name": "Elemental Aura: Earth",
"type": "attached",
"shape": "emanation",
"size": "close",
"hasHole": true,
"effects": [
"yvpWNCNobg0LLjey"
]
}
]
},
"S4t5HlgxWlHwaBDw": {
"type": "effect",
@ -112,12 +129,22 @@
}
],
"target": {
"type": "hostile",
"type": "self",
"amount": null
},
"name": "Water",
"img": "icons/magic/water/vortex-water-whirlpool.webp",
"range": "close"
"range": "self",
"areas": [
{
"name": "Elemental Aura: Water",
"type": "attached",
"shape": "emanation",
"size": "close",
"hasHole": false,
"effects": []
}
]
},
"hAsKFFewtTqd1gg9": {
"type": "attack",
@ -138,15 +165,10 @@
"includeBase": false
},
"target": {
"type": "any",
"type": "self",
"amount": null
},
"effects": [
{
"_id": "mJBA2QTyM9SM5NVS",
"onSave": false
}
],
"effects": [],
"roll": {
"type": "diceSet",
"trait": null,
@ -169,7 +191,19 @@
},
"name": "Air",
"img": "icons/magic/air/air-burst-spiral-blue-gray.webp",
"range": ""
"range": "self",
"areas": [
{
"name": "Elemental Aura: Air",
"type": "attached",
"shape": "emanation",
"size": "close",
"hasHole": false,
"effects": [
"vnKV5hsO4DVjURAl"
]
}
]
}
},
"originItemType": null,
@ -181,49 +215,6 @@
}
},
"effects": [
{
"name": "Elemental Aura (Earth)",
"img": "icons/magic/control/buff-strength-muscle-damage-orange.webp",
"origin": "Compendium.daggerheart.subclasses.Item.2JH9NaOh69yN80Gw",
"transfer": false,
"_id": "WRuijfHxmUscAa69",
"type": "base",
"system": {
"rangeDependence": {
"enabled": false,
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "1",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"tint": "#ffffff",
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null
},
"_key": "!items.effects!2JH9NaOh69yN80Gw.WRuijfHxmUscAa69"
},
{
"name": "Elemental Aura (Water)",
"img": "icons/magic/water/vortex-water-whirlpool.webp",
@ -237,18 +228,15 @@
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"description": "",
"tint": "#ffffff",
@ -258,6 +246,9 @@
"_stats": {
"compendiumSource": null
},
"start": null,
"showIcon": 1,
"folder": null,
"_key": "!items.effects!2JH9NaOh69yN80Gw.H7W52ps5d3UGmaFr"
},
{
@ -273,18 +264,15 @@
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"description": "",
"tint": "#ffffff",
@ -294,43 +282,120 @@
"_stats": {
"compendiumSource": null
},
"start": null,
"showIcon": 1,
"folder": null,
"_key": "!items.effects!2JH9NaOh69yN80Gw.WX5AMEpmUAutB9Hm"
},
{
"name": "Elemental Aura (Air)",
"img": "icons/magic/air/air-burst-spiral-blue-gray.webp",
"origin": "Compendium.daggerheart.subclasses.Item.2JH9NaOh69yN80Gw",
"name": "Elemental Aura: Earth",
"disabled": false,
"img": "icons/magic/control/buff-strength-muscle-damage-red.webp",
"description": "<p>Your allies gain a +1 bonus to Strength.</p>",
"transfer": false,
"_id": "mJBA2QTyM9SM5NVS",
"type": "base",
"statuses": [],
"system": {
"rangeDependence": {
"enabled": false,
"type": "withinRange",
"target": "hostile",
"range": "melee"
}
},
"changes": [
{
"key": "system.traits.strength.value",
"type": "add",
"value": 1,
"priority": null,
"phase": "initial"
}
],
"duration": {
"description": "",
"type": ""
},
"stacking": null,
"targetDispositions": [
1
]
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"_id": "yvpWNCNobg0LLjey",
"type": "base",
"start": {
"time": 0,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
"combatant": null,
"initiative": null,
"round": null,
"turn": null
},
"description": "",
"duration": {
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"origin": null,
"tint": "#ffffff",
"statuses": [],
"showIcon": 1,
"folder": null,
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null
},
"_key": "!items.effects!2JH9NaOh69yN80Gw.mJBA2QTyM9SM5NVS"
"_key": "!items.effects!2JH9NaOh69yN80Gw.yvpWNCNobg0LLjey"
},
{
"name": "Elemental Aura: Air",
"disabled": false,
"img": "icons/magic/air/air-burst-spiral-blue-gray.webp",
"description": "<p><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.753);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">When you or an ally takes damage from an attack beyond Melee range, reduce the damage by 1d8.</span></p>",
"transfer": false,
"statuses": [],
"system": {
"rangeDependence": {
"enabled": false,
"type": "withinRange",
"target": "hostile",
"range": "melee"
},
"changes": [],
"duration": {
"description": "",
"type": ""
},
"stacking": null,
"targetDispositions": [
1
]
},
"_id": "vnKV5hsO4DVjURAl",
"type": "base",
"start": {
"time": 0,
"combat": null,
"combatant": null,
"initiative": null,
"round": null,
"turn": null
},
"duration": {
"value": null,
"units": "seconds",
"expiry": null,
"expired": false
},
"origin": null,
"tint": "#ffffff",
"showIcon": 1,
"folder": null,
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null
},
"_key": "!items.effects!2JH9NaOh69yN80Gw.vnKV5hsO4DVjURAl"
}
],
"sort": 100000,

View file

@ -384,6 +384,10 @@
justify-content: space-between;
}
label {
white-space: nowrap;
}
.btn {
padding-top: 15px;
}
@ -399,6 +403,13 @@
> .checkbox {
align-self: end;
}
.auto-sized {
width: auto;
display: flex;
flex-direction: column;
align-items: center;
}
}
.form-group {

View file

@ -11,9 +11,10 @@
<a class="btn" data-tooltip="{{localize "CONTROLS.CommonDelete"}}" data-action="removeElement" data-index="{{index}}"><i class="fas fa-trash"></i></a>
</div>
<div class="nest-inputs">
{{formField ../fields.type value=area.type name=(concat "areas." index ".type") localize=true}}
{{formField ../fields.shape value=area.shape name=(concat "areas." index ".shape") localize=true}}
{{formField ../fields.size value=area.size name=(concat "areas." index ".size") localize=true}}
{{formField ../fields.type value=area.type name=(concat "areas." index ".type") localize=true blank=false}}
{{formField ../fields.shape value=area.shape name=(concat "areas." index ".shape") localize=true blank=false}}
{{formField ../fields.size value=area.size name=(concat "areas." index ".size") localize=true blank=false}}
{{formField ../fields.hasHole value=area.hasHole name=(concat "areas." index ".hasHole") localize=true classes="auto-sized" }}
</div>
<div class="sub-section-header">