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

[Script] [API] [PF] repeating esction macro creation during character import

1453317524

Edited 1454218109
I have some questions about feature feasability for ideas for   Pathfinder Character Importer (Gosh how I hate that threads lock after two months! GRR) Attacks create repeating attacks, and non-template macros (one for to hit and one for damage). I was wondering if it wouldn't be possible to place the repeating attack macros on the token instead. - Currently things like "plus grab" are placed in that normal macro at the end, but they could be placed under weapon notes. However, I've never seen any API create a repeating section and a macro to roll said section. Has someone done something similar before?
I've been working with it for a few weeks now, and the biggest problem I've been having are with the sheetworkers.  The repeating sections require some of the sheetworker fields to calculate in order to roll correctly.  While there are some that are backed, it really seems like you need to recalc in the sheet itself to get the right bonuses for the repeating sections.   I openly wondered if the best importer would actually be in the sheet itself so that it could call all the sheetworker methods to ensure those fields were correct.  The API doesn't seem to be able to call those methods. Lastly, I'll leave you with the option of adding the macro text into the abilities section in roll template form, instead of bothering with the repeating sections.  That was the last direction I was going in, but I couldn't make it work myself.  Good luck to you, I know a GM or two that would love it.  
The current script does actually add macros in the abilities section, so you might want to look into it, if you want to use that for yours. Interesting idea on including the script in the sheet... - is that a possibility? Because it would actually make a whole lot of sense, given that most pathfinder sheet users (gm) would have import something sooner or later...
1453681632
The Aaron
Forum Champion
API Scripter
I've been thinking the same thing about sheet workers. =D
And what were the results of your thinking, Aaron? The smiley looks so optimistic! :)
1453939441
The Aaron
Forum Champion
API Scripter
Well, largely that I'd like to write something into TheAaronSheet to assist with import/export.  I'd need to make some sort of script or online parser that would detect all the editable attributes to make it truly useful.  At a minimum, I could just expect the sheet author to specify the order/grouping/naming.  This is very similar to a current development effort on TheAaronSheet: Migrations.  I need to finish that up, but I'm always looking for where a system can be more general and therefore more useful.
1454002067

Edited 1454002365
chris b.
Pro
Sheet Author
API Scripter
Riley did hint that they might build some API to Sheetworker sync. But until then you have to hit recalc in an open sheet.  having the importer inside the sheet would also make it available to Plus users. not just Pro. You also get the ability to generate new repeating section rows as you pointed out (otherwise, the other workaround for now would be dummy ids the recalc would look for, unfortunately there is no delete attribute in the sheet worker so .. that is just begging to duplicate rows ! :) ) On the other hand: Since they are about to release the compendium i'd say it's almost moot. Once we implement that, then all we need is a Pathfinder statblock to JSON converter that matches the tags... and beg them to let us drag over custom JSON :) And actually, using that drag and drop JSON into the sheet would mandate using the sheetworker to make sure the vales get to the proper attributes. I've been using CharacterGen instead of the one linked to and still do, since it's just easier. and super robust. It doens't update the sheet itself, but I hacked it up some to use the sheet rolltemplates instead of the ones it comes with.  The big issue with importing NPCs/Monsters after you do the parsing is the monster stat blocks are the "final" value already calculated. Trying to reverse engineer the stat block is incredibly difficult  as you go up in CR...  I am thinking we need a checkbox on the config to say "this is a monster do not do (certain) sheetworker calcs to change things.and break it"  and instead of importing base items and calculating mods, update into the final value. .. it's something to think about.  Since Character Generator is already there, could we lift the parsing code? I know he stopped supporting it, but not sure why. I think it's under some non MIT license we'd have to go look, i am not familiar with open source licensing all that much. He also has that great Tracker Jacker  that has exactly the features i need.
1454188219

Edited 1454218082
I was having trouble earlier with the script (it was just doing nothing at all!), I waited a few hours (i.e. got distracted) hit refresh (again) and somehow this time it worked again. Not sure what that was about. Then I imported Reta Bigbad, a goblin fighter and noticed race was displayed under xp, because I hadn't included CR nor XP. I checked the code for race (first time I actually looked at the code in a while) and found that while CR is split at "CR ", XP was just splitting at " " - so it would take anything that came after... When I write 600 XP instead of XP 600 it just shows the words XP in the NPC-XP attribute. I found this&nbsp; <a href="http://www.w3schools.com/jsref/jsref_split.asp" rel="nofollow">http://www.w3schools.com/jsref/jsref_split.asp</a> and it helped me change it so that the code for me now it splits at "XP " instead of " " Somehow working with macros for a while made me more open to reading javascript as well... And while this is tiny, maybe I'm the code monkey I was waiting for... code monkey to be... a code chipmonkey... :D Still, on my&nbsp;next problem I got stuck: NPC-other-gear was also not working for me... Admittedly her gear is really really weird, but when I finally thought to make it simpler, it still didn't work. [Spoiler alert: solution is don't have a line break between combat gear and other gear --- pretty sure I had even stumbled over that before, ages ago, and just forgotten because I didn't understand why.] It took me almost all day to be able to increase my understand what's going on in this code...&nbsp;(half of it spent on code academy getting way too involved coding an interactive dialogue with an orange merchant. *cough*, not sure if that counts) - Eventually I understood that this: var gearStr = findString(data, "Combat Gear", true); if (gearStr != null) { gearStr = gearStr.replace("Combat Gear ",""); gearStr = gearStr.split("Other Gear"); AddAttribute("npc-combat-gear",gearStr[0],charID); AddAttribute("npc-other-gear",gearStr[1],charID); } means that data (which is the gmNotes post all kinds of stripping etc) gets searched for Combat Gear, and then it is "true" that&nbsp;doConcat gets applied (not sure what it does, but eh). - The result is a string called gearStr, from which the substring "Combat Gear &nbsp;" gets deleted, and everything after "Other Gear" gets split into it's own... aehm "piece"(?), which can be referenced with "gearStr[1]" (because it's the second such "piece" in the array that gearStr has now become (right?) Anyway, that's where I was starting to think given the code of data and gmNotes, shouldn't there be a var gearStr and a findString for each? - if not, wouldn't that mean they'd have to be part of the same string, and thus the same line?&nbsp; I didn't realize my stat block was atypical in this particular aspect. low level goblins only have their gear listed under Treasure, while higher &nbsp;level seem to have it in the same line, split by a ";". Mine was from a PDF and had it with a line break...&nbsp; Long story short, problem solved and unnecessary amounts of code were learned in the process... wow. There really should be instead of (or in addition to) the list at the top of all the things it can import, a default format example to show what the expectations of this thing are...&nbsp;
1454196993
Corin S.
Sheet Author
In regards to using sheet workers for importing characters, over the past week I've added code to the Pokémon Tabletop United sheet for just such a purpose. It reads a JSON placed in an input field, turns it into an object, and then reads off that object's keys as attributes and values as values. The implementation's a bit more intricate than that, to facilitate repeating fields, but it's certainly interesting. I'd be glad for anyone who can read my messy code to take a peek, if only for error-checking. The github for it is&nbsp; here , and the sheet it imports from is&nbsp; here .
1454221342

Edited 1454221675
chris b. said: [...] The big issue with importing NPCs/Monsters after you do the parsing is the monster stat blocks are the "final" value already calculated. Trying to reverse engineer the stat block is incredibly difficult &nbsp;as you go up in CR...&nbsp; I am thinking we need a checkbox on the config to say "this is a monster do not do (certain) sheetworker calcs to change things.and break it" &nbsp;and instead of importing base items and calculating mods, update into the final value. .. it's something to think about.&nbsp; Isn't that's a problem inherent to the way stat blocks are presented? i.e. they show the total, and between the various things you can figure out all the numbers manually (i.e. reverse engineer) but it's effortful. Having just the totals is fine for monsters that you're only going to see die in the next combat anyway, but if players mess with the creatures ability to do some things, or more likely my curiosity gets the better of me, I'll have to figure it out. Any news related to the&nbsp;compositum&nbsp;or the copy paste ability it is supposed to get at some point? - last I heard (3 months ago) they weren't currently working on it, but rather focusing on more API stuff, is that still the case? EDIT: Found it&nbsp; <a href="https://app.roll20.net/forum/post/2877778/slug%7D" rel="nofollow">https://app.roll20.net/forum/post/2877778/slug%7D</a>
1454264837

Edited 1454264852
The Aaron
Forum Champion
API Scripter
Corin S. said: In regards to using sheet workers for importing characters, over the past week I've added code to the Pokémon Tabletop United sheet for just such a purpose. It reads a JSON placed in an input field, turns it into an object, and then reads off that object's keys as attributes and values as values. The implementation's a bit more intricate than that, to facilitate repeating fields, but it's certainly interesting. I'd be glad for anyone who can read my messy code to take a peek, if only for error-checking. The github for it is&nbsp; here , and the sheet it imports from is&nbsp; here . One thing I notice is that getRepeatingIDs() is asynchronous (I updated the wiki to make that more apparent): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_moves", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rmoves = idarray; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_capabilities", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rcaps = idarray; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_edges", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; redges = idarray; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_features", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rfeats = idarray; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_abilities", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rabils = idarray; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (rmoves==undefined){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rmoves=[]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (rcaps==undefined){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rcaps=[]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (redges==undefined){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; redges=[]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (rfeats==undefined){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rfeats=[]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (rabils==undefined){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rabils=[]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } That will likely have rabils, rmoves, etc as empty arrays as the if checks will fire before the async calls are done. &nbsp;You probably need something more along the lines of: getSectionIDs("repeating_moves", function(idarray) { &nbsp; rmoves = idarray; &nbsp; getSectionIDs("repeating_capabilities", function(idarray) { &nbsp; &nbsp; rcaps = idarray; &nbsp; &nbsp; getSectionIDs("repeating_edges", function(idarray) { &nbsp; &nbsp; &nbsp; redges = idarray; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_features", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; rfeats = idarray; &nbsp; &nbsp; &nbsp; &nbsp; getSectionIDs("repeating_abilities", function(idarray) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rabils = idarray; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* rest of the code here */ &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; }); &nbsp; }); }); You might also be interested in this. &nbsp;Anywhere you have this construct: for (var property in char) { if (char.hasOwnProperty(property)) { /* do something with property */ } } you can use this instead: _.each(char,function(value,property){ /* do something with property and value */ }); which tends to be a bit more readable and saves all the ifs. Something else to consider: if (property.match(/Move\d/g)!=null) { i = Number(property.substring(4)); /* more stuff */ } could be written as: if (matches = property.match(/Move(\d+)/)) { i = parseInt(matches[1],10); /* more stuff */ } Not much different, but you don't need to know the offset to the numbers that way, and trailing characters aren't picked up. That lets you copy the same block and only change the regular expression. &nbsp;(Also, the /g isn't needed there. Neither is the !=null, as the result will be truthy if there was a match and falsy otherwise.) &nbsp;The regular expression could be written as /^Move(\d+)$/ (or possibly /^\s*Move(\d+)\s*$/ to ignore whitespace) if you want to be sure that's the only thing in the property name. &nbsp;Also, the parseInt() is more idiomatic than using the Number constructor. String concatenation can be done with the + operator (which has it's own problems to be sure, but you might like the syntax better: x = "repeating_edges_".concat(i).concat("_Edge").concat(prop); could be: x = "repeating_edges_"+i+"_Edge"+prop; Hope that helps!
1454283154
Corin S.
Sheet Author
Thank you, oh wise Scriptomancer!