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] Rolling using Character Attributes

I've been working on a character sheet for a little while, and finished getting help on finishing it up. With that done, I'm moving onto automating rolls using the attributes on the character sheets. I have a roll all worked out, and tested it to make sure that it works in chat: [[ [[floor(([[d21]]+79)/100*(@{selected|r_p_atk}-@{target|p_def}))]] * [[ [[@{selected|max_hits}]]d100>[[100 - (@{selected|aim} - @{target|eva})]] ]] ]] It basically takes a random value (80%-100%) and difference between the p_atk and p_def to calculate the damage, and then checks how many hits there were against the chance to miss.  I'm trying to run a script to roll this, but I've run into a few issues. //Auto Attack on("chat:message", function(msg) {   if(msg.type == "api" && msg.content.indexOf("!attack ") !== -1) {     var user = msg.who;     var target = msg.content.replace("!attack ", "");         sendChat(msg.who, "[[ [[floor(([[d21]]+79)/100*(@{"+user+"|r_p_atk}-@{"+target+"|p_def}))]] * [[ [[@{"+user+"|max_hits}]]d100>[[100 - (@{"+user+"|aim} - @{"+target+"|eva})]] ]] ]]"); } }); First issue, the roll doesn't work, even though the format is basically the same. It gets held up in the API with an error: "SyntaxError: Expected "[" or [ |\t] but "1" found. undefined". I'm trying to get the roll to work as a macro like this: !attack {target|character_name} Using the typer's name as the character name, and the target. Unless there's an easier way to do this. Second issue is that I realized that I need to check some of the attribute values before rolling: (@{selected|r_p_atk}-@{target|p_def}) to see if it's less than 0; since that would do 0 damage (@{selected|aim} - @{target|eva}) to see if it's greater than 100; since then none of the hits can miss. I'll also need to check if @{selected|l_p_atk} > 0; to see if a second weapon is being used to attack, and then roll the damage for that I'm getting close, but I think I need to get the values for these attributes so I can use them before rolling Thank you in advance, I know I've been asking for a lot of help lately, but this is basically the only html/java work I've done since high-school :x
1473361257
The Aaron
Pro
API Scripter
For the first issue, it's a matter of spacing. &nbsp;The inline roll interpreter for the API's sendChat() has a bug with spaces. &nbsp;I detailed it out fully in this bug report: &nbsp; <a href="https://app.roll20.net/forum/post/2217884/api-regr" rel="nofollow">https://app.roll20.net/forum/post/2217884/api-regr</a>... Where in your equation that's happening is hard to discern, but hopefully you can track it down by looking at the examples and adjusting where spaces are around the [[ and ]] For the second, there are two ways to get attributes: •&nbsp; getAttrByName() -- is a function for getting the value (either current or max) of an attribute. &nbsp;This is convenient if you just want to have the number and don't need to manipulate the attribute at all. •&nbsp; getObj() / findObjs() / filterObjs() -- are functions you can use to get the&nbsp; attribute object . This is better if you need to make changes to the attribute, or use it a bunch of times, etc. Incidentally, @{selected} and @{target} are meaningless once you are in the API. &nbsp;Those are concepts for the UI. &nbsp;The closest you can get is knowing which tokens were selected when an api command was run ( msg.selected is an array of object types and ids for such). &nbsp;Once you're in the API, you either need to use the get/find functions to acquire the&nbsp; character object for for a character you are interested in, or use the msg.selected or an argument to your command to specify it (you'll still need the get/find functions, that just makes it dynamic). In the long run, you might be better off writing a function that loads the character object and all the attribute objects and does all of the computation in Javascript using&nbsp; randomInteger() for the dice rolls (this is probably how I would do this). &nbsp;The only time you need to use sendChat() to evaluate is when the attribute contains a formula, rather than just numbers.
Thank you for the reply, again. I was fairly sure that I would need to use a function like that. The only issue is that I guess I don't know how to use the, correctly at least. I've been spinning my wheels for a while, and I don't know what I'm doing wrong. on("chat:message", function(msg) { &nbsp; if(msg.type == "api" && msg.content.indexOf("!str ") !== -1) { &nbsp; &nbsp; var user = msg.who; &nbsp; &nbsp; &nbsp; &nbsp; var userTokens = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; name: user &nbsp; &nbsp; &nbsp; &nbsp; }, {caseInsensitive: true}); &nbsp; &nbsp; var target = msg.content.replace("!attack ", ""); &nbsp; &nbsp; &nbsp; &nbsp; var targetTokens = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; name: target &nbsp; &nbsp; &nbsp; &nbsp; }, {caseInsensitive: true}); &nbsp; &nbsp; sendChat(msg.who, userTokens.get("str")); &nbsp; &nbsp; sendChat(msg.who, targetTokens.get("str")); &nbsp; } }); Just was trying to get used to getting the characters I need, to see if I can send messages to chat, returning the attribute. This gives me the error " SyntaxError: Unexpected token } " when I try to save the script. I tried looking at some of the APIs that are out there, to compare, but most of them were either way over my head, or weren't trying to get attribute information.
1473386224

Edited 1473386421
The Aaron
Pro
API Scripter
Here's a quick script that should demonstrate some of the relationships of the various objects and how to handle them. &nbsp;I tried to comment it thoroughly, but let me know if you want me to expand on any of it: // the 'ready' event occurs once the API is fully loaded on('ready',function(){ &nbsp; &nbsp; "use strict"; &nbsp; &nbsp; // Register a handler for the chat message event &nbsp; &nbsp; on('chat:message', function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; // skip message that are not api commands (rolls, emotes, chat, etc) &nbsp; &nbsp; &nbsp; &nbsp; if('api' !== msg.type) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; // split the command into parts at contiguous whitespace &nbsp; &nbsp; &nbsp; &nbsp; let cmd = msg.content.split(/\s+/); &nbsp; &nbsp; &nbsp; &nbsp; // handle commands for this script &nbsp; &nbsp; &nbsp; &nbsp; switch(cmd[0]) { &nbsp;// the first part will be the command &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!str': // if the command was !str &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // get the player object -- this will be just an object &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let player = getObj('player',msg.playerid); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // find all the characters the player controls -- this will be an array &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let charsForPlayer = filterObjs(function(o){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // object is a character &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return o.get('type') === 'character' &&&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // object has the player in the list of controlled by or all &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; &nbsp; _.contains(o.get('controlledby').split(/,/), msg.playerid) || &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o.get('controlledby').match(/\ball\b/) &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; &nbsp; &nbsp; // log the player's name and a note &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log(player.get('displayname') + ' can control the following characters: '); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // for each of the characters in the array... &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.each(charsForPlayer,function(c){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // ...show it's name &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log('-- ' + c.get('name')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // try to get the 'str' attribute for that character &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let strAttr = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'attribute', &nbsp;// object is an Attribute &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: 'str', &nbsp; &nbsp; &nbsp; &nbsp;// attribute is named 'str' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: c.id &nbsp; // attribute belongs to the character &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })[0]; // grab only the first one in the array that findObjs() returns &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // if the attribute exists (strAttr isn't undefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(strAttr){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // mention that it exists &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log(' &nbsp; -- Has attribute "str" with value '+strAttr.get('current')+'/'+strAttr.get('max')); &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; // done with '!str' case &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); If you execute !str from the chat, you should see some output in the API console &nbsp;(log() outputs there, which is lighter weight than dumping things to chat). If the player running the command controls any characters (or any characters are set to 'all'), then it should log a list of all of those characters and also the 'str' attribute of each, if it exists. "The Aaron can control the following characters: " "-- Kobold" "-- [P] The Mayfield" "-- Foo" "-- Spy" "-- Rook" " &nbsp; -- Has attribute \"str\" with value taco/meal" "-- Ragrao Stratowo" "-- Evard's Black Tentacles (20'x20')"
1473462702

Edited 1473462793
Thank you for the example. I was working with the example you gave me, trying to see how it worked, and how to use parts of it. Unfortunately I'm running into issues get in the attribute's value // the 'ready' event occurs once the API is fully loaded on('ready',function(){ &nbsp; &nbsp; "use strict"; &nbsp; &nbsp; // Register a handler for the chat message event &nbsp; &nbsp; on('chat:message', function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; // skip message that are not api commands (rolls, emotes, chat, etc) &nbsp; &nbsp; &nbsp; &nbsp; if('api' !== msg.type) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // get the player object -- this will be just an object &nbsp; &nbsp; &nbsp; &nbsp; let user = findObjs( {_type: 'character'},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {name: msg.who}, &nbsp; &nbsp; &nbsp; &nbsp; {caseInsensitive: true}); &nbsp; &nbsp; &nbsp; &nbsp; let max_hits = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'attribute', &nbsp;// object is an Attribute &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: 'max_hits', &nbsp; &nbsp; &nbsp; &nbsp;// attribute is named 'str' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: user.id &nbsp; // attribute belongs to the character &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let max_hitsVal = max_hits.get('current') &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //dummy values &nbsp; &nbsp; &nbsp; &nbsp; let r_p_atkVal = 34 &nbsp; &nbsp; &nbsp; &nbsp; let l_p_atkVal = 34 &nbsp; &nbsp; &nbsp; &nbsp; let p_defVal = 5 &nbsp; &nbsp; &nbsp; &nbsp; let aimVal = 102 &nbsp; &nbsp; &nbsp; &nbsp; let evaVal = 10 &nbsp; &nbsp; &nbsp; &nbsp; //let max_hitsVal = 4 &nbsp; &nbsp; &nbsp; &nbsp; // split the command into parts at contiguous whitespace &nbsp; &nbsp; &nbsp; &nbsp; let cmd = msg.content.split(/\s+/); &nbsp; &nbsp; &nbsp; &nbsp; // handle commands for this script &nbsp; &nbsp; &nbsp; &nbsp; switch(cmd[0]) { &nbsp;// the first part will be the command &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!atk': // if the command was !atk &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let random = (randomInteger(21)+79)/100 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let hitsVal = 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (let hit=1; hit&lt;=max_hitsVal; hit++) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (randomInteger(100) &gt; (100 - aimVal + evaVal) ) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;hitsVal = hitsVal + 1; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let r_damage = Math.round( random * (r_p_atkVal - p_defVal) * hitsVal ) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // output hits and damage &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat(msg.who, "/me attacks and lands " + hitsVal + ' Hits with their main weapon, dealing ' + r_damage + ' Damage!'); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (l_p_atkVal &gt; 0) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let random = (randomInteger(21)+79)/100 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let hitsVal = 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (let hit=1; hit&lt;=max_hitsVal; hit++) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (randomInteger(100) &gt; (100 - aimVal + evaVal) ) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hitsVal = hitsVal + 1; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let l_damage = Math.round( random * (l_p_atkVal - p_defVal) * hitsVal ) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat(msg.who, "/me attacks and lands " + hitsVal + ' Hits with their left weapon, dealing ' + l_damage + ' Damage!'); &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; &nbsp; // done with '!atk' case &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); I am trying to use 'let max_hitsVal = max_hits.get('current')' to get me the value for the Attribute 'max_hits' &nbsp;which is giving the the error: TypeError: max_hits.get is not a function TypeError: max_hits.get is not a function at apiscript.js:89:36 at eval (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:105:34), &lt;anonymous&gt;:65:16) at Object.publish (eval at &lt;anonymous&gt; (/home/node/d20-api-server/api.js:105:34), &lt;anonymous&gt;:70:8) at /home/node/d20-api-server/api.js:1200:12 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:111:400 It was the same for the other wattributes too. I'm guessing I'm not using .get() correctly. Secondly, I was wondering about the usage of 'switch(cmd[0])'. If I were to have the API command formatted like: !{command} {target}, would I be able to get the attribute values for both the user and target so I can then do the calculations and rolls after? Maybe go with&nbsp;switch(cmd[1]) first to evaluate the target? The last part of the code was there for me to test if I could perform the calculations and output them to chat. That works, yay.
1473474223
The Aaron
Pro
API Scripter
The first findObjs() here will find all the objects of type character, the {name: msg.who} ends up being passed as the second parameter to the function, not as a part of the criteria to find characters: &nbsp; &nbsp; &nbsp; &nbsp; // get the player object -- this will be just an object &nbsp; &nbsp; &nbsp; &nbsp; let user = findObjs( {_type: 'character'},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {name: msg.who}, &nbsp; &nbsp; &nbsp; &nbsp; {caseInsensitive: true}); You'd need to write it like this if you want characters with the same name as the currently talking as selection: &nbsp; &nbsp; &nbsp; &nbsp; // get the player object -- this will be just an object &nbsp; &nbsp; &nbsp; &nbsp; let user = findObjs( { _type: 'character',&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: msg.who }, &nbsp; &nbsp; &nbsp; &nbsp; {caseInsensitive: true}); Assuming that found a character then this code would be searching for all the attributes named max_hits where characterid is undefined . &nbsp; &nbsp; &nbsp; &nbsp; let max_hits = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'attribute', &nbsp;// object is an Attribute &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: 'max_hits', &nbsp; &nbsp; &nbsp; &nbsp;// attribute is named 'str' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: user.id &nbsp; // attribute belongs to the character &nbsp; &nbsp; &nbsp; &nbsp; }); The reason is that findObjs() returns an array, not an object. &nbsp;The array does not have a field .id , so user.id would be undefined . &nbsp;To get around this, you'd probably want to use the first index of the array instead: &nbsp; &nbsp; &nbsp; &nbsp; let max_hits = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'attribute', &nbsp;// object is an Attribute &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: 'max_hits', &nbsp; &nbsp; &nbsp; &nbsp;// attribute is named 'str' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: user [0] .id &nbsp; // attribute belongs to the character &nbsp; &nbsp; &nbsp; &nbsp; }); similarly, if findObjs() found the 'max_hits' attribute, then accessing the .get property will also not do what you expect. &nbsp;Usually, array doesn't have a .get property, but in this case it does (no idea what it is), but it isn't a function, so attempting to invoke it with 'current' causes an exception &nbsp; &nbsp; &nbsp; &nbsp; let max_hitsVal = max_hits.get('current') What you'd need to do is access the first index of the array and call get on that: &nbsp; &nbsp; &nbsp; &nbsp; let max_hitsVal = max_hits [0] .get('current') However, that makes the assumption that there was such an attribute. &nbsp;What you would really want to do is check the length of the array (or at least the index you'll be using) to be certain there actually is something there. &nbsp;Putting it all together you'll have something like this: &nbsp; &nbsp; &nbsp; &nbsp; // get the player object -- this will be just an object &nbsp; &nbsp; &nbsp; &nbsp; let user = findObjs( { _type: 'character', &nbsp; &nbsp; &nbsp; &nbsp; name: msg.who }, &nbsp; &nbsp; &nbsp; &nbsp; {caseInsensitive: true}); if(user[0]){ &nbsp; &nbsp; &nbsp; &nbsp; let max_hits = findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type: 'attribute', &nbsp;// object is an Attribute &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: 'max_hits', &nbsp; &nbsp; &nbsp; &nbsp;// attribute is named 'str' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid: user [0] .id &nbsp; // attribute belongs to the character &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(max_hits[0]) { &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let max_hitsVal = max_hits [0] .get('current'); } } msg.who might not be the name of a character, so it's usually better to either start with a player and find the characters they can access, or pass in the id of a character as part of the command: !atk @{selected|character_id} If you did the above, you would be able to access the second argument just as you've surmised: &nbsp; &nbsp; &nbsp; &nbsp; // split the command into parts at contiguous whitespace &nbsp; &nbsp; &nbsp; &nbsp; let cmd = msg.content.split(/\s+/); &nbsp; &nbsp; &nbsp; &nbsp; // handle commands for this script &nbsp; &nbsp; &nbsp; &nbsp; switch(cmd[0]) { &nbsp;// the first part will be the command &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!atk': // if the command was !atk let character = getObj('character',cmd[1]); if(character){ log('Found the character: '+character.get('name')); } break; } /\s+/ is a regular expression, a special series of characters that specifies how to match certain parts of a string. &nbsp;Regular expressions are specified with / at the beginning and end. &nbsp;\s matches any type of whitespace, so spaces or tabs. &nbsp;+ modifies the prior match (\s in this case) to mean 1 or more of something. &nbsp;so /\s+/ will match any sequence of one or more spaces and tabs. Let me know if you have any more questions!
1473478512

Edited 1473478896
Ah, so that's why. Glad to see I was getting close. I just finished passing all of the attributes into the API that I'll need, so I think I actually have everything I need to complete the commands. The outputs to the chat are all working, and the calculations are working. Thank you so much for your help. (fun fact, the most difficult calculation is for the auto attack, for some reason)
1473479150
The Aaron
Pro
API Scripter
Great! &nbsp;I'm here to help if you need me!