From 2f397f34fa94077c20beb7560bfb2aec6021d0b1 Mon Sep 17 00:00:00 2001 From: FoxLee Date: Thu, 19 Oct 2023 20:52:12 +1100 Subject: [PATCH 01/10] Updated "contains" helper Updated "contains" helper to also search arrays of objects for a give key/value pair. --- module/helper.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/module/helper.js b/module/helper.js index 032d9127..cb6c80d9 100644 --- a/module/helper.js +++ b/module/helper.js @@ -1252,6 +1252,26 @@ export async function handleAutoDoTs(data) { await actor.autoDoTsSocket(data.tokenID); } -Handlebars.registerHelper('contains', function(lunch, lunchbox) { - return lunchbox.includes(lunch); +/* "Contains" Handlebars Helper: checks if a value exists in an array. +* +* @param {String} lunch The value to find +* @param {Array} lunchbox The array to search +* @param {String} meal (optional) A key to pair with lunch +* @returns {boolean} true if lunch exists in lunchbox. +* If meal is provided, lunchbox is assumed to contain objects, +* and will search for one where meal = lunch. +* +* I don't know why, but meal is apparently the helper object, +* if not given? Not a null, which would have been useful :\ +* Anyway the type check shoudl take care of it. +/* */ +Handlebars.registerHelper('contains', function(lunch, lunchbox, meal) { + try{ + if(typeof meal != "string") return lunchbox.includes(lunch); + const lunchLocation = lunchbox.findIndex((x) => x[meal] == lunch); + if(lunchLocation > 0) return true; + return false; + } catch(err) { + return "Contains helper spat up. Did you give it the right parameter types?"; + } }); \ No newline at end of file From ae81487218cd4ad95368898c334927d03ed2dbb8 Mon Sep 17 00:00:00 2001 From: FoxLee Date: Thu, 19 Oct 2023 20:56:13 +1100 Subject: [PATCH 02/10] Status conditions now available I've added a subsection so users can attach one or more status conditions to an active effect. This means we can have status conditions being applied automatically by powers, and taking advantage of durations like EoT, save-ends, etc. Since conditions can be remapped by some modules, unrecognised condition IDs are retained but labelled as "unknown". If the condition map is changed back to something that includes the ID, it should be recognised again. --- module/effects/effects-config.js | 80 +++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/module/effects/effects-config.js b/module/effects/effects-config.js index 964e5f48..9c6eabe5 100644 --- a/module/effects/effects-config.js +++ b/module/effects/effects-config.js @@ -19,6 +19,7 @@ export default class ActiveEffectConfig4e extends ActiveEffectConfig { data.config = CONFIG.DND4EBETA; data.powerParent = (["power", "consumable"].includes(this.object.parent.type)); + data.config.statusEffects = CONFIG.statusEffects; return data; } @@ -29,6 +30,7 @@ export default class ActiveEffectConfig4e extends ActiveEffectConfig { activateListeners(html) { super.activateListeners(html); html.find(".effect-dot-control").click(this._onEffectDotControl.bind(this)); + html.find(".effect-status-control").click(this._onEffectStatusControl.bind(this)); html.find(".refreshes").change(this._refresh.bind(this)); } @@ -53,6 +55,31 @@ export default class ActiveEffectConfig4e extends ActiveEffectConfig { /* ----------------------------------------- */ + /** + * Handling for mouse clicks on status control buttons - adapted from _onEffectControl + * Delegate responsibility out to action-specific handlers depending on the button action. + * @param {MouseEvent} event The originating click event + */ + _onEffectStatusControl(event) { + event.preventDefault(); + const button = event.currentTarget; + switch ( button.dataset.action ) { + case "copy-name": + case "copy-icon": + case "copy-desc": + case "copy-all": + const statusId = button.closest(".effect-status").getAttribute("data-status-id"); + return this._copyStatusDetails(statusId,button.dataset.action).then(() => this.render()); + case "add": + return this._addEffectStatus(); + case "delete": + button.closest(".effect-status").remove(); + return this.submit({preventClose: true}).then(() => this.render()); + } + } + + /* ----------------------------------------- */ + /** * Handle adding a new dot to the dots array - adapted from _addEffectChange */ @@ -64,6 +91,51 @@ export default class ActiveEffectConfig4e extends ActiveEffectConfig { }}); } + /* ----------------------------------------- */ + + /** + * Handle adding a new status to the statuses array - adapted from _addEffectChange + */ + async _addEffectStatus() { + const i = this.document.statuses.size; + return this.submit({preventClose: true, updateData: { + [`statuses.${i}`] : "none" + }}); + } + + /* ----------------------------------------- */ + + /** + * Copy fluff to effect from status condition config + */ + async _copyStatusDetails(statusId,scope="copy-all") { + if(!statusId) return; + + const statuses = CONFIG.statusEffects; + + try{ + //I remembered error handling this time! This should be expected to fail if the status id isn't found, such as if you have remapped your conditions since setting up the effect. + + const statusIndex = statuses.findIndex((x) => x.id == statusId); + let effectUpdates = {}; + + if(scope == "copy-name" || scope == "copy-all"){ + effectUpdates.name = game.i18n.localize(statuses[statusIndex].label); + } + if(scope == "copy-icon" || scope == "copy-all"){ + effectUpdates.icon = statuses[statusIndex].icon; + } + if(scope == "copy-desc" || scope == "copy-all"){ + effectUpdates.description = game.i18n.localize(statuses[statusIndex].description); + } + + return this.submit({preventClose: true, updateData: effectUpdates }); + + } catch(err) { + ui.notifications.error(game.i18n.localize('ERROR.4eCopyStatusDetails')); + } + } + /* ----------------------------------------- */ /* I'm really worried that I had to override this method from the core class. I'm afraid it might mess up module compatibility or something! @@ -74,18 +146,14 @@ export default class ActiveEffectConfig4e extends ActiveEffectConfig { let data = foundry.utils.expandObject(fd.object); if ( updateData ) foundry.utils.mergeObject(data, updateData); data.changes = Array.from(Object.values(data.changes || {})); + data.statuses = Array.from(Object.values(data.statuses || {})).filter(x => x); + //The form throws an error if it's updated while there is an unselected status condition row. I can't find a way to catch it, so instead I'm just trimming data.flags.dnd4e.dots = Array.from(Object.values(data.flags.dnd4e.dots || {})); if (data.flags.dnd4e.dots.length){ for (let [i, dot] of data.flags.dnd4e.dots.entries()){ data.flags.dnd4e.dots[i].amount = dot.amount; data.flags.dnd4e.dots[i].typesArray = dot.typesArray.sort(); - /*if(!dot.type) { - data.flags.dnd4e.dots[i].typesArray = ['physical']; - } else { - let type = dot.type.toLowerCase().replaceAll(/( and )|(;(?! ))|(; )|(, )|(,(?! ))|([^;,]) (?!and)/g,"$6|"); - data.flags.dnd4e.dots[i].typesArray = type.split("|").sort(); - }*/ } } From 0c597c8c4ec5031cee455a3c8d9324e460b62623 Mon Sep 17 00:00:00 2001 From: FoxLee Date: Thu, 19 Oct 2023 20:57:54 +1100 Subject: [PATCH 03/10] New strings & fixes - New strings for effect config updates - Restored some strings that were removed from the en.json language file. (Sorry, I had a weird input bug going on last week and I missed a few of the problems it caused!) --- lang/en-au.json | 4 ++++ lang/en.json | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/lang/en-au.json b/lang/en-au.json index 1393762e..d8b69d18 100644 --- a/lang/en-au.json +++ b/lang/en-au.json @@ -1060,6 +1060,7 @@ "DND4EBETA.SpokenPrimordial": "Primordial", "DND4EBETA.SpokenSupernal": "Supernal", "DND4EBETA.Stance": "Stance", +"DND4EBETA.StatusConditions": "Status Conditions", "DND4EBETA.SubclassName": "Subclass Name", "DND4EBETA.SubName": "Class Name Attack 1", "DND4EBETA.Summoning": "Summoning", @@ -1421,6 +1422,7 @@ "EFFECTDESC.hidden": "The creature is hidden or disguised.", "EFFECTDESC.sneaking": "The creature is being stealthy. It is hidden (invisible and silent) from creatures that fail to detect it.", "EFFECTDESC.torch": "The creature is holding a light source.", +"ERROR.4eCopyStatusDetails": "Could not copy status details. This is expected if you have changed the standard list of status conditions since adding one to this effect.", "DND4EUI.AttributeKey": "Attribute Key", "DND4EUI.AddNew": "Add New", "DND4EUI.Data Type": "Data Type", @@ -1436,6 +1438,8 @@ "DND4EUI.ShowImage": "Show image", "DND4EUI.SortBy": "Sort By", "DND4EUI.StringEnterValues": "Enter values (separate entries with semicolons)", +"DND4EUI.UnknownCondition.Label": "Unknown value", +"DND4EUI.UnknownCondition.Tip": "No match was found for this id in the current status conditions configuration.", "DND4EUI.Yes": "Yes", "SETTINGS.4eAutoCollapseCardL": "Automatically collapse Item Card descriptions in the Chat Log", "SETTINGS.4eAutoCollapseCardN": "Collapse Item Cards in Chat", diff --git a/lang/en.json b/lang/en.json index 7370845d..beb35fe9 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1060,6 +1060,7 @@ "DND4EBETA.SpokenPrimordial": "Primordial", "DND4EBETA.SpokenSupernal": "Supernal", "DND4EBETA.Stance": "Stance", +"DND4EBETA.StatusConditions": "Status Conditions", "DND4EBETA.SubclassName": "Subclass Name", "DND4EBETA.SubName": "Class Name Attack 1", "DND4EBETA.Summoning": "Summoning", @@ -1078,6 +1079,8 @@ "DND4EBETA.Sustain": "Sustain", "DND4EBETA.Target": "Target", "DND4EBETA.TargetAll": "Affect All Targets", +"DND4EBETA.TargetAllies": "Affect All Allies", +"DND4EBETA.TargetEnemies": "Affect All Enemies", "DND4EBETA.TargetHit": "Affect Hit Targets", "DND4EBETA.TargetMiss": "Affect Miss Targets", "DND4EBETA.TargetSelf": "Affect Self", @@ -1419,6 +1422,7 @@ "EFFECTDESC.hidden": "The creature is hidden or disguised.", "EFFECTDESC.sneaking": "The creature is being stealthy. It is hidden (invisible and silent) from creatures that fail to detect it.", "EFFECTDESC.torch": "The creature is holding a light source.", +"ERROR.4eCopyStatusDetails": "Could not copy status details. This is expected if you have changed the standard list of status conditions since adding one to this effect.", "DND4EUI.AttributeKey": "Attribute Key", "DND4EUI.AddNew": "Add New", "DND4EUI.Data Type": "Data Type", @@ -1434,6 +1438,8 @@ "DND4EUI.ShowImage": "Show image", "DND4EUI.SortBy": "Sort By", "DND4EUI.StringEnterValues": "Enter values (separate entries with semicolons)", +"DND4EUI.Unknown": "Unknown value", +"DND4EUI.UnknownCondition.Tip": "No match was found for this id in the current status conditions configuration.", "DND4EUI.Yes": "Yes", "SETTINGS.4eAutoCollapseCardL": "Automatically collapse Item Card descriptions in the Chat Log", "SETTINGS.4eAutoCollapseCardN": "Collapse Item Cards in Chat", @@ -1496,6 +1502,9 @@ "MIGRATION.4eBegin": "Applying DnD4E System Migration for version {version}. Please be patient and do not close your game or shut down your server.", "MIGRATION.4eComplete": "DnD4E System Migration to version {version} completed!", "MIGRATION.4eVersionTooOldWarning": "Your DnD4e system data is from too old a Foundry version and cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.", +"SHEET.Character.Basic": "Basic Character Sheet", +"SHEET.NPC": "NPC Sheet", +"SHEET.Item": "Item Sheet", "TYPES.Actor.NPC": "NPC", "TYPES.Actor.Player Character": "Player Character", "TYPES.Item.backpack": "Container", From 4919e44129747ed414e59f610f649c45ea525dc2 Mon Sep 17 00:00:00 2001 From: FoxLee Date: Thu, 19 Oct 2023 21:14:21 +1100 Subject: [PATCH 04/10] Minor tweaks for active effects form --- styles/dnd4eBeta-DarkMode.css | 5 +++++ styles/dnd4eBeta.css | 32 +++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/styles/dnd4eBeta-DarkMode.css b/styles/dnd4eBeta-DarkMode.css index 5c25103e..8bf588df 100644 --- a/styles/dnd4eBeta-DarkMode.css +++ b/styles/dnd4eBeta-DarkMode.css @@ -176,6 +176,11 @@ img[src$="svg"].rollable:hover{ .dnd4eBeta.sheet .npc .sheet-header .automath img{ filter:unset; } +.active-effect-sheet .effect-dot:not(:last-child), +.active-effect-sheet .effect-status:not(:last-child), +.active-effect-sheet .effect-change:not(:last-child){ + border-bottom:1px solid rgba(255,255,255,0.1); +} /** CHAT **/ diff --git a/styles/dnd4eBeta.css b/styles/dnd4eBeta.css index 35fc653b..4a4440f0 100644 --- a/styles/dnd4eBeta.css +++ b/styles/dnd4eBeta.css @@ -413,7 +413,7 @@ textarea{ margin-left:15px; border-right:var(--border-heavy); height:100%; - /* overflow-y:auto; */ + overflow-y:auto; } .dnd4eBeta .section--abilities{ margin-right:15px; @@ -464,8 +464,6 @@ textarea{ .dnd4eBeta .section--skills{ margin-right:15px; margin-top:10px; - overflow-y: auto; - height:calc(100% - 195px); } .dnd4eBeta .section--skills .skill--block{ min-height:27px; @@ -2184,18 +2182,38 @@ font-size:12px; .active-effect-sheet .effects-header>:first-child{ padding-left:4px; } -.active-effect-sheet .dots-list{ +.active-effect-sheet .dots-list, +.active-effect-sheet .statuses-list{ list-style:none; margin:0; padding:0; margin-bottom:2em; } -.active-effect-sheet .effect-dot{ +.active-effect-sheet .effect-dot, +.active-effect-sheet .effect-status{ align-items:center; padding:3px 1px; } -.active-effect-sheet .effect-dot:not(:last-child){ - border-bottom:1px solid var(--color-border-light-primary); +.active-effect-sheet .effect-dot:not(:last-child), +.active-effect-sheet .effect-status:not(:last-child), +.active-effect-sheet .effect-change:not(:last-child){ + border-bottom:1px solid rgba(0,0,0,0.1); +} +.active-effect-sheet .effect-status .status-select{ + flex-basis:8.5em; + margin-right:4px; +} +.active-effect-sheet .effect-status .status-controls{ + flex-basis:19.5em; +} +.active-effect-sheet .effect-status .status-select select{ + width:100%; +} +.active-effect-sheet .effect-status button{ + border-radius:3px; + font-size:80%; + line-height:1; + padding:0.5em; } From 3e13b8a453e498d412241f183b555b54e36462b5 Mon Sep 17 00:00:00 2001 From: FoxLee Date: Thu, 19 Oct 2023 21:18:37 +1100 Subject: [PATCH 05/10] New strings & fixes - New strings for effect config updates - Restored some strings that were removed from the en.json language file. (Sorry, I had a weird input bug going on last week and I missed a few of the problems it caused!) --- lang/en-au.json | 5 ++++- lang/en.json | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lang/en-au.json b/lang/en-au.json index d8b69d18..41fe8bb1 100644 --- a/lang/en-au.json +++ b/lang/en-au.json @@ -199,6 +199,7 @@ "DND4EBETA.ComponentMaterial": "Material", "DND4EBETA.ComponentSomatic": "Somatic", "DND4EBETA.ComponentVerbal": "Verbal", +"DND4EBETA.Condition": "Condition", "DND4EBETA.ConBlinded": "Blinded", "DND4EBETA.Concentration": "Concentration", "DND4EBETA.ConCharmed": "Charmed", @@ -1423,12 +1424,14 @@ "EFFECTDESC.sneaking": "The creature is being stealthy. It is hidden (invisible and silent) from creatures that fail to detect it.", "EFFECTDESC.torch": "The creature is holding a light source.", "ERROR.4eCopyStatusDetails": "Could not copy status details. This is expected if you have changed the standard list of status conditions since adding one to this effect.", -"DND4EUI.AttributeKey": "Attribute Key", "DND4EUI.AddNew": "Add New", +"DND4EUI.All": "All", +"DND4EUI.AttributeKey": "Attribute Key", "DND4EUI.Data Type": "Data Type", "DND4EUI.Delete": "Delete", "DND4EUI.GroupBy": "Group By", "DND4EUI.HowSelectMultiple": "Hold ctrl/cmd to select multiple", +"DND4EUI.Icon": "Icon", "DND4EUI.Import": "Import", "DND4EUI.ImportJSONInput": "Input JSON to import in the field below", "DND4EUI.ImportJSONUpload": "Upload JSON", diff --git a/lang/en.json b/lang/en.json index beb35fe9..ae41df0b 100644 --- a/lang/en.json +++ b/lang/en.json @@ -199,6 +199,7 @@ "DND4EBETA.ComponentMaterial": "Material", "DND4EBETA.ComponentSomatic": "Somatic", "DND4EBETA.ComponentVerbal": "Verbal", +"DND4EBETA.Condition": "Condition", "DND4EBETA.ConBlinded": "Blinded", "DND4EBETA.Concentration": "Concentration", "DND4EBETA.ConCharmed": "Charmed", @@ -1423,12 +1424,14 @@ "EFFECTDESC.sneaking": "The creature is being stealthy. It is hidden (invisible and silent) from creatures that fail to detect it.", "EFFECTDESC.torch": "The creature is holding a light source.", "ERROR.4eCopyStatusDetails": "Could not copy status details. This is expected if you have changed the standard list of status conditions since adding one to this effect.", -"DND4EUI.AttributeKey": "Attribute Key", "DND4EUI.AddNew": "Add New", +"DND4EUI.All": "All", +"DND4EUI.AttributeKey": "Attribute Key", "DND4EUI.Data Type": "Data Type", "DND4EUI.Delete": "Delete", "DND4EUI.GroupBy": "Group By", "DND4EUI.HowSelectMultiple": "Hold ctrl/cmd to select multiple", +"DND4EUI.Icon": "Icon", "DND4EUI.Import": "Import", "DND4EUI.ImportJSONInput": "Input JSON to import in the field below", "DND4EUI.ImportJSONUpload": "Upload JSON", From 5062575beade46026fa59d1b409b0e5775607ded Mon Sep 17 00:00:00 2001 From: FoxLee Date: Thu, 19 Oct 2023 21:19:27 +1100 Subject: [PATCH 06/10] Added new subsection for statuses --- templates/sheets/active-effect-config.html | 42 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/templates/sheets/active-effect-config.html b/templates/sheets/active-effect-config.html index ee786dac..be620d95 100644 --- a/templates/sheets/active-effect-config.html +++ b/templates/sheets/active-effect-config.html @@ -174,11 +174,50 @@

{{ localize "DND4EBETA.ModifierPlural" }}

{{/each}} +
+ + +

{{localize 'DND4EBETA.StatusConditions'}}

+
+
Status
+
Replace Effect Details
+
+ +
+
+
    + {{#each data.statuses as |status i|}} +
  1. +
    + +
    + + + +
    + +
    +
  2. + {{/each}} +
+

{{ localize "DND4EBETA.OngoingDamage" }} / {{localize "EFFECT.statusRegen"}}

-
+
{{ localize "DND4EBETA.Value" }}
{{ localize "DND4EBETA.Type" }} ({{ localize "DND4EUI.HowSelectMultiple" }})
@@ -216,7 +255,6 @@

{{ localize "DND4EBETA.OngoingDamage" }} / {{localize "EF -

From 4b7cd4a2bb88c6e0aa6cbbab8d15cb086deb8004 Mon Sep 17 00:00:00 2001 From: FoxLee Date: Fri, 20 Oct 2023 11:26:28 +1100 Subject: [PATCH 07/10] Made status select re-render form on update Prevents an issue I noticed where the copy buttons can fail if the form hasn't re-rendered since you picked the status effect. --- templates/sheets/active-effect-config.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/sheets/active-effect-config.html b/templates/sheets/active-effect-config.html index be620d95..8fc659b9 100644 --- a/templates/sheets/active-effect-config.html +++ b/templates/sheets/active-effect-config.html @@ -189,7 +189,7 @@

{{localize 'DND4EBETA.StatusConditions'}}

{{#each data.statuses as |status i|}}
  • -