Creating a list from selected tokens following conditions

Talk about whatever topic you'd like, RPG related or not. (But please discuss things related to our software in the Tools section, below.)

Moderators: dorpond, trevor, Azhrei

Post Reply
User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Creating a list from selected tokens following conditions

Post by AriesBreath »

Hello, I need to create a list of tokens following some conditions. In this case I need to follow these steps:
1) Select some tokens on the map
2) Run the macro
3) The macro gets the selected tokens and adds them to a list if they meet a condition.

The condition, for example, is if they have an active status like "Concentrating".

I initially came up with this:

Code: Select all

[h: json = getSelected()]

[h: ConcList = ("")]

[r, foreach(character, json), CODE:
   {
      [h: switchToken(character)]
      
      [if(state.Concentrating == 1),CODE:
     {[h: json.append(ConcList, character)]
      [r: token.name + " Is Concentrated"]}]

      [if(state.Concentrating == 0),CODE:
     {[r: token.name + " Is Not Concentrated"]}]
      
    }]

[r, foreach(character, ConcList), CODE:
{
   [h: switchToken(character)]

   [if(state.Concentrating == 1),CODE:
    {[r: "Yes"]}]
    
   [if(state.Concentrating == 0),CODE:
    {[r: "No"]}]

}]
It gets all the selected tokens and puts them in a list, create an empty list in which it will be putting some tokens and then it try to check if a condition (the token is concentrating) is met, putting it into the list and returning some text to check everything is fine. Then I try to check if everything is working by searching in the created list and returning some response like Yes and No, but nothing comes up.

I don't know where I'm wrong and I don't know how to create or access this list, can you help me?

User avatar
aliasmask
Deity
Posts: 8611
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Creating a list from selected tokens following conditions

Post by aliasmask »

Your main issue with your example is that you don't save the list.

This is how I would write your code. I recommend to save your output to a variable and use variable names that are detailed and representative of the contents. Adding comments is also helpful when creating a macro and when you come back to it as a later date. Indentation for the various blocks also helps with the readability.

Code: Select all

<!-- get selected tokens -->
[h: tokenIds = getSelected("json")]
[H: concentrateList = ""]

<!-- build output showing concentrating state -->
[h: output = ""]
[H, foreach(tokenId, tokenIds), code: {
   [h: switchToken(tokenId)]

   [H, if(state.Concentrating), code: {
      <!-- save list of tokens who are concentrating -->
      [h: concentrateList = json.append(concentrateList, tokenId)]
      [H: output = json.append(output,strformat("%{token.name} Is Concentrated."))]
   };{
      [H: output = json.append(output,strformat("%{token.name} Is Not Concentrated."))]
   }]
}]

<!-- show all tokens and concentrating state -->
[R: json.toList(output,"<br>")]

User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Re: Creating a list from selected tokens following conditions

Post by AriesBreath »

Thanks for the reply and sorry for my bad programming but I'm just a GM who's trying to get some macros and that can't program.

I tried your code but it asks me for "concentrateList" and stops working. I analyzed it and corrected my code (I don't really know how I did it) and now it works.

Code: Select all

<!-- Get Tokens -->
[h: TokenIDs = getSelected()]

<!-- Initialize List -->
[h: ConcentratingList = ("")]

<!-- Fills the list with the concentrating tokens, then outputs the names and if they are concentrating -->
[r, foreach(Character, TokenIDs), CODE:
   {
      [h: switchToken(Character)]
      
      [r, if(state.Concentrating == 1),CODE:
     {
       [h: ConcentratingList = json.append(ConcentratingList, Character)]
       [r: token.name + " Is Concentrated"]
       };{
       [r: token.name + " Is Not Concentrated"]}]
}]

<!-- Once the list is done, this gives a feedback if everything is fine -->
[r, foreach(Character, ConcentratingList), CODE:
{
   [h: switchToken(Character)]

   [if(state.Concentrating == 1),CODE:
    {[r: token.name + " Yes"]}]
}]
I then implemented it in another macro I was working on that needed this code but now there is a last thing (I hope it's the last) that doesn't work.
I need a pop up window that shows me some data collected during the execution of the macro. I'm trying to use Dialogs but it doesn't really work.

The macro fills a list in this way during the execution (these are pieces of code inside the bigger macro)

Code: Select all

[h: OutputText = ("")]

 [r, foreach(Character, ConcentratingList), CODE:
   {
      [h: switchToken(Character)]
      [r: listAppend(OutputText, token.name + " " + getProperty("Constitution"))]
   }]
If I write this it just ignores the variable OutputText and treat it like a normal text, getting exactly ""Some characters were concentrating" + OutputText"

Code: Select all

[dialog("WARNING"): {
   "Some characters were concentrating" + OutputText
}]
while if I write this it outputs the correct data in the chat, letting me know that the variable is saved and accessible

Code: Select all

[r: OutputText]
what am I doing wrong?
Last edited by AriesBreath on Wed Nov 20, 2019 8:13 am, edited 1 time in total.

User avatar
wolph42
Deity
Posts: 9794
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: Creating a list from selected tokens following conditions

Post by wolph42 »

AMs code lacked the initialization that you have, which rendered the error:

Code: Select all

[h: concentrateList = "[]"]
in your case: the dialog statement, just shows: "Some characters were concentrating" and nothing else OR nothing at all?

User avatar
aliasmask
Deity
Posts: 8611
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Creating a list from selected tokens following conditions

Post by aliasmask »

If you're building a list, you need to assign the values to it. ie list = list + newItem which was the problem with the first macro and now the 2nd macro.

ie

Code: Select all

      [r: listAppend(OutputText, token.name + " " + getProperty("Constitution"))]
should be

Code: Select all

      [r: OutputText = listAppend(OutputText, token.name + " " + getProperty("Constitution"))]
I also fix my first macro by defining concentrateList and initializing it to blank. I put in the code to build the list, but didn't use it in the code segment, so I forgot about it.

User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Re: Creating a list from selected tokens following conditions

Post by AriesBreath »

wolph42 wrote:
Wed Nov 20, 2019 7:33 am
in your case: the dialog statement, just shows: "Some characters were concentrating" and nothing else OR nothing at all?
It shows "Some characters were concentrating" + OutputText. I corrected it in the last message to be more understandable.
aliasmask wrote:
Wed Nov 20, 2019 7:36 am
If you're building a list, you need to assign the values to it
I don't think I understood. I think the macro fills the list correctly, demonstrated by the fact that if I write "[r: OutputText]" it shows in chat the data collected BUT it doesn't only in the Dialog.
aliasmask wrote:
Wed Nov 20, 2019 7:36 am
should be
CODE: SELECT ALL

[r: OutputText = listAppend(OutputText, token.name + " " + getProperty("Constitution"))]
When I tried this method to create the list it didn't fill it correctly, let's say, instead of getting a list like (1,2,3) I got (1,1,2,1,2,3)

User avatar
BearFather
Cave Troll
Posts: 45
Joined: Tue Jan 07, 2014 11:46 pm
Location: Inside Your Head! <USA>
Contact:

Re: Creating a list from selected tokens following conditions

Post by BearFather »

I'm still learning myself but I thing you want to do something like this...

Code: Select all

[dialog("WARNING"): {
   "Some characters were concentrating" + [r:OutputText]
}]

User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Re: Creating a list from selected tokens following conditions

Post by AriesBreath »

This way the feedback is just
""Some characters were concentrating" +"

For the sake of context I'll write here the whole code, I know it is long but maybe it can help.

Code: Select all

<!-- Create Menu -->
[h:status = input(
"hpChange|0|Number of Hit Points",
"Operation|Damage,Healing,Kill,Refull|Is the character taking damage or being healed?|RADIO|SELECT=0",
"ShowDead|Yes,No|Alternate the Dead status on tokens|RADIO|SELECT=0")]
[h:abort(status)]
[h: hpChange = eval(string(hpChange))]

<!-- Get Tokens -->
[h: TokenIDs = getSelected()]

<!-- Create an empty Concentrating List and Output Text -->
[h: ConcentratingList = ("")]
[h: OutputText = ("")]

<!-- Fill the Concentrating List -->
[h, foreach(Character, TokenIDs), CODE:
   {
      [h: switchToken(Character)]
      
      [h, if(state.Concentrating == 1),CODE:
     {
       [h: ConcentratingList = json.append(ConcentratingList, Character)]
       }]
}]

<!-- Parse operations switching in Characters and editing its properties -->
[if(Operation == 0),CODE:
{
   [h, foreach(Character, TokenIDs), CODE:
   {
      [h: switchToken(Character)]
      [h: HP = HP - hpChange]
   }]

   <!-- Fill Output Text -->
   [r, foreach(Character, ConcentratingList), CODE:
   {
      [h: switchToken(Character)]
      [r: listAppend(OutputText, token.name + " " + getProperty("Constitution"))]      
   }]
}]

[if(Operation == 1),CODE:
{
   [h, foreach(Character, TokenIDs), CODE:
   {
      [h: switchToken(Character)]
      [h:HP = min(HP+hpChange, MaxHP)]
    }]
}]

[if(Operation == 2),CODE:
{
   [h, foreach(Character, TokenIDs), CODE:
   {
      [h: switchToken(Character)]
      [h:HP = 0)]
    }]
}]

[if(Operation == 3),CODE:
{
   [h, foreach(Character, TokenIDs), CODE:
   {
      [h: switchToken(Character)]
      [h:HP = MaxHP)]
    }]
}]


<!-- After the damage or healing has been done, this sets health bar and dead state  -->
[g, foreach(Character, TokenIDs), CODE:
{
<!-- Sets Health Bar progression -->
   [h: switchToken(Character)]
   [h:bar.Health = HP / MaxHP]
   [g: token.name + " took " + hpChange + " damage"]
   <!-- Sets Dead State if allowed AND if HP are <= 0 -->
   [if(ShowDead == 0),CODE:
     {[h: state.Dead = if(HP <= 0, 1, 0)]}]

   <!-- If a Character is dead set it to Object Layer, otherwise take it back to Token Layer -->  
   [if(state.Dead == 0),CODE:
     {[h:layername = "Token"]
      [h:setLayer(layername)]}]

   [if(state.Dead == 1),CODE:
     {[h:layername = "Object"]
      [h:setLayer(layername)]}]
}]

<!-- Check if the Output Text has been saved -->
[r: OutputText]

<!-- Create a Pop Up Dialog with names and concentration saves of the damaged character -->
[dialog("WARNING"): {
   "Some characters were concentrating" + [r:OutputText]
}]

User avatar
aliasmask
Deity
Posts: 8611
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Creating a list from selected tokens following conditions

Post by aliasmask »

See if this works for you. There is no chat output but a popup will show all the hp changes.

Code: Select all

<!-- Create Menu -->
[h: abort(input(
   "hpChange|0|Number of Hit Points",
   "Operation|Damage,Healing,Kill,Refull|Is the character taking damage or being healed?|RADIO|SELECT=0",
   "ShowDead|Yes,No|Alternate the Dead status on tokens|RADIO|SELECT=0"
))]

[h: hpChange = eval(string(hpChange))]

<!-- Get Tokens -->
[h: TokenIDs = getSelected()]

<!-- Create an empty Concentrating List and Output Text -->
[h: gmOutput = ""]

<!-- Parse operations switching in Characters and editing its properties -->
[h, if(Operation == 0), CODE: {
   [h: ConcentratingList = ("")]
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = HP - hpChange]
      [h, if(state.Concentrating): gmOutput = listAppend(gmOutput, strformat("%{token.name} (%s) is Concentrating.",getProperty("Constitution")),"<br>")]
      [h: gmOutput = listAppend(gmOutput,token.name + " took " + hpChange + " damage","<br>")]
   }]
};{}]

[h, if(Operation == 1), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = min(HP+hpChange, MaxHP)]
      [h: gmOutput = listAppend(gmOutput,token.name + " healed " + hpChange + " damage","<br>")]
   }]
};{}]

[h, if(Operation == 2), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = 0)]
      [h: gmOutput = listAppend(gmOutput,token.name + " is killed.","<br>")]
   }]
};{}]

[h, if(Operation == 3), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = MaxHP)]
      [h: gmOutput = listAppend(gmOutput,token.name + " is restored to full.","<br>")]
   }]
};{}]

<!-- After the damage or healing has been done, this sets health bar and dead state  -->
[h, foreach(Character, TokenIDs), CODE: {
   <!-- Sets Health Bar progression -->
   [h: switchToken(Character)]
   [h: bar.Health = HP / MaxHP]
   
   <!-- Sets Dead State if allowed AND if HP are <= 0 -->
   [h, if(ShowDead == 0): state.Dead = if(HP <= 0, 1, 0)]

   <!-- If a Character is dead set it to Object Layer, otherwise take it back to Token Layer -->  
   [h, if(state.Dead == 0): setLayer("Token"); setLayer("Object")]
}]

<!-- Create a Pop Up Dialog with names and concentration saves of the damaged character -->
[dialog("WARNING"): {
   [r: gmOutput]
}]

User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Re: Creating a list from selected tokens following conditions

Post by AriesBreath »

I'm sorry but it still doesn't work, it refuse to execute the commands and sends this to the chat

Code: Select all


java.util.UnknownFormatConversionException: Conversion = '{' error executing expression gmOutput = listAppend(gmOutput, strformat("%{token.name (%s) is Concentrating.",getProperty("Constitution"))," ").
In the final piece you coded "[r: gmOutput]" that I suppose would have to be converted in the data that are stored inside, but what's the difference with "[r:OutputText]"? The way that the data is stored?

Phergus
Deity
Posts: 7132
Joined: Fri May 12, 2006 8:56 pm
Location: Middle of Nowhere, NM
Contact:

Re: Creating a list from selected tokens following conditions

Post by Phergus »

Line 22 in AM's macro is missing a right curly brace after token name:

Code: Select all

[h, if(state.Concentrating): gmOutput = listAppend(gmOutput, strformat("%{token.name (%s) is Concentrating.",getProperty("Constitution")),"<br>")]
Change it to:

Code: Select all

[h, if(state.Concentrating): gmOutput = listAppend(gmOutput, strformat("%{token.name} (%s) is Concentrating.",getProperty("Constitution")),"<br>")]
And it works fine.

In [r: gmOutput] and [r: OutputText], the names used are just variable names. They don't have any meaning beyond what you give to them. Could be just X. AM chose to use gmOutput and if you look at the code you'll see that's what he used in each line to build up his output string.

User avatar
aliasmask
Deity
Posts: 8611
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Creating a list from selected tokens following conditions

Post by aliasmask »

Whoops. Fixed in OP.

User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Re: Creating a list from selected tokens following conditions

Post by AriesBreath »

Phergus wrote:
Wed Nov 20, 2019 6:09 pm
In [r: gmOutput] and [r: OutputText], the names used are just variable names. They don't have any meaning beyond what you give to them.
That's what I was trying to say, the difference is in the way the data are stored.
aliasmask wrote:
Wed Nov 20, 2019 1:58 pm
See if this works for you. There is no chat output but a popup will show all the hp changes.
Many thanks, it works! I studied your code trying to understand how it works and why. I adapted it to better suit some of my needs, and then I realized it probably was the wrong system to get what I was trying to reach. I scavenged some other code, studied it and readapted with yours and mine, giving life to this Frankenstein monster. I'm sure that it's not optimized nor it's the best way to get everything done, but it works fine in every aspect (for what I tested), except that the final Dialog formatting isn't completely correct as it puts some commas between the rows but, since it works and I don't know how to program, I think I am satisfied.

Code: Select all

<!-- Get Tokens, create ExecPermission, Concentrating List and Result Output  -->
[h: TokenIDs = getSelected()]
[h: ExecPermission = 1]
[h: ConcentratingList = ("")]
[h: ResultOutput = ""]

<!-- Checks if the tokens are ready for the macro -->
[h, foreach(Character, TokenIDs), CODE: {
	[h: switchToken(Character)]
	[h, if(isNumber(HP)):ExecPermission=ExecPermission; ExecPermission = 0]
	[h, if(isNumber(MaxHP)):ExecPermission=ExecPermission; ExecPermission = 0]
}]

<!-- Abort the macro if a token is not ready for its execution -->
[h, if(ExecPermission == 0), CODE: {
	[dialog("Macro Abort"): {
		"Some characters are missing some properties. Check for HP and MaxHP"
	}]
	[h: abort(0)]
}]

<!-- Create Menu -->
[h: abort(input(
   "hpChange|0|Number of Hit Points",
   "Operation|Damage,Healing,Kill,Refull|Is the character taking damage or being healed?|RADIO|SELECT=0",
   "ShowDead|Yes,No|Alternate the Dead status on tokens|RADIO|SELECT=0"
))]
<!-- Evaluate and floor the expression -->
[h: hpChange = floor(eval(string(hpChange)))]

<!-- Parse operations switching in Characters and editing its properties -->
[g, if(Operation == 0), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = HP - hpChange]
      <!-- Puts concentrating characters in ConcentratingList -->
      [h, if(state.Concentrating): ConcentratingList = listappend(ConcentratingList, Character)]
      <!-- Tells GM who took damage -->
      [h: ResultOutput = listAppend(ResultOutput, token.name)]
   }]
   [g: ResultOutput + " <b>suffer " + hpChange + " damage</b>"]
}]

[g, if(Operation == 1), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = min(HP+hpChange, MaxHP)]
      [h: ResultOutput = listAppend(ResultOutput, token.name)]
   }]
   [g: ResultOutput + " <b>healed " + hpChange + " HP</b>"]
}]

[g, if(Operation == 2), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = 0)]
      [h: ResultOutput = listAppend(ResultOutput, token.name)]
   }]
   [g: ResultOutput + " is/are killed</b>"]
}]

[g, if(Operation == 3), CODE: {
   [h, foreach(Character, TokenIDs), CODE: {
      [h: switchToken(Character)]
      [h: HP = MaxHP)]
      [h: ResultOutput = listAppend(ResultOutput, token.name)]
   }]
   [g: ResultOutput + " is/are completely restored</b>"]
}]

<!-- After the damage or healing has been done, this sets health bar and dead state  -->
[h, foreach(Character, TokenIDs), CODE: {
   <!-- Sets Health Bar progression -->
   [h: switchToken(Character)]
   [h: bar.Health = HP / MaxHP]
   
   <!-- Sets Dead State if allowed AND if HP are <= 0 -->
   [h, if(ShowDead == 0): state.Dead = if(HP <= 0, 1, 0)]

   <!-- If a Character is dead set it to Object Layer, otherwise take it back to Token Layer -->  
   [h, if(state.Dead == 0): setLayer("Token"); setLayer("Object")]
}]

<!-- Create a Pop Up Dialog with names and concentration modifiers and saves of the damaged character -->
[dialog("Concentration Result"): {
	[h: ConSTCD = if(floor((hpChange)/2) > 10, floor((hpChange)/2), 10)]		
	[r: "Some characters were concentrating: they will keep the concentration if they succed a <b>CD " + ConSTCD + " Constitution Saving Throw.</b><br>"]
  <html>
    <head>
      <style> [R: "
      .oddRow { background-color: #FFFFFF }
      .evenRow { background-color: #EEEEAA }
      #stats th { background-color: #113311; color: #FFFFFF }"
      ]
      </style>
      <title>Character Sheet</title>
    </head>
    <body>
      <table id="stats">
        <tr>
          <th>Character Name</th>
          <th>Constitution S.T. Modifier</th>
          <th>Final Saving Throw</th>
          <th>Save Result</th>
        </tr>
        [h: class = "oddRow"]
        [foreach(Character, ConcentratingList), code: {
        	[h: switchToken(Character)]
        	[h: ConSTMod = getProperty("Saving Throw - Constitution")]
        	[h: ConSTFinal = 1d20+ConSTMod]
            <tr class="[r:class]">
              <td>[r: token.name]</td>
              <td>[r: "+" + if(isNumber(ConSTMod), ConSTMod, 0)ConSTMod]</td>
              <td>[r: ConSTFinal]</td>
              <td>[r: if(ConSTFinal >= ConSTCD, "Yes", "No")]</td>
            </tr>
          [h: class = if(class=="oddRow", "evenRow", "oddRow")]
        }]
      </table>
    </body>
  </html>
}]

<!-- Since it's not possible to open the dialog with a condition because of depth calculation limits, this closes the dialog immediatley if there's not need to keep it open -->
[r, if(length(ConcentratingList) == 0): closeDialog("Concentration Result")]
Attachments
Dialog.JPG
Dialog.JPG (29.95 KiB) Viewed 918 times


User avatar
AriesBreath
Cave Troll
Posts: 32
Joined: Thu May 02, 2019 10:37 am

Re: Creating a list from selected tokens following conditions

Post by AriesBreath »

wolph42 wrote:
Thu Nov 21, 2019 4:14 pm
you forgot to set the output seperator:
Nice, it works! Many thanks again

Post Reply

Return to “General Discussion”