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

[HELP] Change value in repeating section

1678872362

Edited 1678873680
Hello, I have make a bestiary on a part of my character sheet. In this bestiary I have a level value (nvgroupe) and I have a repeating section (repeating_En) where I have my monsters. When the level change, I want to update the life points (Pdv) of all my monsters. I have wrote this sheetworker but it doesn't work. Anyone can help me ? on ( "change:nvgroupe" , function () {         getAttrs ([ "nvgroupe" ], function ( values ) {             var coeff = 1.15 ;             var nvgroupe = parseInt ( values . nvgroupe );             getSectionIDs ( "repeating_En" , function ( idarray ) {                 var fieldNames = [];                 for ( var i = 0 ; i < idarray . length ; i ++) {                     fieldnames . push ( "repeating_En_" + idarray [ i ] + "_Pdv" );                 }                 if ( nvgroupe > 1 ) {                     getAttrs ( fieldNames , function ( values ) {                         var output = {};                         for ( var i = 0 ; i < idarray . length ; i ++) {                             output [ "repeating_En_" + idarray [ i ] + "_Pdv" ] = [ "repeating_En_" + idarray [ i ] + "_Pdv" ] * ( coeff ^( nvgroupe - 1 ));                         }                         setAttrs ( output );                     });                 }             });         });     });
1678888612

Edited 1678888985
GiGs
Pro
Sheet Author
API Scripter
One big problem in your code is you create a fieldNames array, but your loop uses fieldnames: those are spelled differentky, and refer to different attributes. So your second loop finds no attributes. Working with a repeating section requires you to build your sheet workers a little differently, if you want to be efficient. Do not put getAttrrs and setAttrs inside a loop. Try this: on ( 'change:nvgroupe' , function () {         getSectionIDs ( "repeating_En" , function ( idarray ) {             var fieldNames = [];             for ( var i = 0 ; i < idarray . length ; i ++) {                 fieldNames . push ( "repeating_En_" + idarray [ i ] + "_Pdv" );             }             fieldNames . push ( "nvgroupe" );             getAttrs ( fieldNames , function ( values ) {                 var coeff = 1.15 ;                 var nvgroupe = parseInt ( values . nvgroupe ) || 0 ;                 var output = {};                 for ( var i = 0 ; i < idarray . length ; i ++) {                     output [ "repeating_En_" + idarray [ i ] + "_Pdv" ] = parseInt ( values [ "repeating_En_" + idarray [ i ] + "_Pdv" ]) * ( coeff ^( nvgroupe - 1 ));                 }                 setAttrs ( output );             });         });     }); I'm not sure about your calculation. It doesnt take the previous level before the change into account. Remember you can go from level 3 to 7, or level 9 to 1. If you want to make sure the only possible change is to increase level by one each time, an action button would probably be better.
1678890136

Edited 1678892374
GiGs
Pro
Sheet Author
API Scripter
Here's an alternative worker to take any level into account. (There is a problem here I'll describe below). const repeatingName = ( section , id , name ) => `repeating_ ${ section } _ ${ id } _ ${ name } `;     on ( 'change:nvgroupe' , function ( event ) {         const new_level = + event . newValue || 0 ;         const old_level = + event . previousValue || 0 ;         if ( new_level === 0 || old_level === 0 ) return ;         getSectionIDs ( "repeating_En" , function ( idarray ) {             const fieldNames = idarray . reduce (( all , one ) => [... all , `repeating_En_ ${ one } __Pdv` ], []);             getAttrs ( fieldNames , function ( values ) {                 const coeff = 1.15 ; c onst level_multiplier = Math . pow ( coeff , new_level - old_level );                 const output = {};                 idarray . forEach ( id => {                     const old_hp = parseInt ( values [ repeatingName ( "En" , id , "Pdv" )]);                     const new_hp = old_hp * level_multiplier ;                     output [ repeatingName ( "En" , id , "Pdv" )] = new_hp ;                 });                 setAttrs ( output );             });         });     }); The new feature: apart fromdifferent coding techniques, this uses the evntInfo object (event),  which allows you to get the level before the change, and the level after the change. You can then use the Math.pow function to determine what the multiplier should be. The problem: this requires all Pdv attributes be entered correctly for the character's current level before you change the level at all. It would probabky be better to have a constant HP and Level to enter base amounts into (or assuming a standard level like 1), then the calculation can be made without needing eventInfo, and monsters can be entered at any level without issue. It also raises questions: why are these creatures dependent on a single level outside the repeating section? But that's probably game-specific.
Hello GiGs, Thanks for your answers. T hese creatures dependent on a single level outside the repeating section because it's the group level and I want to fit the life of the creatures compared with the level of the players. So the second solution you have make seems the best to me. GiGs said: Here's an alternative worker to take any level into account. (There is a problem here I'll describe below). const repeatingName = ( section , id , name ) => `repeating_ ${ section } _ ${ id } _ ${ name } `     on ( 'change:nvgroupe' , function ( event ) {         const new_level = + event . newValue || 0 ;         const old_level = + event . previousValue || 0 ;         if ( new_level === 0 || old_level === 0 ) return ;         getSectionIDs ( "repeating_En" , function ( idarray ) {             const fieldNames = indarray . reduce (( all , one ) => [... all , `repeating_En_ ${ one } __Pdv` ], []);             getAttrs ( fieldNames , function ( values ) {                 const coeff = 1.15 ; c onst level_multiplier = Math . pow ( coeff , new_level - old_level );                 const output = {};                 idarray . forEach ( id => {                     const old_hp = parseInt ( values [ repeatingName ( "En" , idarray [ i ], "Pdv" )]);                     const new_hp = old_hp * level_multiplier ;                     output [ repeatingName ( "En" , idarray [ i ], "Pdv" )] = new_hp ;                 })                 setAttrs ( output );             });         });     }); The new feature: apart fromdifferent coding techniques, this uses the evntInfo object (event),  which allows you to get the level before the change, and the level after the change. You can then use the Math.pow function to determine what the multiplier should be. The problem: this requires all Pdv attributes be entered correctly for the character's current level before you change the level at all. It would probabky be better to have a constant HP and Level to enter base amounts into (or assuming a standard level like 1), then the calculation can be made without needing eventInfo, and monsters can be entered at any level without issue. It also raises questions: why are these creatures dependent on a single level outside the repeating section? But that's probably game-specific.
1678892173

Edited 1678892344
GiGs
Pro
Sheet Author
API Scripter
I just noticed I had two errors in my second worker (ironically, one was a typo in the same place as you had - affecting the fieldNames array - indarray vs idarray), and have now corrected that post.
1678892526
GiGs
Pro
Sheet Author
API Scripter
You might also want some rounding on the new)hp value, to make sure you dont have attributes with ridiculous number of decimals. This will affect the accuracy of the calculation later on (which is another reason to use a separate base_hp attribute). const new_hp = old_hp * level_multiplier ; would probably be something like const new_hp = Math.round( old_hp * level_multiplier );
Yes I will use a base_hp attribute. It's smarter than what I want to make. GiGs said: You might also want some rounding on the new)hp value, to make sure you dont have attributes with ridiculous number of decimals. This will affect the accuracy of the calculation later on (which is another reason to use a separate base_hp attribute). const new_hp = old_hp * level_multiplier ; would probably be something like const new_hp = Math.round( old_hp * level_multiplier );
1678905182

Edited 1678913261
GiGs
Pro
Sheet Author
API Scripter
Assuming you have a base_hp for every creature at level 1, here's a worker that should work. const repeatingName = ( section , id , name ) => `repeating_ ${ section } _ ${ id } _ ${ name } ` ; on ( 'change:nvgroupe change:repeating_en:hp_base' , function () {         getSectionIDs ( "repeating_En" , function ( idarray ) {             const fieldNames = idarray . reduce (( all , one ) => [... all , repeatingName ( "En" , one , "hp_base" )], []);             getAttrs ([... fieldNames , "nvgroupe" ], function ( values ) {                 const coeff = 1.15 ;                 const nvgroupe = parseInt ( values . nvgroupe ) || 0 ;                 const level_multiplier = Math . pow ( coeff , nvgroupe - 1 );                 const output = {};                 idarray . forEach ( id => {                     const base_hp = parseInt ( values [ repeatingName ( "En" , id , "hp_base" )]);                     output [ repeatingName ( "En" , id , "Pdv" )] = Math . round ( base_hp * level_multiplier );                 });                 setAttrs ( output );             });         });     }); Just change each instance of "hp_base" to whatever you change that attribute name to. (It's used 3 times in the above code.) Your Pdv attribute can be set to readonly, so players dont accidentally change it - it is now calculated automatically. Note that this runs whenever the hp_base attribute is changed - so if a player enters a new monster, enters their base attribute, the total Pdv will be calculated taking the current level into account.
1678910050

Edited 1678910088
 I have include your script but it doesn't work . I now use the PdV variable as HP base and I have add a line with the update HP value. Can you help me to make it work ? Also, I want to make this for the damage (I can explain the formula to calculate damage modification) and repeat this sheetworker for six others repeating sections (the name of the variables is the same for all of them). < div class = "char" style = " color: black; font-weight: bold; font-size: large;" > Niveau du groupe : < input class = "textbox-stat" type = "number" name = "attr_nvgroupe" value = "1" /></ div ><!--Group level variable-->     < div class = "dark_row" >                                                                                  < div class = "title" style = " font-effect:engrave; margin-top:35px;" > Enahora </ div >         < div class = 'expandable-section' >             < input class = "expand-control" type = "hidden" name = "attr_expand_item" value = "1" />             < label class = "expand-button" >                 < input type = "checkbox" name = "attr_expand_item" value = "1" checked = "true" />< span class = "expand-state pictos" > 4 </ span >             </ label >             < div class = "expanded-view" >                  < fieldset class = "repeating_En" >                     < div class = "dark_row" style = " font-size:20px;line-height:25px;" >                         < div   class = "arme-title" style = " margin-left:75px; margin-top: 10px;" > Nom </ div >                         < div   class = "arme-title" style = " margin-left:65px;" > Dégâts base </ div >                         < div   class = "arme-title" style = " margin-left:18px;" > PdV base </ div >                         < div   class = "arme-title" style = " margin-left:50px;" > Description </ div >                         < div class = "arme-title" style = " margin-left:140px;" > Image </ div >                     </ div >                     < div class = "dark_row" >                         < button   type = "roll" title = "bestiaireDesc" name = "roll_desc" value = "&{template:bestiaire} {{name=@{name } }} {{image=[Image](@{image})}} {{description=@{Description } }} {{carac1=@{FO } }} {{carac2=@{DEX } }} {{carac3=@{PO } }} {{pdv=@{PdvNv } }}" ></ button >                         < input class = "textbox" type = "text" name = "attr_name" style = " width:120px;" />                         < input class = "textbox-stat" type = "number" name = "attr_Deg" style = " width:40px;text-align:center;" > d10+ </ input >                         < input class = "textbox-stat" type = "number" name = "attr_DegModif" style = " width:40px;text-align:center;" />           <!--HP base--> < input class = "textbox-stat" type = "number" name = "attr_PdV" style = " width:50px; margin-left:15px;" />                         < textarea class = "textareaboxStat" type = "text" name = "attr_Description" style = " vertical-align: top; width:200px; margin-left:15px;" ></ textarea >                         < textarea class = "textareaboxStat" type = "text" name = "attr_Image" value = "[Image](Lien)" style = " vertical-align: top; width:200px; margin-left: 5px;" ></ textarea >                         < br >                         < div   class = "arme-title" style = " margin-left:20px;" > XP </ div >                         < div class = "arme-title" style = " margin-left:35px;" > FO </ div >                         < div class = "arme-title" style = " margin-left:30px;" > DEX </ div >                         < div class = "arme-title" style = " margin-left:28px;" > PO </ div >                         < div   class = "arme-title" style = " margin-left:35px;" > Dégâts niveau </ div >                         < div   class = "arme-title" style = " margin-left:7px;" > PdV niveau </ div >                         < br >                         < input class = "textbox-stat" type = "number" name = "attr_Xp" style = " width:50px;" />                         < input class = "textbox-stat" type = "number" name = "attr_FO" style = " width:50px;text-align:center;" />                         < input class = "textbox-stat" type = "number" name = "attr_DEX" style = " width:50px;text-align:center;" />                         < input class = "textbox-stat" type = "number" name = "attr_PO" style = " width:50px;" />                         < input class = "textbox-stat" type = "number" name = "attr_DegNv" style = " width:40px;text-align:center;" readonly = "readonly" > d10+ </ input >                         < input class = "textbox-stat" type = "number" name = "attr_DegModifNv" style = " width:40px;text-align:center;" readonly = "readonly" /> <!--HP update with level--> < input class = "textbox-stat" type = "number" name = "attr_PdvNv" style = " width:50px; margin-left:15px;" readonly = "readonly" />                         < br >                         < button type = "roll" style = " margin-left:80px;" title = "bestiaireEnForce" name = "roll_FO" value = "&{template:enyllion} {{perso=@{name } }} {{name=Force}} {{Carac=[[@{FO}+?{Modificateur|0}]]}}  {{Roll=[[1D100]]}}" ></ button >                         < button type = "roll" style = " margin-left:30px;" title = "bestiaireEnDexterité" name = "roll_DEX" value = "&{template:enyllion} {{perso=@{name } }} {{name=Dexterité}} {{Carac=[[@{DEX}+?{Modificateur|0}]]}}  {{Roll=[[1D100]]}}" ></ button >                         < button type = "roll" style = " margin-left:30px;" title = "bestiaireEnPouvoir" name = "roll_PO" value = "&{template:enyllion} {{perso=@{name } }} {{name=Pouvoir}} {{Carac=[[@{PO}+?{Modificateur|0}]]}}  {{Roll=[[1D100]]}}" ></ button >                         < button type = "roll" style = " margin-left:75px;" title = "bestiaireEnDegats" name = "roll_Degats" value = "&{template:degats} {{perso=@{name } }} {{name=Dégats}} {{Roll=[[@{DegNv}d10+@{DegModifNv}]]}}" ></ button >                     </ div >                     < hr align = "center" width = "60%" >                 </ fieldset >              </ div >         </ div >
1678913329

Edited 1678913406
GiGs
Pro
Sheet Author
API Scripter
I had a code error on one of the lines. I have fixed it now. This line                     const base_hp = parseInt(values[repeatingName("En", one, "hp_base")]); should be                     const base_hp = parseInt(values[repeatingName("En", id, "hp_base")]); It's fixed now. This is the sheet worker with your attribute names. const repeatingName = ( section , id , name ) => `repeating_ ${ section } _ ${ id } _ ${ name } ` ;     on ( 'change:nvgroupe change:repeating_en:pdv' , function ( event ) {         getSectionIDs ( "repeating_En" , function ( idarray ) {             const fieldNames = idarray . reduce (( all , one ) => [... all , repeatingName ( "En" , one , "pdv" )], []);             getAttrs ([... fieldNames , "nvgroupe" ], function ( values ) {                 const coeff = 1.15 ;                 const nvgroupe = parseInt ( values . nvgroupe ) || 0 ;                 const level_multiplier = Math . pow ( coeff , nvgroupe - 1 );                 const output = {};                 idarray . forEach ( id => {                     const base_hp = parseInt ( values [ repeatingName ( "En" , id , "pdv" )]);                     output [ repeatingName ( "En" , id , "Pdvnv" )] = Math . round ( base_hp * level_multiplier );                 });                 setAttrs ( output );             });         });     });
1678913954

Edited 1678914175
It works. Very big thank you. :D Now I will do this for the others section and the damages. How can I make this ? It is possible to add two others values in the fieldNames array ?
1678914599
GiGs
Pro
Sheet Author
API Scripter
You'll have to tell me exactly what you want to do.
In my bestiary, I have three values to update : - the HP (it works now) - the damage and the damage modifier The formulas to do this is : DegTot = (( Deg * 10 ) + DegModif ) * ( coeff ^( niveau - 1 )); DegNv  =  Math . floor ( DegTot / 10 ); DegModifNv = DegTot % 10 ; The name of inputs are "Deg" an "DegModif" for the base and "DegNv" and "DegModifNv" for the updated values.
1678918382
GiGs
Pro
Sheet Author
API Scripter
I'm not following which of those stats are relavant. Is DegTotal different from Pdvnv? It looks like you have inputs of Deg and DegModif, and everything else is calculated from them - is that correct? And you want extra calculations for each monster to go with these?                      const base_hp = parseInt ( values [ repeatingName ( "En" , id , "pdv" )]);                     output [ repeatingName ( "En" , id , "Pdvnv" )] = Math . round ( base_hp * level_multiplier ); Is niveau the same stat as nvgroupe?
1678918707

Edited 1678918845
Yes, I want extra calculations for each monster to go with the hp output if it's possible. DegTotal is the result of the formula I have wrote above, is different from Pdvnv. And "niveau" is the same stat as nvgroupe. And yes, everything for damages is calculated from Deg and DegModif.
1678941082

Edited 1678941163
GiGs
Pro
Sheet Author
API Scripter
If I've understood what you need, here's code that should do what you need, set up for the additional repeating sections (some notes follow) const repeatingName = ( section , id , name ) => `repeating_ ${ section } _ ${ id } _ ${ name } ` ;     [ "En" ]. forEach ( section => {         const coeff = 1.15 ;         on ( `change:niveau change:repeating_ ${ section . toLowerCase () } :pdv` , function () {             getSectionIDs ( `repeating_ ${ section } ` , function ( idarray ) {                 const fieldNames = idarray . reduce (( all , one ) => [... all , repeatingName ( section , one , "pdv" )], []);                 getAttrs ([... fieldNames , "niveau" ], function ( values ) {                     const niveau = parseInt ( values . niveau ) || 0 ;                     const level_multiplier = Math . pow ( coeff , niveau - 1 );                     const output = {};                     idarray . forEach ( id => {                         const base_hp = parseInt ( values [ repeatingName ( section , id , "pdv" )]);                         output [ repeatingName ( section , id , "Pdvnv" )] = Math . round ( base_hp * level_multiplier );                     });                     setAttrs ( output );                 });             });         });         on ( `change:niveau change:repeating_ ${ section . toLowerCase () } :deg change:repeating_ ${ section . toLowerCase () } :degmodif` , function () {             getSectionIDs ( `repeating_ ${ section } ` , function ( idarray ) {                 const fieldNames = idarray . reduce (( all , one ) => [... all , repeatingName ( section , one , "deg" ), repeatingName ( section , one , "degmodif" )], []);                 getAttrs ([... fieldNames , "niveau" ], function ( values ) {                     const niveau = parseInt ( values . niveau ) || 0 ;                     const level_multiplier = Math . pow ( coeff , niveau - 1 );                     const output = {};                     idarray . forEach ( id => {                         const deg = parseInt ( values [ repeatingName ( section , id , "deg" )]);                         const degmodif = parseInt ( values [ repeatingName ( section , id , "degmodif" )]);                         const DegTot = ( deg * 10 + degmodif ) * level_multiplier ;                         const DegNv = Math . floor ( DegTot / 10 );                         const DegModifNv = DegTot %= 10 ;                         output [ repeatingName ( section , id , "DegNv" )] = DegNv ;                         output [ repeatingName ( section , id , "DegModifNv" )] = DegModifNv ;                     });                     setAttrs ( output );                 });             });         });     }); This splits the work over two sheet workers in the interests of ease and modularity. The first worker calculates hp, the second the damage stuff, but the sheet workers use most of the same code. (So this could have been written more elegantly, but copy & paste exists, so...) This assumes you have changed the name nvgroupe to niveau, and changed it in the original sheet worker. Now if you have multiple sheet workers whose attributes are all the same and the only change is the repeating section, you only need to change one line:     [ "En" ]. forEach ( section => { Just add the extra repeating sectiowns in there separated by comams, so if your had repeating_en, repeating_two, and repeating_three, you'd change that line to     [ "En", "two ", "three" ]. forEach ( section => { That will handle all the repeating sections with no further changes needed.
1678951429

Edited 1678951444
Hello GiGs, thanks for your time. I have replace the variable's names with the correct and I have replace this line : const DegModifNv = DegTot %= 10 ; With this line const degModifNv = Math . round ( degTot % 10 ); And it's work. In your expression, the "%=" doesn't work but just "%" work. It's pretty good, all the sheetworker works, thank you very much :D .
1678981899
GiGs
Pro
Sheet Author
API Scripter
I'm happy you got it working. I've never used the remainder function in JS before, I'll have to play around with it.