mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-03-07 14:36:13 +01:00
Added the ability for effects to have stacks
This commit is contained in:
parent
4aab5d315a
commit
79057b0718
11 changed files with 187 additions and 18 deletions
|
|
@ -138,7 +138,8 @@
|
||||||
"Config": {
|
"Config": {
|
||||||
"rangeDependence": {
|
"rangeDependence": {
|
||||||
"title": "Range Dependence"
|
"title": "Range Dependence"
|
||||||
}
|
},
|
||||||
|
"stacking": { "title": "Stacking" }
|
||||||
},
|
},
|
||||||
"RangeDependance": {
|
"RangeDependance": {
|
||||||
"hint": "Settings for an optional distance at which this effect should activate",
|
"hint": "Settings for an optional distance at which this effect should activate",
|
||||||
|
|
@ -2864,6 +2865,8 @@
|
||||||
},
|
},
|
||||||
"EffectsDisplay": {
|
"EffectsDisplay": {
|
||||||
"removeThing": "[Right Click] Remove {thing}",
|
"removeThing": "[Right Click] Remove {thing}",
|
||||||
|
"increaseStacks": "[Left Click] Increment Stacks",
|
||||||
|
"decreaseStacks": "[Right Click] Decrement Stacks",
|
||||||
"appliedBy": "Applied By: {by}"
|
"appliedBy": "Applied By: {by}"
|
||||||
},
|
},
|
||||||
"ItemBrowser": {
|
"ItemBrowser": {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
|
||||||
|
|
||||||
if (this.element) {
|
if (this.element) {
|
||||||
this.element.querySelectorAll('.effect-container a').forEach(element => {
|
this.element.querySelectorAll('.effect-container a').forEach(element => {
|
||||||
element.addEventListener('contextmenu', this.removeEffect.bind(this));
|
element.addEventListener('click', this.effectLeftclick.bind(this));
|
||||||
|
element.addEventListener('contextmenu', this.effectRightclick.bind(this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,11 +88,28 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeEffect(event) {
|
async effectLeftclick(event) {
|
||||||
const element = event.target.closest('.effect-container');
|
const element = event.target.closest('.effect-container');
|
||||||
const effects = DhEffectsDisplay.getTokenEffects();
|
const effects = DhEffectsDisplay.getTokenEffects();
|
||||||
const effect = effects.find(x => x.id === element.dataset.effectId);
|
const effect = effects.find(x => x.id === element.dataset.effectId);
|
||||||
|
|
||||||
|
if (!effect.system.stacking?.enabled) return;
|
||||||
|
|
||||||
|
const newValue = Math.min(effect.system.stacking.value + 1, effect.system.stacking.max);
|
||||||
|
await effect.update({ 'system.stacking.value': newValue });
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async effectRightclick(event) {
|
||||||
|
const element = event.target.closest('.effect-container');
|
||||||
|
const effects = DhEffectsDisplay.getTokenEffects();
|
||||||
|
const effect = effects.find(x => x.id === element.dataset.effectId);
|
||||||
|
if (effect.system.stacking?.enabled && effect.system.stacking.value > 1) {
|
||||||
|
await effect.update({ 'system.stacking.value': effect.system.stacking.value - 1 });
|
||||||
|
} else {
|
||||||
await effect.delete();
|
await effect.delete();
|
||||||
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
if (!effect.img) continue;
|
if (!effect.img) continue;
|
||||||
const promise =
|
const promise =
|
||||||
effect === overlayEffect
|
effect === overlayEffect
|
||||||
? this._drawOverlay(effect.img, effect.tint)
|
? this._drawOverlay(effect.img, effect.tint, effect)
|
||||||
: this._drawEffect(effect.img, effect.tint);
|
: this._drawEffect(effect.img, effect.tint, effect);
|
||||||
promises.push(
|
promises.push(
|
||||||
promise.then(e => {
|
promise.then(e => {
|
||||||
if (e) e.zIndex = i;
|
if (e) e.zIndex = i;
|
||||||
|
|
@ -45,6 +45,38 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
|
||||||
this.renderFlags.set({ refreshEffects: true });
|
this.renderFlags.set({ refreshEffects: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**@inheritdoc */
|
||||||
|
async _drawEffect(src, tint, effect) {
|
||||||
|
if (!src) return;
|
||||||
|
const tex = await loadTexture(src, { fallback: 'icons/svg/hazard.svg' });
|
||||||
|
const icon = new PIXI.Sprite(tex);
|
||||||
|
icon.tint = tint ?? 0xffffff;
|
||||||
|
|
||||||
|
if (effect?.system?.stacking?.enabled) {
|
||||||
|
const stackOverlay = new PIXI.Text(effect.system.stacking.value, {
|
||||||
|
fill: '#f3c267',
|
||||||
|
stroke: '#000000',
|
||||||
|
fontSize: 96,
|
||||||
|
strokeThickness: 4
|
||||||
|
});
|
||||||
|
const nrDigits = Math.floor(effect.system.stacking.value / 10) + 1;
|
||||||
|
stackOverlay.x = icon.width - 56 * nrDigits;
|
||||||
|
stackOverlay.y = 4;
|
||||||
|
stackOverlay.anchor.set(0, 0);
|
||||||
|
|
||||||
|
icon.addChild(stackOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.effects.addChild(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _drawOverlay(src, tint, effect) {
|
||||||
|
const icon = await this._drawEffect(src, tint, effect);
|
||||||
|
if (icon) icon.alpha = 0.8;
|
||||||
|
this.effects.overlay = icon ?? null;
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the distance from this token to another token object.
|
* Returns the distance from this token to another token object.
|
||||||
* This value is corrected to handle alternate token sizes and other grid types
|
* This value is corrected to handle alternate token sizes and other grid types
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,18 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
|
||||||
initial: CONFIG.DH.GENERAL.range.melee.id,
|
initial: CONFIG.DH.GENERAL.range.melee.id,
|
||||||
label: 'DAGGERHEART.GENERAL.range'
|
label: 'DAGGERHEART.GENERAL.range'
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
stacking: new fields.SchemaField({
|
||||||
|
enabled: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.GENERAL.enabled' }),
|
||||||
|
value: new fields.NumberField({
|
||||||
|
initial: 1,
|
||||||
|
min: 1,
|
||||||
|
integer: true,
|
||||||
|
nullable: false,
|
||||||
|
label: 'DAGGERHEART.GENERAL.value'
|
||||||
|
}),
|
||||||
|
max: new fields.NumberField({ label: 'DAGGERHEART.GENERAL.max' })
|
||||||
|
// max: new fields.StringField({ required: true, nullable: false }),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { itemAbleRollParse } from '../helpers/utils.mjs';
|
import { itemAbleRollParse } from '../helpers/utils.mjs';
|
||||||
import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs';
|
import { RefreshType } from '../systemRegistration/socket.mjs';
|
||||||
|
|
||||||
export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
@ -182,10 +182,17 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const evalValue = this.effectSafeEval(itemAbleRollParse(key, parseModel, effect.parent));
|
const evalValue = this.effectSafeEval(this.effectRollParse(key, parseModel, effect.parent, effect));
|
||||||
return evalValue ?? key;
|
return evalValue ?? key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static effectRollParse(value, actor, item, effect) {
|
||||||
|
const stackingParsedValue = effect.system.stacking?.enabled
|
||||||
|
? Roll.replaceFormulaData(value, { stacks: effect.system.stacking.value })
|
||||||
|
: value;
|
||||||
|
return itemAbleRollParse(stackingParsedValue, actor, item);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Altered Foundry safeEval to allow non-numeric return
|
* Altered Foundry safeEval to allow non-numeric return
|
||||||
* @param {string} expression
|
* @param {string} expression
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,10 @@
|
||||||
&.even {
|
&.even {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line-div {
|
line-div {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,19 @@
|
||||||
color: @golden;
|
color: @golden;
|
||||||
filter: drop-shadow(0 0 3px black);
|
filter: drop-shadow(0 0 3px black);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stacking-value {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @golden;
|
||||||
|
background: black;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid @golden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
.daggerheart.dh-style.tooltip {
|
.daggerheart.dh-style.tooltip {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
@ -13,6 +14,7 @@
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
.tooltip-header {
|
.tooltip-header {
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -35,12 +37,48 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.effect-stacks-outer-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.effect-stacks-title {
|
||||||
|
font-size: var(--font-size-20);
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-stacks-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.effect-stacks-inner-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.effect-stack-title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-hints {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
.close-hint {
|
.close-hint {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: @rustic-brown-80;
|
background: @rustic-brown-80;
|
||||||
color: @golden;
|
color: @golden;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.duration-container {
|
.duration-container {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,15 @@
|
||||||
<section class="tab{{#if tab.active}} active{{/if}}" data-group="{{tab.group}}" data-tab="{{tab.id}}">
|
<section class="tab{{#if tab.active}} active{{/if}}" data-group="{{tab.group}}" data-tab="{{tab.id}}">
|
||||||
|
<fieldset class="one-column">
|
||||||
|
<legend>{{localize "DAGGERHEART.ACTIVEEFFECT.Config.stacking.title"}}</legend>
|
||||||
|
|
||||||
|
{{formGroup systemFields.stacking.fields.enabled value=source.system.stacking.enabled localize=true }}
|
||||||
|
|
||||||
|
<div class="two-columns even full-width">
|
||||||
|
{{formGroup systemFields.stacking.fields.value value=source.system.stacking.value localize=true }}
|
||||||
|
{{formGroup systemFields.stacking.fields.max value=source.system.stacking.max localize=true }}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
<legend>{{localize "DAGGERHEART.ACTIVEEFFECT.Config.rangeDependence.title"}}</legend>
|
<legend>{{localize "DAGGERHEART.ACTIVEEFFECT.Config.rangeDependence.title"}}</legend>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
<a {{#if effect.condition}}disabled{{/if}}>
|
<a {{#if effect.condition}}disabled{{/if}}>
|
||||||
<img src="{{effect.img}}" />
|
<img src="{{effect.img}}" />
|
||||||
</a>
|
</a>
|
||||||
|
{{#if effect.system.stacking.enabled}}
|
||||||
|
<span class="stacking-value">{{effect.system.stacking.value}}</span>
|
||||||
|
{{/if}}
|
||||||
{{#if effect.condition}}<i class="effect-locked fa-solid fa-lock"></i>{{/if}}
|
{{#if effect.condition}}<i class="effect-locked fa-solid fa-lock"></i>{{/if}}
|
||||||
</span>
|
</span>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if effect.system.duration.type}}
|
{{#if effect.system.duration.type}}
|
||||||
|
|
||||||
<div class="duration-container">
|
<div class="duration-container">
|
||||||
<div class="duration-inner-container">
|
<div class="duration-inner-container">
|
||||||
<span>{{localize "EFFECT.DURATION.Label"}}:</span>
|
<span>{{localize "EFFECT.DURATION.Label"}}:</span>
|
||||||
|
|
@ -26,9 +25,38 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if effect.system.stacking.enabled}}
|
||||||
|
<div class="effect-stacks-outer-container">
|
||||||
|
<div class="effect-stacks-title">{{localize "Stacks"}}</div>
|
||||||
|
<div class="effect-stacks-container">
|
||||||
|
<div class="effect-stacks-inner-container">
|
||||||
|
<span class="effect-stack-title">{{localize "Current"}}</span>
|
||||||
|
<span>{{effect.system.stacking.value}}</span>
|
||||||
|
</div>
|
||||||
|
{{#if effect.system.stacking.max}}
|
||||||
|
<div class="effect-stacks-inner-container">
|
||||||
|
<span class="effect-stack-title">{{localize "Max"}}</span>
|
||||||
|
<span>{{effect.system.stacking.max}}</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#unless effect.isLockedCondition}}
|
{{#unless effect.isLockedCondition}}
|
||||||
|
<div class="close-hints">
|
||||||
|
{{#if effect.system.stacking.enabled}}
|
||||||
|
<p class="close-hint">
|
||||||
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.increaseStacks"}}
|
||||||
|
</p>
|
||||||
|
<p class="close-hint">
|
||||||
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.decreaseStacks"}}
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
<p class="close-hint">
|
<p class="close-hint">
|
||||||
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}}
|
<i class="fa-solid fa-computer-mouse"></i> {{localize "DAGGERHEART.UI.EffectsDisplay.removeThing" thing=(localize "DAGGERHEART.GENERAL.Effect.single")}}
|
||||||
</p>
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue