Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×

[mod-related] Have rowId: How Do I access other row attrs to modify them?

1780369762

Edited 1780369923
So from the script behind these two chat boxes where I click on the +1 magic arrows for this character's inventory of stuff in the section Equipment : As an example, I am able to draw the 20 character code for the +1 Arrows which is  -OsjMmJU-yHNFVyZlyVq . I know from the various help sections it might be best to use .toLowerCase(), but I am not sure how to search for, let's say: Displaying  repeating_equipment_$X_equipment_description in the 2nd Details chat, and Modifying repeating_equipment_$X_equipment_quantity  without going over the  repeating_equipment_$X_equipment_description|max , if latter is specified. Note that quantity is already showing - that's one of the characteristics passed from the original calling script to produce the first chat box, above (showing in the button <> 1) and to the Quantity amount in the second chat box. Can anyone give me a hint of how to find those things? I also have the character token ID, but I don't think that's needed to find and change the number. I'd be adding a button to change amount. My JS code for the second chat window, if necessary, is: //This will be launched from the StuffAmmo.js script. //No need to check for selected character as it is established in //which opens the first chat window. on('ready', () => { const ver = "Version 1"; const verdate = "2 June 2026"; log("═╣ StuffAmmoEq.js for All Eqpt by Tim, !stuff " + ver + ", " + verdate + " ╠═"); on("chat:message", function(msg) { if (msg.type !== "api") return; let args = msg.content.trim().split(/\s*--/); if (args[0].toLowerCase() !== "!listequip") { //log("Crumbs, it did not work: " + msg.content); return; //log("It worked: " + msg.content); // process args here let charID = args[1]; let charName = args[2]; let eqID = args[3]; let equipName = args[4]; //although a number in calling script, arguments are passed as strings, I believe let qtyNo = parseInt(args[5], 10) || 0; let equipMagic = parseInt(args[6], 10) || 0; //sendChat ("System", "2) charID: " + charID + " charName: " + charName + " eqID: " + eqID + " equipName: " + equipName + " Qty: " + qtyNo ); //Set up button and chat window: // The following 4 constants are from ROTTO's TradingPost.js const esc = (s) => String(s) .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); const attrEsc = esc; const panel = (title, body, usercolour) => '<div style="background:' + usercolour + ';border:1px solid #5b4632;border-radius:10px;padding:2px;max-width:760px;color:#f7f1e6;box-shadow:0 2px 6px rgba(0,0,0,0.35);">' + '<div style="font-size:22px;font-weight:bold;text-align:center;margin:0 0 10px 0;font-family:Georgia,serif;color:#fff8ec;line-height:1.15;word-break:break-word;">' + title + '</div>' + '<div style="background:#efe3cf;color:#1b1712;padding:12px;border-radius:6px;">' + body + '</div>' + '</div>'; const btn = (label, cmd, width) => '<a style="display:inline-block;text-align:center;margin:1px 2px 1px 0;padding:3px 6px;min-width:' + (width || 90) + 'px;color:#ffffff;background:#8a5a32;border:1px solid #6a4323;border-radius:6px;text-decoration:none;font-normal:100;" href="' + attrEsc(String(cmd).replace(/\n/g, ' ')) + '">' + esc(label) + '</a>'; //Get user colour from Roll Template Cover on sheet let usercol = getAttrByName(charID,"color_option") || ""; //Translate AD&D 1e sheet colour names to hex - may not be necessary, but convert to hex colours anyway. const colorMap = {"black": "#000000", "blue": "#4479EA", "darkblue": "#293178", "darkgreen": "#004900", "darkgrey": "#484848", "darkorange": "#6D2306", "darkpink": "#5A1D59", "darkpurple": "#4E2267", "darkred": "#6C1515", "darkyellow": "#977D30", "green": "#189A30", "grey": "#9E9E9E", "orange": "#D15400", "pink": "#B751B5", "red": "#FF0000", "white": "#FFFFFF", "yellow": "#F2E000"}; let userhex = colorMap[usercol] || "#FFFFFF"; if (userhex === "#FFFFFF") { userhex = "#808080"; } //Assemble title let strTitle = "<p>" + charName; strTitle += "<br>" + equipName+ "</p>";        //Assemble body let strBody = ""; if (equipMagic === 1) { strBody += "Magic Item"; if (qtyNo !== 1) { strBody += "s, "; } else { strBody += ", " } } strBody += "Quantity: " + qtyNo; sendChat("Details", panel(strTitle, strBody, userhex)); return; }); }); Thanks
That long code you found, something like: -OsjMmJU-yHNFVyZlyVq is the row ID for that specific equipment item. So you do not need to search for $X. $X is more of a sheet/macro shorthand. In the API script, once you know the real row ID, you build the attribute names directly. For example, if your row ID is: -OsjMmJU-yHNFVyZlyVq then the description field is: repeating_equipment_-OsjMmJU-yHNFVyZlyVq_equipment_description and the quantity field is: repeating_equipment_-OsjMmJU-yHNFVyZlyVq_equipment_quantity So in your script, since you already have: let eqID = args[3]; you can read the description like this: const desc = getAttrByName(   charID,   `repeating_equipment_${eqID}_equipment_description` ) || ""; Then, when you build your second chat box, you can add: if (desc.trim()) {   strBody += "<br><br><b>Description:</b><br>" + esc(desc); } That will display the equipment description in your Details chat window. For quantity, same idea. Build the attribute name from the row ID: const qtyAttrName = `repeating_equipment_${eqID}_equipment_quantity`; Then read the current quantity: const qty = parseInt(getAttrByName(charID, qtyAttrName), 10) || 0; If you want to check the max value, use the same attribute name with |max on the end: const maxRaw = getAttrByName(charID, `${qtyAttrName}|max`); Then convert it: const maxQty =   maxRaw === undefined ||   maxRaw === null ||   String(maxRaw).trim() === ""     ? null     : parseInt(maxRaw, 10); If maxQty is null, that means no max is set. Before changing the quantity, clamp it so it does not go below zero or above max: const clampQty = (value, max) => {   let n = parseInt(value, 10) || 0;   if (n < 0) n = 0;   if (max !== null && Number.isFinite(max) && n > max) {     n = max;   }   return n; }; So if you wanted to subtract one arrow, you could do: const newQty = clampQty(qty - 1, maxQty); or if you wanted to add one: const newQty = clampQty(qty + 1, maxQty); The important part is that getAttrByName() only reads the value. It does not let you change it. To change the quantity, you need to find the actual attribute object and set its current value: const setRepeatingAttr = (charID, attrName, value) => {   let attr = findObjs({     type: "attribute",     characterid: charID,     name: attrName   })[0];   if (attr) {     attr.set("current", String(value));   } else {     createObj("attribute", {       characterid: charID,       name: attrName,       current: String(value)     });   } }; Then you update the quantity like this: setRepeatingAttr(charID, qtyAttrName, newQty); So the whole flow is: Use the row ID you already have. Build the attribute name from it. Read the current quantity. Read the max quantity, if there is one. Clamp the new value. Set the quantity attribute. One other thing: in the script you pasted, check this part: if (args[0].toLowerCase() !== "!listequip") {     return; That needs a closing brace: if (args[0].toLowerCase() !== "!listequip") {     return; } Otherwise the rest of the script may be inside that if block after the return, which would stop it from running.
ROTTO said: That long code you found, something like: -OsjMmJU-yHNFVyZlyVq is the row ID for that specific equipment item. So you do not need to search for $X. $X is more of a sheet/macro shorthand. In the API script, once you know the real row ID, you build the attribute names directly. Thank you ROTTO, I've been away, let me have a go at this stuff. :-)
Make a list and I’ll make changes 
ROTTO said: Make a list and I’ll make changes  I'm good. Your first reply was excellent, thank you. 
1781119966

Edited 1781120008
ROTTO said: <snip> For quantity, same idea. Build the attribute name from the row ID: const qtyAttrName = `repeating_equipment_${eqID}_equipment_quantity`; Then read the current quantity: const qty = parseInt(getAttrByName(charID, qtyAttrName), 10) || 0; If you want to check the max value, use the same attribute name with |max on the end: const maxRaw = getAttrByName(charID, `${qtyAttrName}|max`); Hi ROTTO, I'm having a lot of difficulty getting max quantity. I've done the above as well as just plugging in repeating_equipment_${id}_equipment_quantity|max. Logging it with log(eqQty + "/" + missMax); I get: "Error: No attribute or sheet field found for character_id -OcDaqKQXSH4zFPblN5i named {eqQty1}|max" "5/0" "Error: No attribute or sheet field found for character_id -OcDaqKQXSH4zFPblN5i named {eqQty1}|max" "10/0" or "Error: No attribute or sheet field found for character_id -OcDaqKQXSH4zFPblN5i named repeating_equipment_-fpFzRsbmWmPTbURBDQC_equipment_quantity|max" "5/0" "Error: No attribute or sheet field found for character_id -OcDaqKQXSH4zFPblN5i named repeating_equipment_-DDS1i9zZRy2eJUlrgER_equipment_quantity|max" "10/0" The code I am using is:             let eqQty1 = getAttrByName( charID, `repeating_equipment_${id}_equipment_quantity` ) || "0"; <SNIP>             let missMax = getAttrByName( charID, `repeating_equipment_${id}_equipment_quantity|max` or `${eqQty1}|max` ) || "0"; //`${qtyAttrName}|max` log(eqQty + "/" + missMax); Everything else is working nicely: and But I'd like to get a grip on the max value before I start messing with quantity calcs. Thanks. :-) (That's supposed to be an  obsequious  grin)
I think the issue is that |max works in sheet/macros, but not the same way in API code. For getAttrByName(), the max/current part goes in the third argument, not on the end of the attribute name. Try this: const qtyAttrName = `repeating_equipment_${id}_equipment_quantity`; const eqQty = parseInt(     getAttrByName(charID, qtyAttrName, 'current'),     10 ) || 0; const missMax = parseInt(     getAttrByName(charID, qtyAttrName, 'max'),     10 ) || 0; log(`${eqQty}/${missMax}`); So instead of: getAttrByName(charID, `${qtyAttrName}|max`) use: getAttrByName(charID, qtyAttrName, 'max') Also, ${eqQty1}|max won’t work because eqQty1 is the quantity value, like 5 or 10, not the attribute name. For debugging, you could also try this version, which is a little quieter if the max field is missing: const qtyAttrName = `repeating_equipment_${id}_equipment_quantity`; const qtyAttr = findObjs({     type: 'attribute',     characterid: charID,     name: qtyAttrName })[0]; const eqQty = qtyAttr ? parseInt(qtyAttr.get('current'), 10) || 0 : 0; const missMax = qtyAttr ? parseInt(qtyAttr.get('max'), 10) || 0 : 0; log(`${eqQty}/${missMax}`); That should let you see whether the max is actually being stored on that attribute. give me output without download option I think the issue is that |max works in sheet/macros, but not the same way in API code. For getAttrByName(), the max/current part goes in the third argument, not on the end of the attribute name. Try this: const qtyAttrName = `repeating_equipment_${id}_equipment_quantity`; const eqQty = parseInt(     getAttrByName(charID, qtyAttrName, 'current'),     10 ) || 0; const missMax = parseInt(     getAttrByName(charID, qtyAttrName, 'max'),     10 ) || 0; log(`${eqQty}/${missMax}`); So instead of: getAttrByName(charID, `${qtyAttrName}|max`) use: getAttrByName(charID, qtyAttrName, 'max') Also, ${eqQty1}|max won’t work because eqQty1 is the quantity value, like 5 or 10, not the attribute name. For debugging, you could also try this version, which is a little quieter if the max field is missing: const qtyAttrName = `repeating_equipment_${id}_equipment_quantity`; const qtyAttr = findObjs({     type: 'attribute',     characterid: charID,     name: qtyAttrName })[0]; const eqQty = qtyAttr ? parseInt(qtyAttr.get('current'), 10) || 0 : 0; const missMax = qtyAttr ? parseInt(qtyAttr.get('max'), 10) || 0 : 0; log(`${eqQty}/${missMax}`); That should let you see whether the max is actually being stored on that attribute.