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 .
×
Create a free account

Defense Score won't display...

Hello, I have a custom sheet I'm working on.&nbsp; I'm not programmer (cut and paste amateur). This is the code that I have to display &nbsp;the Defense Score. And below is the java. &lt; span class = " subheading " style = " width: 80px " value = " Ranged Def " &gt;&lt; span class = " subheading " name = " attr_rangedDefense " /&gt; The Java I have to display "attr_rangedDefense": function updateDefense () { getAttrs ([ ' strength ' , ' dexterity ' , ' constitution ' , ' wisdom ' , ' rawEnergy ' , ' penetrationPlus ' , ' armorPenetrationPlus ' , ' shieldsPenetrationPlus ' , ' armorDef ' , ' shieldsDef ' , ' Deflection ' , &nbsp; ' racialHPBonus ' ] , function ( values ) { &nbsp; &nbsp; /* &nbsp; &nbsp; &nbsp; &nbsp; DEFENSE &nbsp; &nbsp; &nbsp; &nbsp; Ranged Def &nbsp; &nbsp; &nbsp;10 + Dex + armorDef + shieldsDef + psiDeflection + Misc &nbsp; &nbsp; &nbsp; &nbsp; Melee Def &nbsp; &nbsp; &nbsp; Roll off + Melee Skill + Misc + Deflection &nbsp; &nbsp; &nbsp; &nbsp; Mental Def &nbsp; &nbsp; &nbsp;20+Wis+psiDeflection+psiDomination+RE+Misc &nbsp; &nbsp; &nbsp; &nbsp; CDS &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _"10+Tech Level"__ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HEALTH &nbsp; &nbsp; &nbsp; &nbsp; HP &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Race Base Bonus + Strength x Constitution &nbsp; &nbsp; &nbsp; &nbsp; P+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;P+ = Floor( Con / 10) &nbsp; &nbsp; &nbsp; &nbsp; Regen &nbsp; &nbsp; &nbsp; Floor(CON/5) / day &nbsp; &nbsp; */ &nbsp; &nbsp; let str = parseInt ( values . strength ) || 0 ; &nbsp; &nbsp; let dex = parseInt ( values . dexterity ) || 0 ; &nbsp; &nbsp; let con = parseInt ( values . constitution ) || 0 ; &nbsp; &nbsp; let wis = parseInt ( values . wisdom ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; let re = parseInt ( values . RE ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let techLevel = 0 ; &nbsp; &nbsp; &nbsp; &nbsp; let racialHPBonus = parseInt ( values . racialHPBonus ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; let basePenetrationPlus = Math . floor ( con / 10 ) ; &nbsp; &nbsp; let armorPenetrationPlus = parseInt ( values . armorPenetrationPlus ) || 0 ; &nbsp; &nbsp; let shieldsPenetrationPlus = parseInt ( values . shieldsPenetrationPlus ) || 0 ; &nbsp; &nbsp; let armorDefense = parsInt ( values . armorDef ) || 0 ; &nbsp; &nbsp; let shieldsDefense = parsInt ( values . shieldsDef ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; let psiDef = parseInt ( values . Deflection ) || 0 ; &nbsp; &nbsp; let psiDom = parseInt ( values . Domination ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; // DEFENSE &nbsp; &nbsp; setAttrs ( { rangedDefense : 10 + dex + armorDef + shieldsDef + Deflection } ) ; &nbsp; &nbsp; setAttrs ( { CDS : 10 + techLevel } ) ; &nbsp; &nbsp; // HEALTH &nbsp; &nbsp; setAttrs ( { health_max : racialHPBonus + str * con } ) ; &nbsp; &nbsp; setAttrs ( { penetrationPlus : basePenetrationPlus } ) ; &nbsp; &nbsp; setAttrs ( { regen : Math . floor ( con / 5 ) + } ) ; } ) ; Just about to lose my marbles. Full Code Here:&nbsp;<a href="https://github.com/MavSantroy/Drakudai/blob/main/RangeDefense_Wont_Display.html" rel="nofollow">https://github.com/MavSantroy/Drakudai/blob/main/RangeDefense_Wont_Display.html</a>&nbsp;
PS - I did not write the above. I'm merely editing it a little.
1645749950
GiGs
Pro
Sheet Author
API Scripter
Firstly, you dont want to have multiple setAttrs in the same function. You can combine them into one in at least two ways. This is the method most people use when starting out: setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;stat1: value1, &nbsp;&nbsp;&nbsp;&nbsp;stat2: value2 }); Basically you just use a comma as a separator instead of having a new setAttrs command. This is really important, because setAttrs is a very slow function, can multiples of them can lag a sheet - bundling them together offsets that. Secondly, I notice you havent matched up variable names currectly. For instance you define two variables here: let armorDefense = parsInt(values.armorDef) || 0; let shieldsDefense = parsInt(values.shieldsDef) || 0; In this code you create two variables , named armorDefence and shieldsDefence, and they store the values of the attributes named armorDef and shieldsDef. You have basically grabbed the values of those attributes, and stored them in variables. Now when you want the values of those attributes you have to use the variable name. Then in your first setAttrs, you use attribute names, not variable names. &nbsp; // DEFENSE &nbsp; &nbsp; setAttrs({ rangedDefense : 10 + dex + armorDef + shieldsDef + Deflection }); The armorDef + shieldsDef part should be armorDefense + shieldsDefense Also in that line, you have Deflection, but you do not define a variable by the name anywhere. So you would need something like let Deflection = parsInt(values.Deflection) || 0; Sheet workers don't know anything about the character sheet. To be able to use the attribute values on the sheet, you first need to tell the worker to go to the sheet and grab them - thats what the getAttrs line does: getAttrs(['strength', 'dexterity', 'constitution', 'wisdom', 'rawEnergy', 'penetrationPlus', 'armorPenetrationPlus', 'shieldsPenetrationPlus', 'armorDef', 'shieldsDef', 'Deflection', &nbsp;'racialHPBonus'], function(values) { The getAttrs says, "go to the sheet, and grab the values of strength, dexterity, and the other attributes named here, and store them in an object named values (the bit named in function). Now once you have those attributes stored in the values object, you need to get them out so you can use them. One way of doing that is a line like this: let Deflection = parsInt(values.Deflection) || 0; This takes the attribute named Deflection out of the values object, and stores its value in a variable named Deflection. You can then use Deflection in the worker.
1645751276

Edited 1645751497
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Welcome to sheet coding Michael! There's a few issues that I can see that I believe are causing your issue. Span elements can't be self closing. This: &lt;span class="subheading" name="attr_rangedDefense" /&gt; needs to be: &lt;span class="subheading" name="attr_rangedDefense"&gt;&lt;/span&gt; Undefined variables used in your calculation The rangedefense calculation uses the variables&nbsp; armorDef ,&nbsp; shieldsDef , and&nbsp; Deflection . These are the keys of the attribute values in the values object. The attributes that are storing the actual numbers in the code are&nbsp; armorDefense ,&nbsp; shieldsDefense , and&nbsp; psiDef &nbsp;respectively. These are the variables you need to use.\ Syntaxerror The final&nbsp; setAttrs &nbsp;call in your code has a syntaxerror in it where you have a trailing plus sign. This syntaxerror will stop your sheetworkers from loading at all and so nothing should be running on your sheet. CODE CRITIQUE I realize that this isn't originally your code, but I've also got some general code critique: Multiple&nbsp; setAttrs It's generally not a good idea to chain several&nbsp; setAttrs &nbsp;together. The more efficient way to do this operation is to set all your values with a single&nbsp; setAttrs . The way to do this is to create an object to store your changes in and then simply pass the object as the first argument to&nbsp; setAttrs . This looks like this: function updateDefense() { getAttrs(['strength', 'dexterity', 'constitution', 'wisdom', 'rawEnergy', 'penetrationPlus', 'armorPenetrationPlus', 'shieldsPenetrationPlus', 'armorDef', 'shieldsDef', 'Deflection', 'racialHPBonus'], function(values) { /* DEFENSE Ranged Def 10 + Dex + armorDef + shieldsDef + psiDeflection + Misc Melee Def Roll off + Melee Skill + Misc + Deflection Mental Def 20+Wis+psiDeflection+psiDomination+RE+Misc CDS _"10+Tech Level"__ HEALTH HP Race Base Bonus + Strength x Constitution P+ P+ = Floor( Con / 10) Regen Floor(CON/5) / day */ const setObj = {};//Create a blank object that is going to store the values we want to set. let str = parseInt(values.strength) || 0; let dex = parseInt(values.dexterity) || 0; let con = parseInt(values.constitution) || 0; let wis = parseInt(values.wisdom) || 0; let re = parseInt(values.RE) || 0; let techLevel = 0; let racialHPBonus = parseInt(values.racialHPBonus) || 0; setObj.penetrationPlus = Math.floor(con/10); let armorPenetrationPlus = parseInt(values.armorPenetrationPlus) || 0; let shieldsPenetrationPlus = parseInt(values.shieldsPenetrationPlus) || 0; let armorDefense = parsInt(values.armorDef) || 0; let shieldsDefense = parsInt(values.shieldsDef) || 0; let psiDef = parseInt(values.Deflection) || 0; let psiDom = parseInt(values.Domination) || 0; // DEFENSE setObj.rangedDefense = 10 + dex + armorDefense + shieldsDefense + psiDef; setObj.CDS = 10 + techLevel // HEALTH setObj.health_max = racialHPBonus + str * con;//it's also a best practice to separate your math operation symbols from the variables you are using them on. This makes for code that is more readable. setObj.regen = Math.floor(con/5); setAttrs(setObj); }); This also allows you to log your changes so that you can more easily debug issues with your sheet. Attribute Names I notice that the sheet is using camelCase for its attribute names. This is something that I heavily recommend against for a couple of reasons: CamelCase is harder for assistive technologies like screen readers to handle. I find that snake_case works better for these The sheetworker listeners have to be in all lower case anyways and having them be cased in one area while not in another is asking for typos to occur. Once again, snake_case is already all lowercased and so is written the same regardless of where in the code it is. The Roll20 attribute system is case insensitive (with the exception of the sheetworker listeners). So, there's no functional difference between myAttribute , myattribute , and MyAtTrIbUtE &nbsp;as attribute names. I hope that was helpful, and good luck! EDIT: Sniped by GiGs!
This is GREAT GUYS! I got it to work.&nbsp; I couldn't figure out how to make the setAttrs to blob them together... it wasn't working :( That said...&nbsp;&nbsp; I'm trying to do some math with the javascript: let lifeForce = parseInt ( values . life_energy ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let rawEnergy = [] ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ( lifeForce &lt; 100 ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; " 0 " &nbsp; || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;( lifeForce - 100 ) / 10 || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; } But it doesn't seem to be updating. I have two attributes: Life_Energy &amp; Raw_Energy.&nbsp; Raw_Energy is a product of Life Energy.&nbsp; When Life Energy reaches 100 it starts to produce 1 RE, and then +1 every 10 additional Life Energy. So I'm trying to say IF Life Energy = less than 100, then just say "0", otherwise minus 100 from the life energy and then divide the remainder by 10 (+1 which hasn't been added yet). Here's the whole function function updateRawEnergy () { &nbsp; &nbsp; getAttrs ([ ' life_energy ' ] , function ( values ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let lifeForce = parseInt ( values . life_energy ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let rawEnergy = [] ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ( lifeForce &lt; 100 ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; " 0 " &nbsp; || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;( ( lifeForce - 100 ) / 7 + 1 ) &nbsp; || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } ) ; }
I think I see where I messed up... I didn't set any attrs after the I did all the work
1645823115
GiGs
Pro
Sheet Author
API Scripter
Thats one thing - you also dont save the result of your calculation to a variable. Here is an attempt at a fixed worker: &nbsp;&nbsp;&nbsp;&nbsp; function updateRawEnergy () { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getAttrs ([ 'life_energy' ], function ( values ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let lifeForce = parseInt ( values . life_energy ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let rawEnergy = 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ( lifeForce &lt; 100 ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rawEnergy = 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rawEnergy = Math . floor (( lifeForce - 100 ) / 10 + 1 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setAttrs ({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ATTRIBUTE_NAME_ON_SHEET : rawEnergy &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; }; Some things to note: You use || 0 a lot in situations where it's not needed. Generally speaking, you should only be doing that when initially defining vaiables that you are grabbing from values . (There other situations where you might use it, but most of the time you don't need it.) Math.floor() is a function that lets you round down, you probably dont want fractional numbers. I divided by 10, because thats what your text says - though the function dovides by 7. Finally ATTRIBUTE_NAME_ON_SHEET should be replaced by whatever the rawEnergy attribute is named on your sheet. Once more observation: this is defined as a function that is called from somewhere else. I have seen a few sheets do this, and I honestly don't understand why - uness the worker is to be used in multiple places, it just makes the code harder to follow and harder to maintain.So I would replace that first line with the on('change;) that triggers it. Here's an adjusted worker, which also has a streamlined version of the calculation: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on ( 'change:life_energy' , function () { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getAttrs ([ 'life_energy' ], function ( values ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let lifeForce = parseInt ( values . life_energy ) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let rawEnergy = Math . max ( 0 , Math . floor ( lifeForce - 10 ) - 9 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setAttrs ({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ATTRIBUTE_NAME_ON_SHEET : rawEnergy &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; }); This uses the Math.max() function, which takes a list of numebrs or calculations, and keeps the highest value. This ensures that when lifeForce is below 100, he result is 0.
1645831106
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
GiGs said: Once more observation: this is defined as a function that is called from somewhere else. I have seen a few sheets do this, and I honestly don't understand why - uness the worker is to be used in multiple places, it just makes the code harder to follow and harder to maintain.So I would replace that first line with the on('change;) that triggers it. Here's an adjusted worker, which also has a streamlined version of the calculation Personally, I prefer defining them as functions. This lets me define all my listeners in a few compact lines, and if I use well named functions, it's easy to see the general idea of what that listener does at a glance. YMMV.
1645831299
GiGs
Pro
Sheet Author
API Scripter
Scott C. said: GiGs said: Once more observation: this is defined as a function that is called from somewhere else. I have seen a few sheets do this, and I honestly don't understand why - uness the worker is to be used in multiple places, it just makes the code harder to follow and harder to maintain.So I would replace that first line with the on('change;) that triggers it. Here's an adjusted worker, which also has a streamlined version of the calculation Personally, I prefer defining them as functions. This lets me define all my listeners in a few compact lines, and if I use well named functions, it's easy to see the general idea of what that listener does at a glance. YMMV. Do you think that's what's happening here though?
1645831699

Edited 1645831716
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Based on having an updateLifeEnergy function and an updateDefense function; I'm hoping so. Although I'm also hoping that multiple of these aren't being called at once, as that'd be asking for a performance issue and the creation of a race condition causing unreliable order of operations on the sheet.
1645832233
GiGs
Pro
Sheet Author
API Scripter
Scott C. said: Based on having an updateLifeEnergy function and an updateDefense function; I'm hoping so. Although I'm also hoping that multiple of these aren't being called at once, as that'd be asking for a performance issue and the creation of a race condition causing unreliable order of operations on the sheet. That's one of the reasons I advise against it. In every situation I've seen this (except your sheets), it's been coupled with very inefficent chains of getAttrs and setAttrs but the structure hides the fact its going on and makes it hard to figure out what exactly is happening. It can be a great approach for very skilled sheet creators, but the vast majority of roll20's sheet creators are novices and new learners. There's nothing wrong with that! I think it's the big strength of roll20. But I also think it's best to steer such people away from practices that will likely trip them up - those who are more advanced are able to ignore such advice if they choose to :)
Hey guys, I got it to work. Here is my current code, if you are interested in seeing progress.&nbsp; Code at GitHub I have a question about defining a symbol.&nbsp; I'm not quite getting it's description... What does the " || 0; " mean? after a 'let' portion of the script mean? &nbsp; let move = parseInt(values.movementRacial) || 0;&nbsp; &nbsp; &nbsp;&nbsp; Thanks for all your help! I'm falling in love with coding this stuff... I may even recreate this one totally as a single page or maybe just a couple of pages rather than a bunch of tabs
1646031095

Edited 1646031255
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
|| &nbsp;is the javascript symbol for or &nbsp;( &amp;&amp; &nbsp;is the symbol for and ). That line means that it will set that variable to either the result of parseInt (if it is truthy) or to 0 &nbsp;if the result of parseInt is falsy. Truthy and falsy are key concepts of javascript as javascript defines all values as truthy or falsy. Falsy values are undefined , null , NaN , the number&nbsp; 0 , empty string ( "" ),&nbsp;and false &nbsp;(I think that's all of them, but may have missed one or two from memory). Truthy values are anything else from the number 1 &nbsp;to an empty (or populated) object ( {} ), to an actual boolean value of true , and even just plain non empty strings (e.g. "The quick brown fox" ). EDIT: Note that this means that if you wanted 0 &nbsp;to be a valid value of the variable, but have it default to something else (say 10 ), you'd need to do more logic to properly decide whether to use the default value or the actual return value of parseInt
excellent!&nbsp; thank you!&nbsp; best explanation I've read so far!!!