diff --git a/DrD2LinkedTenacity/0.1.2/DrD2LinkedTenacity.js b/DrD2LinkedTenacity/0.1.2/DrD2LinkedTenacity.js new file mode 100644 index 0000000000..a6a0746e20 --- /dev/null +++ b/DrD2LinkedTenacity/0.1.2/DrD2LinkedTenacity.js @@ -0,0 +1,105 @@ +// Github: TBD +// By: nesuprachy +// Contact: https://app.roll20.net/users/11071738/nesuprachy +// +// This script tracks changes made to the `tenacity_current` attribute and propagates them to other sheets linked via the `npc_owner` attribute. +// Uses ChatSetAttr mod to change attributes from chat https://github.com/Roll20/roll20-api-scripts/tree/master/ChatSetAttr#readme + +var DrD2LinkedTenacity = DrD2LinkedTenacity || (function() { + 'use strict'; + + const version = '0.1.2'; + const lastUpdate = 1739435813976; + // Global list of unarchived characters + // Collect or refresh unarchived character list to avoid calling findObjs on every attribute change + let allCharacters = []; + const collectCharacters = () => { + allCharacters = findObjs({ + _type: 'character', + archived: false + }, {caseInsensitive: true}); + }; + + // Gather all character IDs linked via 'npc_owner' + // If change came from PC sheet, add NPCs whose 'npc_owner' attribute is same as originCharName + // If change came from NPC sheet, add other NPCs with the same 'npc_owner' plus any PC whose name matches 'npc_owner' + const gatherLinkedCharacters = (sheetType, originCharName, npcOwner) => { + const targets = []; + allCharacters.forEach( char => { + const charName = char.get('name'); + const charNpcOwner = getAttrByName(char.id, 'npc_owner'); + + if(sheetType === 'pc' && originCharName) { + if(charNpcOwner === originCharName) targets.push(char.id); + }else if(sheetType === 'npc' && npcOwner) { + if(charNpcOwner === npcOwner || charName === npcOwner) targets.push(char.id); + } + }); + return targets; + }, + + checkInstall = function () { + log(`-=> DrD2LinkedTenacity v${version} <=- [${new Date(lastUpdate)}]`); + }, + + handleTenacityAttribute = function (obj, prev, isMax) { + //const startTime = Date.now(); + + // Parse old and new values + const prevVal = parseInt(isMax ? prev.max : prev.current) || 0; + const newVal = parseInt(isMax ? obj.get('max') : obj.get('current')) || 0; + if(newVal === prevVal) return; + + // Retrieve origin character data + const originCharId = obj.get('_characterid'); + const originChar = getObj('character', originCharId); + const sheetType = getAttrByName(originCharId, 'sheet_type'); + const originCharName = originChar.get('name'); + const npcOwner = getAttrByName(originCharId, 'npc_owner'); + const targetChars = gatherLinkedCharacters(sheetType, originCharName, npcOwner); + + /*log(`targetChars = ${targetChars}`); + log(`sheet_type = \'${sheetType}\', npc_owner = \'${npcOwner}\'`); + if(isMax) { + log(`\'${obj.get('name')}\' (max) of character \'${originCharName}\' changed from ${prevVal} to ${newVal}`); + }else { + log(`\'${obj.get('name')}\' of character \'${originCharName}\' changed from ${prevVal} to ${newVal}`); + }*/ + + if(Array.isArray(targetChars) && targetChars.length){ + const str = (isMax) ? `--tenacity_current||${newVal}` : `--tenacity_current|${newVal}`; + sendChat('API', `!setattr --charid ${targetChars.join(",")} ${str} --silent --nocreate`, null, {noarchive:true}); + //log(`!setattr --charid ${targetChars.join(",")} ${str} --silent --nocreate`); + } + + //const endTime = Date.now(); + //log(`handleAttribute() took ${endTime - startTime} ms to execute`); + }, + + registerEventHandlers = function () { + on('add:character', function (){collectCharacters()}); + on('change:character:archived', function (){collectCharacters()}); + + on('change:attribute:current', function(obj, prev){ + if(obj.get('name') === 'tenacity_current') handleTenacityAttribute(obj, prev, false); + }); + on('change:attribute:max', function(obj, prev){ + if(obj.get('name') === 'tenacity_current') handleTenacityAttribute(obj, prev, true); + }); + }; + + return { + CheckInstall: checkInstall, + CollectCharacters: collectCharacters, + RegisterEventHandlers: registerEventHandlers + }; + +}()); + +on('ready', () => { + 'use strict'; + + DrD2LinkedTenacity.CheckInstall(); + DrD2LinkedTenacity.CollectCharacters(); + DrD2LinkedTenacity.RegisterEventHandlers(); +}); \ No newline at end of file diff --git a/DrD2LinkedTenacity/DrD2LinkedTenacity.js b/DrD2LinkedTenacity/DrD2LinkedTenacity.js index d910fc07cc..a6a0746e20 100644 --- a/DrD2LinkedTenacity/DrD2LinkedTenacity.js +++ b/DrD2LinkedTenacity/DrD2LinkedTenacity.js @@ -8,77 +8,89 @@ var DrD2LinkedTenacity = DrD2LinkedTenacity || (function() { 'use strict'; - const version = '0.1.1'; - const lastUpdate = 1725266705016; - + const version = '0.1.2'; + const lastUpdate = 1739435813976; + // Global list of unarchived characters + // Collect or refresh unarchived character list to avoid calling findObjs on every attribute change + let allCharacters = []; + const collectCharacters = () => { + allCharacters = findObjs({ + _type: 'character', + archived: false + }, {caseInsensitive: true}); + }; + + // Gather all character IDs linked via 'npc_owner' + // If change came from PC sheet, add NPCs whose 'npc_owner' attribute is same as originCharName + // If change came from NPC sheet, add other NPCs with the same 'npc_owner' plus any PC whose name matches 'npc_owner' + const gatherLinkedCharacters = (sheetType, originCharName, npcOwner) => { + const targets = []; + allCharacters.forEach( char => { + const charName = char.get('name'); + const charNpcOwner = getAttrByName(char.id, 'npc_owner'); + + if(sheetType === 'pc' && originCharName) { + if(charNpcOwner === originCharName) targets.push(char.id); + }else if(sheetType === 'npc' && npcOwner) { + if(charNpcOwner === npcOwner || charName === npcOwner) targets.push(char.id); + } + }); + return targets; + }, + checkInstall = function () { - log(`-=> DrD2LinkedTenacity v${version} <=- [${new Date(lastUpdate)}]`); + log(`-=> DrD2LinkedTenacity v${version} <=- [${new Date(lastUpdate)}]`); }, handleTenacityAttribute = function (obj, prev, isMax) { - if(obj.get('name') === 'tenacity_current') { - var prevVal, newVal = 0; - if(isMax){ - prevVal = parseInt(prev.max)||0; - newVal = parseInt(obj.get('max'))||0; - }else { - prevVal = parseInt(prev.current)||0; - newVal = parseInt(obj.get('current'))||0; - } - if(newVal !== prevVal) { - var targetChars = []; - var allCharacters = findObjs({ - _type: 'character', - archived: false - }, {caseInsensitive: true}); - var originChar = getObj('character', obj.get('_characterid')); - var originCharName = originChar.get('name'); - var sheetType = getAttrByName(originChar.id, 'sheet_type'); - var npcOwner = getAttrByName(originChar.id, 'npc_owner'); - - if((sheetType === 'pc') && originCharName) { - // If PC, add ID of every character owned by this PC to target characters array - _.each(allCharacters, function(obj){ - if(getAttrByName(obj.id, 'npc_owner') === originCharName) {targetChars.push(obj.id)} - }); - }else if ((sheetType === 'npc') && npcOwner) { - // If NPC, add ID of every character with same 'npc_owner' to target characters array - _.each(allCharacters, function(obj) { - if(getAttrByName(obj.id, 'npc_owner') === npcOwner) {targetChars.push(obj.id)} - }); - // Add sheets where 'character_name' equals 'npc_owner' to target characters array - _.each(allCharacters, function(obj) { - if(obj.get('name') === npcOwner) {targetChars.push(obj.id)} - }); - } + //const startTime = Date.now(); - /*log(`sheet_type = \'${sheetType}\', npc_owner = \'${npcOwner}\'`); - if(isMax) { - log(`\'${obj.get('name')}\' (max) of character \'${originCharName}\' changed from ${prevVal} to ${newVal}`); - }else { - log(`\'${obj.get('name')}\' of character \'${originCharName}\' changed from ${prevVal} to ${newVal}`); - }*/ + // Parse old and new values + const prevVal = parseInt(isMax ? prev.max : prev.current) || 0; + const newVal = parseInt(isMax ? obj.get('max') : obj.get('current')) || 0; + if(newVal === prevVal) return; - if(Array.isArray(targetChars) && targetChars.length){ - if(isMax){ - //log(`!setattr --charid ${targetChars} --tenacity_current||${newVal} --silent --nocreate`); - sendChat('API', `!setattr --charid ${targetChars} --tenacity_current||${newVal} --silent --nocreate`, null, {noarchive:true} ); - }else { - //log(`!setattr --charid ${targetChars} --tenacity_current|${newVal} --silent --nocreate`); - sendChat('API', `!setattr --charid ${targetChars} --tenacity_current|${newVal} --silent --nocreate`, null, {noarchive:true} ); - } - } - } + // Retrieve origin character data + const originCharId = obj.get('_characterid'); + const originChar = getObj('character', originCharId); + const sheetType = getAttrByName(originCharId, 'sheet_type'); + const originCharName = originChar.get('name'); + const npcOwner = getAttrByName(originCharId, 'npc_owner'); + const targetChars = gatherLinkedCharacters(sheetType, originCharName, npcOwner); + + /*log(`targetChars = ${targetChars}`); + log(`sheet_type = \'${sheetType}\', npc_owner = \'${npcOwner}\'`); + if(isMax) { + log(`\'${obj.get('name')}\' (max) of character \'${originCharName}\' changed from ${prevVal} to ${newVal}`); + }else { + log(`\'${obj.get('name')}\' of character \'${originCharName}\' changed from ${prevVal} to ${newVal}`); + }*/ + + if(Array.isArray(targetChars) && targetChars.length){ + const str = (isMax) ? `--tenacity_current||${newVal}` : `--tenacity_current|${newVal}`; + sendChat('API', `!setattr --charid ${targetChars.join(",")} ${str} --silent --nocreate`, null, {noarchive:true}); + //log(`!setattr --charid ${targetChars.join(",")} ${str} --silent --nocreate`); } + + //const endTime = Date.now(); + //log(`handleAttribute() took ${endTime - startTime} ms to execute`); }, registerEventHandlers = function () { - on('change:attribute:current', function(obj, prev){handleTenacityAttribute(obj, prev, false)}); - on('change:attribute:max', function(obj, prev){handleTenacityAttribute(obj, prev, true)}); + on('add:character', function (){collectCharacters()}); + on('change:character:archived', function (){collectCharacters()}); + + on('change:attribute:current', function(obj, prev){ + if(obj.get('name') === 'tenacity_current') handleTenacityAttribute(obj, prev, false); + }); + on('change:attribute:max', function(obj, prev){ + if(obj.get('name') === 'tenacity_current') handleTenacityAttribute(obj, prev, true); + }); }; return { CheckInstall: checkInstall, + CollectCharacters: collectCharacters, RegisterEventHandlers: registerEventHandlers }; @@ -88,5 +100,6 @@ on('ready', () => { 'use strict'; DrD2LinkedTenacity.CheckInstall(); + DrD2LinkedTenacity.CollectCharacters(); DrD2LinkedTenacity.RegisterEventHandlers(); }); \ No newline at end of file diff --git a/DrD2LinkedTenacity/script.json b/DrD2LinkedTenacity/script.json index ff23b2c11c..8a2ba408ed 100644 --- a/DrD2LinkedTenacity/script.json +++ b/DrD2LinkedTenacity/script.json @@ -1,9 +1,9 @@ { "name": "DrD2LinkedTenacity", "script": "DrD2LinkedTenacity.js", - "version": "0.1.1", - "previousversions": [], - "description": "Designed for use only with the Draci Doupe II sheet.\n\nThis script tracks changes made to the `tenacity_current` attribute and propagates them to other sheets linked via the `npc_owner` attribute.", + "version": "0.1.2", + "previousversions": ["0.1.0", "0.1.1"], + "description": "Designed for use only with the Draci Doupe II sheet.\n\nThis script tracks changes made to the `tenacity_current` attribute and propagates them to other sheets linked via the 'npc_owner' attribute.", "authors": "nesuprachy", "roll20userid": "11071738", "useroptions": [],