diff --git a/Condefinition/1.0.0/Condefinition.js b/Condefinition/1.0.0/Condefinition.js new file mode 100644 index 0000000000..8572848b8b --- /dev/null +++ b/Condefinition/1.0.0/Condefinition.js @@ -0,0 +1,1348 @@ +var API_Meta = API_Meta || +{}; //eslint-disable-line no-var +API_Meta.Condefinition = { + offset: Number.MAX_SAFE_INTEGER, + lineCount: -1 +}; +{ + try + { + throw new Error(''); + } + catch (e) + { + API_Meta.Condefinition.offset = (parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/, '$1'), 10) - (7)); + } +} + +/* globals libTokenMarkers, TokenMod, SmartAoE */ + + + +on('ready', () => +{ + + // Make sure libTokenMarkers exists, has the functions that are expected, and has a constant for testing + if('undefined' === typeof libTokenMarkers || + (['getStatus', 'getStatuses', 'getOrderedList'].find(k => + !libTokenMarkers.hasOwnProperty(k) || 'function' !== typeof libTokenMarkers[k] + )) + ) + { + // notify of the missing library + sendChat('', `/w gm
Lvl | Effect |
---|---|
1 | Disadvantage on Ability Checks |
2 | Speed halved |
3 | Disadvantage on attack rolls and Saving Throws |
4 | Hit point maximum halved |
5 | Speed reduced to 0 |
6 | Death |
Condefinition reads incoming roll templates and looks for imposed conditions and saving throws. It display a button(s) in chat that allow you to quickly apply a corresponding token marker, define the condition, track concentration, roll saving throws and/or apply damage from spells or effects that are triggered by saving throws.
Currently, there are some limitations:
Every time a condition is implied by something in a chat roll template, or if the report function is used on a selected token, the script produces a button. Each button has three parts.
The middle, largest part of the button will send a message tot he GM in chat, that defines the condition. Sometimes, this will have links to other conditions that the condition imposes. Such as an Unconscious character is also Incapacitated.
The left side of the button has a word balloon graphic. This also send the definition to chat, but in this case, it is visible to all players.
The right hand side of the button shows the marker associated with that condition. It will toggle the marker for any and all selected tokens.
Condefinition can produce a reference palette of all official condition buttons. Type !condef-all
to display the report. Note that the script tracks more markers than this, such as \"Raging\", or \"Marked\". These are just the official conditions defined by the rules.
Condefintion will monitor if the token taking damage has a defined concentration marker on it. If you reduce the hit point bar of a concentrating token, the script will prompt you to make the appropriate Constitution save.
Example. \"Morrigan\" just took 30 hp of damage while concentrating on a spell. The Script notes that the concentration marker was active, calculates the saving throw and produces a button that will cause the selected token to roll a saving throw. Note: In order to make this script work with both the 2014 and 2024 sheets, the Group Check mod is required and configured for your sheet(s). If you installed Condefinition via Roll20 One Click, it should have installed all helper scripts automatically. See the first section of this handout for more details.
Condefintion also allows you to select any number of tokens and run !condef-report
. The script produces a set of report buttons for each selected token that has a defined condition marker on it. In this case, the sub-buttons that toggle token markers will only affect the respective token, and that token does not need to be selected.
This feature requires both Group Check, and the manually installed script Apply Damage in order to function. It also requires that the spell or feature of a creature is sent to chat with the Description turned on. In some cases this requires rolling damage or clicking the roll template button that shows the spell description. There are too many sheet variables to account for here, but in short, the keywords needed in order to trigger the script must have been sent to chat.
The button produced is very similar to the concentration check button. In this case, the left half will roll a saving throw for all selected tokens. If you have Apply Damage installed, it will also walk you through the steps necessary to resolve the roll and apply damage accordingly. Note that this function will work on a combination of selected 2014 and 2024 tokens.
Out of the box, this script will support conditions defined by both the 2014 and 2024 rule sets. It also can switch between the default token markers or my own Easy to Read token markers. This latter is entirely for my own convenience, and they are not required in any way for the script to function.
Type !condef help
or !condef config
in chat to call up the configuration controls.
Condefinition uses a lot of Regular Expressions (search code) to find and apply token markers and definitions. It reads these from a handout called Condefinitions. You can edit this handout manually. It has all of the code needed to define the conditions wrapped in a code block. You can edit the names, the definitions, the search code and the name of the corresponding token marker. The edited text must be clean plain text wrapped in code tags using the Code style button in the editor. Most users will only need to change the name of the token marker to match their chosen set.
If you don't feel comfortable editing the other code, or do so and make a mistake which breaks the handout, you can reset to the default using the buttons in the configuration panel. The script contains defaults for the 2014 and 2024 rule sets, using the default token markers provided by Roll20. I original wrote this script using my own Marketplace set, so if you have installed my \"Easy to Read Token Markers\" set, then there are also buttons that set defaults for those. This Marketplace set is honestly for my own convenience, and the script does not require this set in any way.
Each Condefintion definition is four lines long, and separated by an empty line.
After editing and saving the configuration document, you must press the \"Update Conditions\" button in the chat configuration controls. This will apply the conditions immediately.
It is strongly advised that if you do make home brew changes, you duplicate the Condefinitions Handout to use as a backup. the controls can \"reset to factory specs\", but they cannot undo breaking changes.
If you create a configuration that uses a marketplace set of Token Markers that you prefer, feel free to share with others. It can be copied and pasted between games, so long as you are careful to paste clean text only (hold down the shift key while pasting, or copy from a plain text editor), and wrap the whole thing in the \"Code\" style.
|<\/pre>$/g, "") // Striptags if present + .split(/\r?\n\r?\n/) // Split on double line breaks + .map(block => + { + const [key, regexStr, description, label] = block.trim() + .split(/\r?\n/); + const regexMatch = regexStr?.match(/^\/(.+)\/([gimuy]*)$/); + const regex = regexMatch ? new RegExp(regexMatch[1], regexMatch[2]) : /.^/; + + return [key, regex, description, label]; + }) + .filter(entry => entry.length === 4); // Prevents malformed rows + } + + + + function loadConditionsFromHandout() + { + const handout = findObjs( + { + _type: "handout", + name: "Condefinitions" + })[0]; + if(!handout) + { + log("⚠️ Handout 'Condefinitions' not found. Creating new Config handout."); + writeConditionsToHandoutMultilinePlainText(defaultConditionsArray); + loadConditionsFromHandout(); + createHandoutIfMissing("Condefinitions", helpHTML); + sendMessage(configMessage, "Condefinition Configuration"); + return; + } + + handout.get("notes", function(notes) + { + const parsed = deserializeConditionsMultiline(notes); + if(parsed.length) + { + conditionsArray = parsed; + } + else + { + log("⚠️ No valid conditions found in 'Condefinitions' handout."); + } + }); + } + + + + + function serializeConditionsArrayMultiline(array) + { + return array.map(([key, regex, description, label]) => + `${key}\n${regex.toString()}\n${description}\n${label}` + ) + .join("\n\n"); + } + + + + function writeConditionsToHandoutMultilinePlainText(conditionsArray) + { + const content = conditionsArray.map(([key, regex, description, label]) => + { + const regexString = regex.toString(); // /.../flags + // const icon = getConditionIcon(key); // optional helper for icon name + return `${key}\n${regex.toString()}\n${description}\n${label}`; + }) + .join("\n\n"); + + const escaped = content + .replace(/&/g, "&") + .replace(//g, ">"); + + const wrapped = `${escaped}`; + + let handout = findObjs( + { + _type: "handout", + name: "Condefinitions" + })[0]; + + if(!handout) + { + handout = createObj("handout", + { + name: "Condefinitions", + inplayerjournals: "all", + archived: false + }); + } + + handout.set( + { + notes: wrapped + }); + } + + + + function readAndParseConditions() + { + const handout = findObjs( + { + _type: "handout", + name: "Condefinitions" + })[0]; + if(!handout) + { + log("⚠️ Handout not found."); + return; + } + + handout.get("notes", function(notes) + { + + const stripped = notes.replace(/^/i, "") + .replace(/<\/pre>$/i, ""); + const decoded = stripped + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); + + + const parsed = deserializeConditionsMultiline(notes); + return parsed + }); + } + + + function mergeDescriptionsWithClassicDefinitions(originalArray, newConditions) + { + return originalArray.map(entry => + { + const [key, regex, , marker] = entry; + const match = newConditions.find(def => def[0] === key); + const newDescription = match ? match[1] : entry[2]; // fallback to original description if no match + return [key, regex, newDescription, marker]; + }); + } + + + + + function sendMessage(message, title) + { + let theTitle = (title ? `${title}` : ""); + let theMessage = (message ? `${theTitle}${message}` : ""); + + sendChat("Condefinition", `/w gm ${theMessage}`, null, + { + noarchive: true + }); + } + + + + + // writeConditionsToHandoutMultilinePlainText(conditionsArray); + + + const input = `blinded +/(be|and|is|magically|become|becomes|is either) blinded|blinded condition/i +Example description here +bleeding-eye +Blinded`; + + const result = deserializeConditionsMultiline(input); + + + + let buttons = ""; + let reportText = ""; + + function makeButton(conditionName, descriptionText, tokenMarker, tokenID) + { + let tmLabel = `L`; + //let markerName = tokenMarker; + + let markerName = ((tokenMarker.includes(";;")) ? tokenMarker.split(";;")[0] : tokenMarker); + if('undefined' !== typeof libTokenMarkers && undefined !== libTokenMarkers.getStatus(markerName) + .url) + { + let tmImage = libTokenMarkers.getStatus(markerName) + .url; + + if(undefined !== tmImage && tmImage.length > 0) + { + tmLabel = ``; + } + } + buttons = buttons + ``; + } + + + function makeGenericButton(name, link) + { + return ``; + + } + + + //Concentration Observer + function concentrationCheck(obj, prev) + { + if(obj.get("statusmarkers") + .includes(condefConcentrationMarker) || obj.get("statusmarkers") + .includes(ConcentrationMarker) || obj.get(condefConcentrationMarker)) + { + // if (obj.get(condefConcentrationMarker)) { + if(prev["bar1_value"] > obj.get("bar1_value")) + { + let final_conc_DC = 10; + let calc_conc_DC = (prev["bar1_value"] - obj.get("bar1_value")) / 2; + if(calc_conc_DC > final_conc_DC) + { + final_conc_DC = Math.floor(calc_conc_DC); + } + // let tokenName = obj.get("name"); + let theMessage = `! &{template:spell} {{name=Concentration}}{{savedc=${final_conc_DC}}} {{description=Make a DC ${final_conc_DC} Constitution saving throw}}{{Concentration=1}}`; + // let theMessage = `this should print`; + sendChat("gm", theMessage); + } + } + + } + + //Event Handlers + on("change:graphic:bar1_value", concentrationCheck); + if(typeof(TokenMod) === 'object') TokenMod.ObserveTokenChange(concentrationCheck); + if('undefined' !== typeof SmartAoE && SmartAoE.ObserveTokenChange) + { + SmartAoE.ObserveTokenChange(function(obj, prev) + { + concentrationCheck(obj, prev); + }); + } + + + //Condition report creator. Use !condef for GM only, pcondef for player report. Follow by condition names separated by spaces. !condef-all returns buttons for all conditions, for reference + on('chat:message', function(msg) + { + + + if(msg.type === "api" && msg.content === "!condef-generic2014") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray, definitions2014)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to Default token markers using the 2014 condition definitions"); + return; + } + + if(msg.type === "api" && msg.content === "!condef-generic2024") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray, definitions2024)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to Default token markers using the 2024 condition definitions"); + return; + } + + if(msg.type === "api" && msg.content === "!condef-easy2014") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray_EasyToRead, definitions2014)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to the Easy to Read token markers using the 2014 condition definitions"); + return; + } + + if(msg.type === "api" && msg.content === "!condef-easy2024") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray_EasyToRead, definitions2024)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to the Easy to Read token markers using the 2024 condition definitions"); + return; + } + + + if(msg.type === "api" && msg.content === "!condef-loadconditions") + { + loadConditionsFromHandout(); + ConcentrationMarker = (conditionsArray.find(([k]) => k.toLowerCase() === "concentrating") || [, , , "death-zone"])[3]; + condefConcentrationMarker = "status_" + ConcentrationMarker; + + sendMessage("Condition definitions reloaded from handout."); + return; + } + + if(msg.type === "api" && (msg.content === "!condef-config" || msg.content === "!condef-help")) + { + createHandoutIfMissing("Help: Condefinition", helpHTML, "", "https://s3.amazonaws.com/files.d20.io/images/442783616/hDkduTWDpcVEKomHnbb6AQ/med.png"); + + sendMessage(configMessage, "Condefinition Configuration"); + return; + } + + + + + if('api' === msg.type && msg.content.match(/^!(condef|pcondef|condef-all|condef-report|condef-toggle)/)) + { + let args = msg.content.split(" "); + let sender = msg.who; + if(msg.content === '!condef-all') + { + let message = `! &{template:noecho} {{description= blinded condition charmed condition deafened condition exhausted condition frightened condition grappled condition incapacitated condition invisible condition paralyzed condition petrified condition poisoned condition prone condition restrained condition stunned condition unconscious condition concentration check}}`; + sendChat("gm", message, null, + { + noarchive: true + }); + message = ""; + return; + } + + + let messagePrefix = ((msg.content.includes('pcondef')) ? '' : '/w ' + sender + ' '); + let theCommand = ((msg.content.includes("pcondef")) ? "!pcondef" : "!condef"); + + + //selected vs target handler + if(msg.content.includes('condef-toggle ') && msg.content.length > 15) + { + let tokenMarker = msg.content.split('condef-toggle ')[1]; + let message = ''; + if(msg.selected) + { + //message = `!token-mod --set statusmarkers|!${tokenMarker}`; + let ids = msg.selected.reduce((m, o) => [...m, o._id], []); + message = `!token-mod --api-as ${msg.playerid} --set statusmarkers|!${tokenMarker} --ids ${ids.join(' ')}`; + //message = `!token-mod --api-as ${msg.playerid} --set statusmarkers|!${tokenMarker}`; + + } + else + { + + message = messagePrefix + buttonboxUnshifted + 'No token is selected.
Click here to pick a target.
Lvl | Effect |
---|---|
1 | Disadvantage on Ability Checks |
2 | Speed halved |
3 | Disadvantage on attack rolls and Saving Throws |
4 | Hit point maximum halved |
5 | Speed reduced to 0 |
6 | Death |
Condefinition reads incoming roll templates and looks for imposed conditions and saving throws. It display a button(s) in chat that allow you to quickly apply a corresponding token marker, define the condition, track concentration, roll saving throws and/or apply damage from spells or effects that are triggered by saving throws.
Currently, there are some limitations:
Every time a condition is implied by something in a chat roll template, or if the report function is used on a selected token, the script produces a button. Each button has three parts.
The middle, largest part of the button will send a message tot he GM in chat, that defines the condition. Sometimes, this will have links to other conditions that the condition imposes. Such as an Unconscious character is also Incapacitated.
The left side of the button has a word balloon graphic. This also send the definition to chat, but in this case, it is visible to all players.
The right hand side of the button shows the marker associated with that condition. It will toggle the marker for any and all selected tokens.
Condefinition can produce a reference palette of all official condition buttons. Type !condef-all
to display the report. Note that the script tracks more markers than this, such as \"Raging\", or \"Marked\". These are just the official conditions defined by the rules.
Condefintion will monitor if the token taking damage has a defined concentration marker on it. If you reduce the hit point bar of a concentrating token, the script will prompt you to make the appropriate Constitution save.
Example. \"Morrigan\" just took 30 hp of damage while concentrating on a spell. The Script notes that the concentration marker was active, calculates the saving throw and produces a button that will cause the selected token to roll a saving throw. Note: In order to make this script work with both the 2014 and 2024 sheets, the Group Check mod is required and configured for your sheet(s). If you installed Condefinition via Roll20 One Click, it should have installed all helper scripts automatically. See the first section of this handout for more details.
Condefintion also allows you to select any number of tokens and run !condef-report
. The script produces a set of report buttons for each selected token that has a defined condition marker on it. In this case, the sub-buttons that toggle token markers will only affect the respective token, and that token does not need to be selected.
This feature requires both Group Check, and the manually installed script Apply Damage in order to function. It also requires that the spell or feature of a creature is sent to chat with the Description turned on. In some cases this requires rolling damage or clicking the roll template button that shows the spell description. There are too many sheet variables to account for here, but in short, the keywords needed in order to trigger the script must have been sent to chat.
The button produced is very similar to the concentration check button. In this case, the left half will roll a saving throw for all selected tokens. If you have Apply Damage installed, it will also walk you through the steps necessary to resolve the roll and apply damage accordingly. Note that this function will work on a combination of selected 2014 and 2024 tokens.
Out of the box, this script will support conditions defined by both the 2014 and 2024 rule sets. It also can switch between the default token markers or my own Easy to Read token markers. This latter is entirely for my own convenience, and they are not required in any way for the script to function.
Type !condef help
or !condef config
in chat to call up the configuration controls.
Condefinition uses a lot of Regular Expressions (search code) to find and apply token markers and definitions. It reads these from a handout called Condefinitions. You can edit this handout manually. It has all of the code needed to define the conditions wrapped in a code block. You can edit the names, the definitions, the search code and the name of the corresponding token marker. The edited text must be clean plain text wrapped in code tags using the Code style button in the editor. Most users will only need to change the name of the token marker to match their chosen set.
If you don't feel comfortable editing the other code, or do so and make a mistake which breaks the handout, you can reset to the default using the buttons in the configuration panel. The script contains defaults for the 2014 and 2024 rule sets, using the default token markers provided by Roll20. I original wrote this script using my own Marketplace set, so if you have installed my \"Easy to Read Token Markers\" set, then there are also buttons that set defaults for those. This Marketplace set is honestly for my own convenience, and the script does not require this set in any way.
Each Condefintion definition is four lines long, and separated by an empty line.
After editing and saving the configuration document, you must press the \"Update Conditions\" button in the chat configuration controls. This will apply the conditions immediately.
It is strongly advised that if you do make home brew changes, you duplicate the Condefinitions Handout to use as a backup. the controls can \"reset to factory specs\", but they cannot undo breaking changes.
If you create a configuration that uses a marketplace set of Token Markers that you prefer, feel free to share with others. It can be copied and pasted between games, so long as you are careful to paste clean text only (hold down the shift key while pasting, or copy from a plain text editor), and wrap the whole thing in the \"Code\" style.
|<\/pre>$/g, "") // Striptags if present + .split(/\r?\n\r?\n/) // Split on double line breaks + .map(block => + { + const [key, regexStr, description, label] = block.trim() + .split(/\r?\n/); + const regexMatch = regexStr?.match(/^\/(.+)\/([gimuy]*)$/); + const regex = regexMatch ? new RegExp(regexMatch[1], regexMatch[2]) : /.^/; + + return [key, regex, description, label]; + }) + .filter(entry => entry.length === 4); // Prevents malformed rows + } + + + + function loadConditionsFromHandout() + { + const handout = findObjs( + { + _type: "handout", + name: "Condefinitions" + })[0]; + if(!handout) + { + log("⚠️ Handout 'Condefinitions' not found. Creating new Config handout."); + writeConditionsToHandoutMultilinePlainText(defaultConditionsArray); + loadConditionsFromHandout(); + createHandoutIfMissing("Condefinitions", helpHTML); + sendMessage(configMessage, "Condefinition Configuration"); + return; + } + + handout.get("notes", function(notes) + { + const parsed = deserializeConditionsMultiline(notes); + if(parsed.length) + { + conditionsArray = parsed; + } + else + { + log("⚠️ No valid conditions found in 'Condefinitions' handout."); + } + }); + } + + + + + function serializeConditionsArrayMultiline(array) + { + return array.map(([key, regex, description, label]) => + `${key}\n${regex.toString()}\n${description}\n${label}` + ) + .join("\n\n"); + } + + + + function writeConditionsToHandoutMultilinePlainText(conditionsArray) + { + const content = conditionsArray.map(([key, regex, description, label]) => + { + const regexString = regex.toString(); // /.../flags + // const icon = getConditionIcon(key); // optional helper for icon name + return `${key}\n${regex.toString()}\n${description}\n${label}`; + }) + .join("\n\n"); + + const escaped = content + .replace(/&/g, "&") + .replace(//g, ">"); + + const wrapped = `${escaped}`; + + let handout = findObjs( + { + _type: "handout", + name: "Condefinitions" + })[0]; + + if(!handout) + { + handout = createObj("handout", + { + name: "Condefinitions", + inplayerjournals: "all", + archived: false + }); + } + + handout.set( + { + notes: wrapped + }); + } + + + + function readAndParseConditions() + { + const handout = findObjs( + { + _type: "handout", + name: "Condefinitions" + })[0]; + if(!handout) + { + log("⚠️ Handout not found."); + return; + } + + handout.get("notes", function(notes) + { + + const stripped = notes.replace(/^/i, "") + .replace(/<\/pre>$/i, ""); + const decoded = stripped + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); + + + const parsed = deserializeConditionsMultiline(notes); + return parsed + }); + } + + + function mergeDescriptionsWithClassicDefinitions(originalArray, newConditions) + { + return originalArray.map(entry => + { + const [key, regex, , marker] = entry; + const match = newConditions.find(def => def[0] === key); + const newDescription = match ? match[1] : entry[2]; // fallback to original description if no match + return [key, regex, newDescription, marker]; + }); + } + + + + + function sendMessage(message, title) + { + let theTitle = (title ? `${title}` : ""); + let theMessage = (message ? `${theTitle}${message}` : ""); + + sendChat("Condefinition", `/w gm ${theMessage}`, null, + { + noarchive: true + }); + } + + + + + // writeConditionsToHandoutMultilinePlainText(conditionsArray); + + + const input = `blinded +/(be|and|is|magically|become|becomes|is either) blinded|blinded condition/i +Example description here +bleeding-eye +Blinded`; + + const result = deserializeConditionsMultiline(input); + + + + let buttons = ""; + let reportText = ""; + + function makeButton(conditionName, descriptionText, tokenMarker, tokenID) + { + let tmLabel = `L`; + //let markerName = tokenMarker; + + let markerName = ((tokenMarker.includes(";;")) ? tokenMarker.split(";;")[0] : tokenMarker); + if('undefined' !== typeof libTokenMarkers && undefined !== libTokenMarkers.getStatus(markerName) + .url) + { + let tmImage = libTokenMarkers.getStatus(markerName) + .url; + + if(undefined !== tmImage && tmImage.length > 0) + { + tmLabel = ``; + } + } + buttons = buttons + ``; + } + + + function makeGenericButton(name, link) + { + return ``; + + } + + + //Concentration Observer + function concentrationCheck(obj, prev) + { + if(obj.get("statusmarkers") + .includes(condefConcentrationMarker) || obj.get("statusmarkers") + .includes(ConcentrationMarker) || obj.get(condefConcentrationMarker)) + { + // if (obj.get(condefConcentrationMarker)) { + if(prev["bar1_value"] > obj.get("bar1_value")) + { + let final_conc_DC = 10; + let calc_conc_DC = (prev["bar1_value"] - obj.get("bar1_value")) / 2; + if(calc_conc_DC > final_conc_DC) + { + final_conc_DC = Math.floor(calc_conc_DC); + } + // let tokenName = obj.get("name"); + let theMessage = `! &{template:spell} {{name=Concentration}}{{savedc=${final_conc_DC}}} {{description=Make a DC ${final_conc_DC} Constitution saving throw}}{{Concentration=1}}`; + // let theMessage = `this should print`; + sendChat("gm", theMessage); + } + } + + } + + //Event Handlers + on("change:graphic:bar1_value", concentrationCheck); + if(typeof(TokenMod) === 'object') TokenMod.ObserveTokenChange(concentrationCheck); + if('undefined' !== typeof SmartAoE && SmartAoE.ObserveTokenChange) + { + SmartAoE.ObserveTokenChange(function(obj, prev) + { + concentrationCheck(obj, prev); + }); + } + + + //Condition report creator. Use !condef for GM only, pcondef for player report. Follow by condition names separated by spaces. !condef-all returns buttons for all conditions, for reference + on('chat:message', function(msg) + { + + + if(msg.type === "api" && msg.content === "!condef-generic2014") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray, definitions2014)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to Default token markers using the 2014 condition definitions"); + return; + } + + if(msg.type === "api" && msg.content === "!condef-generic2024") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray, definitions2024)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to Default token markers using the 2024 condition definitions"); + return; + } + + if(msg.type === "api" && msg.content === "!condef-easy2014") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray_EasyToRead, definitions2014)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to the Easy to Read token markers using the 2014 condition definitions"); + return; + } + + if(msg.type === "api" && msg.content === "!condef-easy2024") + { + writeConditionsToHandoutMultilinePlainText(mergeDescriptionsWithClassicDefinitions(defaultConditionsArray_EasyToRead, definitions2024)); + loadConditionsFromHandout(); + sendMessage("Condefinitions handout has been reset to the Easy to Read token markers using the 2024 condition definitions"); + return; + } + + + if(msg.type === "api" && msg.content === "!condef-loadconditions") + { + loadConditionsFromHandout(); + ConcentrationMarker = (conditionsArray.find(([k]) => k.toLowerCase() === "concentrating") || [, , , "death-zone"])[3]; + condefConcentrationMarker = "status_" + ConcentrationMarker; + + sendMessage("Condition definitions reloaded from handout."); + return; + } + + if(msg.type === "api" && (msg.content === "!condef-config" || msg.content === "!condef-help")) + { + createHandoutIfMissing("Help: Condefinition", helpHTML, "", "https://s3.amazonaws.com/files.d20.io/images/442783616/hDkduTWDpcVEKomHnbb6AQ/med.png"); + + sendMessage(configMessage, "Condefinition Configuration"); + return; + } + + + + + if('api' === msg.type && msg.content.match(/^!(condef|pcondef|condef-all|condef-report|condef-toggle)/)) + { + let args = msg.content.split(" "); + let sender = msg.who; + if(msg.content === '!condef-all') + { + let message = `! &{template:noecho} {{description= blinded condition charmed condition deafened condition exhausted condition frightened condition grappled condition incapacitated condition invisible condition paralyzed condition petrified condition poisoned condition prone condition restrained condition stunned condition unconscious condition concentration check}}`; + sendChat("gm", message, null, + { + noarchive: true + }); + message = ""; + return; + } + + + let messagePrefix = ((msg.content.includes('pcondef')) ? '' : '/w ' + sender + ' '); + let theCommand = ((msg.content.includes("pcondef")) ? "!pcondef" : "!condef"); + + + //selected vs target handler + if(msg.content.includes('condef-toggle ') && msg.content.length > 15) + { + let tokenMarker = msg.content.split('condef-toggle ')[1]; + let message = ''; + if(msg.selected) + { + //message = `!token-mod --set statusmarkers|!${tokenMarker}`; + let ids = msg.selected.reduce((m, o) => [...m, o._id], []); + message = `!token-mod --api-as ${msg.playerid} --set statusmarkers|!${tokenMarker} --ids ${ids.join(' ')}`; + //message = `!token-mod --api-as ${msg.playerid} --set statusmarkers|!${tokenMarker}`; + + } + else + { + + message = messagePrefix + buttonboxUnshifted + 'No token is selected.
Click here to pick a target.