[aliasmask] Coding Tidbits

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

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

Re: [aliasmask] Coding Tidbits

Post by aliasmask »

davout wrote:
aliasmask wrote:Are you putting the objects in the array because you want to define the order, or will the objects have multiple keys? I'm thinking a simple object is good enough for key : result type table. Adding the extra array actually complicates it more and adds to execution speed when passing nested structures.


Because the data I'm working with is an array of objects. So rather than transforming my data everywhere I want display in chat as a table, I moved the logic here.

If there's a more efficient way, I'm all ears.

Well, it kind of depends on your data. The simplest way to convert a json array of objects to a single json object is to use regex removing all {}'s and []'s.

Code: Select all

[H: newObj = "{"+regex(oldObj,"[{}[]|]","")+"}"]

But if you have {}'s and []'s in your data, that won't work. You would resort to a loop to build the new object.

I was going to rewrite this to make it a bit more generic, but allow for style options. As is, this can't be posted to a chat window because of the style. I have another function that adds css styles and replaces class identifiers. For things like table or tr the tag will have to look like this <table class="table">. This basically allows the same code to either use a style sheet or not and since chat doesn't allow style sheets, this function works well for that.

So, what I propose is a function called tooltipResultTable where all the values of a json object are summed and totaled at the end. It will have a threshold option for failure or success with simple formatting (result in red for below, black for match, blue for above) and a text style option for the generic class names throughout the table. Callback just seems unrelated to text formatting and shouldn't be a part of function. Given a json object and values for the styles. This will replace the classes in the text, otherwise a style sheet can be used outside of the formatting of table either as a style sheet or an attached style tag.

Something like this:
tooltipResultTable(jObject[, threshold[, jStyles]]): formattedTooltip

I can also make this share the scope and define tooltipResultTable.value, although that's kind of hacky IMO. Best to be explicit in what values are returned. I'm sure someone has written this before, but having json.sum function separate is another good function. This would take either an array or object and sum all the values. I'll probably write that up too :) It may be double duty to sum it twice, but one function is for text formatting and the other to return a value.

The reason I think it's best to pass a simple json instead of an array of json objects is because of function speed. The more complex the object, the slower things get. Also, other than your needs because of existing data, there's no good reason to do this unless you're going to have separators in the table, like multiple headers and separated sums. But you can get around that too if you just check the json object's value. If text, then use as a header, which I may do as well. For purposes of styles, it would be header1, header2, ... because it would need to be dynamic. So a potential object could be:

Code: Select all

{"synergies":"Synergies","Synergy 1": 2,"Synergy 2": 0,"tempBonuses","Temporary","Temp Bonus 1":4,"Temp Bonus 2":2}
Output wrote:Synergies
Synergy 1 2
Synergy 2 0
Temporary
Temp Bonus 1 4
Temp Bonus 2 2

Json objects maintain their order with simple sets and I believe even with merging so you don't need to put objects in an array to maintain a specific order if that is important to the output. In the above example, it would be possible to return synergies and tempBonuses's sums as well, but I think that may be going outside the scope of a formatting function. Best to use a separate function for that.

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

Re: [aliasmask] Coding Tidbits

Post by aliasmask »

jSum(json): sumTotal

Code: Select all

@@ @jSum
[H: json = arg(0)]
[
H: if(json.type(json) == "OBJECT"), code: {
   [H: json = replace(json,':""',":0")]
   [H: total = eval(strformat("sum(%s)",replace(json,'[{]*"[^"]*":"{0,1}([^,}"]*)"{0,1}(,{0,1})|[}]',"\$1\$2")))]
};{
   [H, if(json.type(json) == "ARRAY"), code: {
      [H: json = replace(json,'(,|\\[)""',"\$10")]
      [H: total = eval(strformat("sum(%s)",json.toList(json)))]
   };{
      [H: total = ""]
   }]
}]
[
H: macro.return = total]

!!
 

What it Does:
  • This function will sum all the values in a json object or array.

How to use:
  • If using onCampaignLoad, no other preparation is needed, otherwise refer to how to use [wroll]macro[/wroll], [wfunc]execLink[/wfunc], [wfunc]macroLink[/wfunc] and/or [wfunc]macroLinkText[/wfunc].
  • Parameters & Return Values
    • json - This is a json object or array where all the values can be evaluated as a number or is blank.
    • sumTotal - The sum of all the values in the json or return "" if not a json.

Other Tips:
  • This is much faster than using a loop to determine the sum of all the values when the number of keys are variable or greater than 2.

Examples:
  • Sum Mods

    Code: Select all

    [H: mods = json.set("{}","a",2,"b","1d6","c","","d","1d4+1")]
    [
    R: am.xx.jSum(mods)] 

Version History:
  • Coded by: aliasmask
  • Updated from oSum to jSum to include summation of arrays. (4/17/12)

<-- Back to Indexing

User avatar
Bone White
Great Wyrm
Posts: 1124
Joined: Tue Aug 23, 2011 11:41 am
Location: Cornwall, UK

Re: [aliasmask] Coding Tidbits

Post by Bone White »

getFacingAngle(tokenIDOfArm, tokenIDOfVertex): angleToFaceToken

Code: Select all

@@ @getFacingAngle
[h: '<!-- arg0 is the tokenID of the first arm, arg1 is the tokenID of the vertex. -->']
[
h: '<!-- uses Bone White`s arcTan UDF -->']
[
h: '<!--  macro.return is the angle you should face the arm token at using setTokenFacing() -->']

[
h: armTokenID = arg(0)]
[
h: vertexTokenID = arg(1)]
[
h, if(getTokenFacing(vertexTokenID) == ""): assert(0,"Vertex token must have a facing",0)]
[
h, if(getTokenFacing(armTokenID) == ""): assert(0,"Arm token must have a facing",0)]
[
h: armTokenX = getTokenX(0, armTokenID)]
[
h: armTokenY = getTokenY(0, armTokenID)]
[
h: vertexTokenX = getTokenX(0, vertexTokenID)]
[
h: vertexTokenY = getTokenY(0, vertexTokenID)]
[
h: xDiff = vertexTokenX - armTokenX]
[
h: yDiff = vertexTokenY - armTokenY]
[
h, if ((yDiff == 0)&&(xDiff == 0)): assert(0,"Vertex token occupies the same grid square as the arm token",0)]
[
h, if (yDiff != 0), code :{
    [h: tanAngle = arcTan(xDiff/yDiff)]
    [h: armTokenAngle = round(toDegrees(tanAngle), 0) + 90]
    };{
    [h, if(xDiff > 0): armTokenAngle = 0]
    [h, if(xDiff < 0): armTokenAngle = 180]
    }
]
[
h, if(yDiff < 0): armTokenAngle = armTokenAngle]
[
h, if(yDiff > 0): armTokenAngle = armTokenAngle + 180]
[
h: macro.return = armTokenAngle]

!!
  


What it Does:
  • Calculates the angle at which to face a token towards the target using [wfunc]setTokenFacing[/wfunc]

How to use:
  • If using onCampaignLoad, no other preparation is needed, otherwise refer to how to use [wroll]macro[/wroll], [wfunc]execLink[/wfunc], [wfunc]macroLink[/wfunc] and/or [wfunc]macroLinkText[/wfunc].
  • Parameters & Return Values
    • tokenIDOfArm - ID of the token to use as the first arm of the angle.
    • tokenIDOfVertex - ID of the token to use as the vertex of the angle.
    • angleToFaceToken - Angle at which to face arm token to face the vertex token using [wfunc]setTokenFacing[/wfunc]

Other Tips:
  • Bone White's arcTan UDF can be found here: Subject: MathLib - SOHCAHTOA [Update 1/8/11]
  • Angle calculated is the red angle "a" in the screenshot below (the east arm is pointing to MapTool's 0-degree angle for facing).
    [spoiler=Explaination Screenshot]
    tokenFacingAngles.PNG
    tokenFacingAngles.PNG (158.82 KiB) Viewed 879 times
    [/spoiler]

Examples:
  • Facing the token with initiative to the selected token.

    Code: Select all

    [h: setTokenFacing(getFacingAngle(getInitiativeToken(),getSelected()),getInitiativeToken())] 

<-- Back to Indexing

User avatar
Bone White
Great Wyrm
Posts: 1124
Joined: Tue Aug 23, 2011 11:41 am
Location: Cornwall, UK

Re: [aliasmask] Coding Tidbits

Post by Bone White »

getFlankAngle(tokenIDOfArm, tokenIDOfVertex): angleOfFlank

Code: Select all

@@ @getFlankAngle
[h: '<!-- arg0 is the tokenID of the first arm, arg1 is the tokenID of the vertex. -->']
[
h: '<!-- uses Bone White`s arcTan UDF -->']
[
h: '<!-- macro.return is the angle at which the first token faces the second.  0 degrees is from behind, 180 degrees is from infront, 90 degrees is to the side -->']

[
h: armTokenID = arg(0)]
[
h: vertexTokenID = arg(1)]
[
h, if(getTokenFacing(vertexTokenID) == ""): assert(0,"Vertex token must have a facing",0)]
[
h, if(getTokenFacing(armTokenID) == ""): assert(0,"Arm token must have a facing",0)]
[
h: armTokenX = getTokenX(0, armTokenID)]
[
h: armTokenY = getTokenY(0, armTokenID)]
[
h: vertexTokenX = getTokenX(0, vertexTokenID)]
[
h: vertexTokenY = getTokenY(0, vertexTokenID)]
[
h: xDiff = vertexTokenX - armTokenX]
[
h: yDiff = vertexTokenY - armTokenY]
[
h, if ((yDiff == 0)&&(xDiff == 0)): assert(0,"Vertex token occupies the same grid square as the arm token",0)]
[
h, if (yDiff != 0), code :{
    [h: tanAngle = arcTan(xDiff/yDiff)]
    [h: armTokenAngle = round(toDegrees(tanAngle), 0) + 90]
    };{
    [h, if(xDiff > 0): armTokenAngle = 0]
    [h, if(xDiff < 0): armTokenAngle = 180]
    }
]
[
h, if(yDiff < 0): armTokenAngle = armTokenAngle]
[
h, if(yDiff > 0): armTokenAngle = armTokenAngle + 180]
[
h: vertexFacing = getTokenFacing(vertexTokenID)]
[
h: flankAngle = 360-vertexFacing + armTokenAngle]
[
h, if(flankAngle >= 360): flankAngle = flankAngle - 360; flankAngle = flankAngle]
[
h, if(flankAngle >= 360): flankAngle = flankAngle - 360; flankAngle = flankAngle]
[
h, if(flankAngle > 180): flankAngle = 180 - (flankAngle - 180); flankAngle = flankAngle]
[
h: macro.return = flankAngle]

!!
   


What it Does:
  • Calculates the angle at which a token is angled from another token's facing to calculate at which angle the first token flanks the second.

How to use:
  • If using onCampaignLoad, no other preparation is needed, otherwise refer to how to use [wroll]macro[/wroll], [wfunc]execLink[/wfunc], [wfunc]macroLink[/wfunc] and/or [wfunc]macroLinkText[/wfunc].
  • Parameters & Return Values
    • tokenIDOfArm - ID of the token to use as the first arm of the angle.
    • tokenIDOfVertex - ID of the token to use as the vertex of the angle.
    • angleOfFlank - Angle at which a token is angled from another token's facing to calculate at which angle the first token flanks the second. 0 degrees means directly behind, 180 degrees means directly ahead.

Other Tips:

Examples:
  • Facing the token with initiative to the selected token.

    Code: Select all

    Angle of Flank: [r: angleOnTarget = getFlankAngle(getInitiativeToken(),getSelected())] 

<-- Back to Indexing

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

Re: [aliasmask] Coding Tidbits

Post by aliasmask »

Update to printProgressBar was made to fix framing size, remove close button and change frame naming convention. The example token and graphic was not updated.

User avatar
Bone White
Great Wyrm
Posts: 1124
Joined: Tue Aug 23, 2011 11:41 am
Location: Cornwall, UK

Re: [aliasmask] Coding Tidbits

Post by Bone White »

autoTokenFace():

Code: Select all

@@ @autoTokenFace
[h"<!-- Adapted from Bone White's getFacingAngle() function -->"]

[
h"<!-- Calculate coordinates of first and last location of the selected token's last path -->"]
[
htoken(getSelected()): pathArray getLastPath()]
[
hpathArrayLength json.length(pathArray)]
[
hfirstCoordinate json.get(pathArray,0)]
[
hsecondCoordinate json.get(pathArray,pathArrayLength-1)]
[
hfirstCoordinateX json.get(firstCoordinate,"x")]
[
hfirstCoordinateY json.get(firstCoordinate,"y")]
[
hsecondCoordinateX json.get(secondCoordinate,"x")]
[
hsecondCoordinateY json.get(secondCoordinate,"y")]

[
h'<!-- Calculate differences between the two coordinates -->']
[
hxDiff secondCoordinateX firstCoordinateX]
[
hyDiff secondCoordinateY firstCoordinateY]
[
h'<!-- If the token has not moved, print this to chat -->']
[
h, if ((yDiff == 0)&&(xDiff == 0)): assert(0,"autoTokenFace: Token has not moved",1)]
[
h'<!-- Extra check to avoid divide by zero -->']
[
h, if (yDiff != 0), code :{
    [
h'<!-- Calculate the angle between the the two coordinates and east using arcTan() -->']
    [
h"<!-- Bone White's arcTan() function -->"]
    [
hxDiff/yDiff]
    [
h999]
    [
h, for (i,10,0,-1), code :
        {
        [
h1]
        [
h+ (z)*(z) / a]
        }
    ]
    [
htanAngle a]
    [
h"<!-- Bone White's arcTan() function -->"]

    [
h'<!-- Convert the result from radians to degrees  -->']
    [
h"<!-- aliasmask's toDegrees() function -->"]
    [
harmTokenAngle round(tanAngle * (180 3.14159265358979323846), 0) + 90]
    [
h"<!-- aliasmask's toDegrees() function -->"]
    };{
    [
h"<!-- If the token didn't move through the y axis, we can't use arcTan() due to divide by zero so set the angle manually  -->"]
    [
h, if(xDiff 0): armTokenAngle 0]
    [
h, if(xDiff 0): armTokenAngle 180]
    }
]
[
h"<!-- If the token didn't move through the x axis, we can't use arcTan() due to divide by zero so set the angle manually  -->"]
[
h, if(yDiff 0): armTokenAngle armTokenAngle]
[
h, if(yDiff 0): armTokenAngle armTokenAngle 180]

[
h'<!-- Finally, set the angle to the selected token  -->']
[
hsetTokenFacing(armTokenAngle,getSelected())]  

What it Does:
  • Faces a selected token away from it's last path.

How to use:
  • Select a token and run the macro either on the token or in the campaign/local window.
  • For best results, drop the code into [wiki]onTokenMove[/wiki]
  • Parameters & Return Values
    • None

Other Tips:
  • This macro ignores all waypoints used when moving, only referring to the origin point and the final point of the last move, as dictated by [wfunc]getLastPath[/wfunc]
  • If a token is picked up and dropped, it resets getLastPath() and the macro will then print an error to the chat. Turn this off by modifying the assert line in the macro.
  • This macro does not work with moving multiple tokens at once.

Examples:
  • Drop into onTokenMove, place and drag a token to see it auto-faced.

Version History:

<-- Back to Indexing

Post Reply

Return to “Macros”