Distance Macro Help

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

Post Reply
TheGrandDesigner
Kobold
Posts: 11
Joined: Fri Aug 16, 2013 11:05 pm

Distance Macro Help

Post by TheGrandDesigner »

I'm attempting to create a macro that gives a list of potential melee targets on a grid-less map. I have the following macro for the selection Dialogue:
Spoiler
[h: cond = json.set("{}", "range", json.set("{}", "upto", 2, "distancePerCell", 0, "metric", "NO_GRID"))]

[h: sourceToken = getSelectedNames() ]
[h: targetTokenList = getTokenNames(",", cond)]
[h: targetTokenListImg = targetTokenList]

[h: Num = listCount(targetTokenListImg)]
[h,COUNT(Num), CODE:
{
[h: targetName = listGet(targetTokenListImg,roll.count)]
[h,token(targetName): image=getTokenImage()]
[h: targetTokenListImg=listReplace(targetTokenListImg,roll.count,targetName+" "+image)]
}]

[h: status=input(
"junkVar| "+sourceToken+"|Source|LABEL",
"target| "+targetTokenListImg+"|Select Target|LIST|SELECT=0 ICON=TRUE ICONSIZE=30",
"Mod|0|Mod",
"Loc|Torso, Neck, Vitals, Face, Skull, Eye, Groin, Right Arm, Left Arm, Right Leg, Left Leg, Right Hand, Left Hand, Right Foot, Left Foot|Hit Location|LIST|Select=0 VALUE=STRING"
)]
This works fine for the majority of tokens, but it doesn't work with tokens larger than Size 0. Since distance measures from the center of the token, rather than the edge, My larger tokens of Size 18-19 have to be standing on top of each other to register as in melee in this macro. If anyone knows of a solution to this problem I'd be incredibly grateful.
If your players aren't complaining, then something is wrong.

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

Re: Distance Macro Help

Post by Bone White »

Subtract (the size of the token / 2) from it's range, but I thought tokens were measured from the top left corner?

TheGrandDesigner
Kobold
Posts: 11
Joined: Fri Aug 16, 2013 11:05 pm

Re: Distance Macro Help

Post by TheGrandDesigner »

Thanks for the reply,
but I thought tokens were measured from the top left corner?
Since The Macro Works when I put a Size 0 Token in the middle of my Size 18 token, but doesn't work when I put it in the top left corner, I'm pretty sure it measures from the center in gridless mode.
Subtract (the size of the token / 2) from it's range
First, I'm pretty sure I'd want to add it to the range when using:

Code: Select all

[h: cond = json.set("{}", "range", json.set("{}", "upto", 2, "distancePerCell", 0, "metric", "NO_GRID"))]
Secondly, how would I go about doing that? I've attempted to use this:

Code: Select all

[h:Size = getSize()]
[h:ModDist = (Size) + 2]
[h: cond = json.set("{}", "range", json.set("{}", "upto", ModDist, "distancePerCell", 0, "metric", "NO_GRID"))]

[h: sourceToken = getSelectedNames() ]
[h: targetTokenList = getTokenNames(",", cond)]
[h: targetTokenListImg = targetTokenList]

[h: Num = listCount(targetTokenListImg)]
[h,COUNT(Num), CODE:
{
[h: targetName = listGet(targetTokenListImg,roll.count)]
[h,token(targetName): image=getTokenImage()]
[h: targetTokenListImg=listReplace(targetTokenListImg,roll.count,targetName+" "+image)]
}]

[h: status=input(
"junkVar| "+sourceToken+"|Source|LABEL",
"target|  "+targetTokenListImg+"|Select Target|LIST|SELECT=0 ICON=TRUE ICONSIZE=30",
"Mod|0|Mod",
"Loc|Torso, Neck, Vitals, Face, Skull, Eye, Groin, Right Arm, Left Arm, Right Leg, Left Leg, Right Hand, Left Hand, Right Foot, Left Foot|Hit Location|LIST|Select=0 VALUE=STRING"
)]
[h:abort(status) ]
But it only works for the Larger tokens targeting smaller tokens, but not for when a smaller token is targeting a larger one.
If your players aren't complaining, then something is wrong.

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Distance Macro Help

Post by aliasmask »

Get all tokens on map and put in to json (or list). Remove source token from list. Loop through list using Wiki: getDistance() and Wiki: getSize(). You'll have to come up with your own formula because I don't know what "18" represents in pixels. If target within the proper distance, add token to targets list.

TheGrandDesigner
Kobold
Posts: 11
Joined: Fri Aug 16, 2013 11:05 pm

Re: Distance Macro Help

Post by TheGrandDesigner »

Thanks for the info. I should be able to figure out a macro from this.
If your players aren't complaining, then something is wrong.

ziltmilt
Dragon
Posts: 331
Joined: Sun Apr 29, 2007 9:28 pm
Contact:

Re: Distance Macro Help

Post by ziltmilt »

This is the code I came up with, but I'm getting an error.

Code: Select all

[h: ID = arg(0)]
[h: targetTokenList = getExposedTokenNames()]
[h: newList = ""]
[h, foreach(tokenName, targetTokenList): newList = if(getDistance(tokenName,0,ID, "ONE_TWO_ONE")==1 && ID <> tokenName, newList + "," + tokenName, newList)]
[h: macro.return = newList]
ID is supposed to be the token ID of the attacker, or the source token, and is the result of GetSelected(). Any idea what I could be doing wrong?

User avatar
Irrlicht
Dragon
Posts: 426
Joined: Mon Feb 09, 2009 10:53 am

Re: Distance Macro Help

Post by Irrlicht »

It's the first time I see that "<>" in the IF condition "ID <> tokenName". Is it something valid (and what is it, in that case)? Otherwise, that may be the cause of the error.

As a side note, simpler than using "newList + "," + tokenName", you could use "listAppend(newList, tokenName)".

EDIT
Ah, yes, I confirm, you want that condition to be "ID != tokenName". That way, it works, I tested it.
"There are many ways my Son, to find where the souls of Demons remain...
But it takes only one second of despair and of doubt until, at last, your Soul they will gain..."

ziltmilt
Dragon
Posts: 331
Joined: Sun Apr 29, 2007 9:28 pm
Contact:

Re: Distance Macro Help

Post by ziltmilt »

Thank you so much! I'm getting tripped up by my day job, where I use VBA a lot.

User avatar
Irrlicht
Dragon
Posts: 426
Joined: Mon Feb 09, 2009 10:53 am

Re: Distance Macro Help

Post by Irrlicht »

I would also add canSeeToken, because sometimes you may have a situation where a target token is within range, but there's actually a wall between it and the character running the macro.

I have no idea how canSeeToken works, though. I tried, but I get weird results and can't find a good explanation on the boards (nor wiki, of course), so I can't help you with it.
"There are many ways my Son, to find where the souls of Demons remain...
But it takes only one second of despair and of doubt until, at last, your Soul they will gain..."

User avatar
JamzTheMan
Great Wyrm
Posts: 1872
Joined: Mon May 10, 2010 12:59 pm
Location: Chicagoland
Contact:

Re: Distance Macro Help

Post by JamzTheMan »

I have a sample macro that uses canSeeToken for you if you want...

Code: Select all

[h: cond = '{ range: {upto:12, distancePerCell:0, metric:"ONE_TWO_ONE"}, setStates:["isMagical"], layer:["HIDDEN"] }']
[h: tokenList = getTokenNames("json", cond)]
[h, FOREACH (token, tokenList): setLayer("TOKEN", token)]
[h: count = json.length(tokenList)]

[h, FOREACH(token, tokenList, "<BR>"), CODE:{
   [cantSee = json.isEmpty(canSeeToken(token))]
   [layer = if(cantSee == 1, "HIDDEN", "OBJECT")]
   [setLayer(layer, token)]
   [count = if(cantSee == 1, count - 1, count)]
}]

Exposed {count} magical auras.
-Jamz
____________________
Custom MapTool 1.4.x.x Fork: maptool.nerps.net
Custom TokenTool 2.0 Fork: tokentool.nerps.net
More information here: MapTool Nerps! Fork

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Distance Macro Help

Post by aliasmask »

Here's a function I use to get seen tokens.

Code: Select all

<!-- getSeenTokens(options,source): json array or object 

options (strprop or json object)
   pc - 0 any token, 1 - PC only
   npc - 0 any token, 1 - NPC only
   range - upto this distance in grid units (ie feet, and not grid squares); 0 - any range
   self - 0 do not include self, 1 include self
source = source token, defaults to currentToken
   
This function will get a list of tokenIds based on the current tokens vision 
-->

[H, if(argCount()): options = lower(arg(0)); options = "{}"]
[H, if(argCount() >= 2): sourceToken = arg(1); sourceToken = currentToken()]

<!-- verify token is on current map -->
[H: sourceToken = findToken(sourceToken)]

[H: assert(! json.isEmpty(sourceToken),"<b>getSeenTokens(options,source):</b> Token not found on current map.",0)]

<!-- define options locally -->
[H: defaults = json.set("{}","pc",0,"npc",0,"range",0,"self",0)]
[H, if(json.type(options) == "UNKNOWN" && ! json.isEmpty(options)): options = json.fromStrProp(options)]
[H, if(json.isEmpty(options)): options = defaults; options = json.merge(defaults,options)]
[H: varsFromStrProp(json.toStrProp(options))]

<!-- get token ids -->
[H: conditions = "{}"]
[H, if(npc && ! pc): conditions = json.set(conditions,"npc",npc)]
[H, if(! npc && pc): conditions = json.set(conditions,"pc",pc)]
[H, if(range): conditions = json.set(conditions,"range",json.set("{}","token",sourceToken,"upto",range,"distancePerCell",1))]
[H, if(! json.isEmpty(conditions)): tokenIds = getTokens("json",conditions); tokenIds = getTokens("json")]

<!-- exclude self if set -->
[H, if(self), code: {
   [H, if(! json.isEmpty(tokenIds)): tokenIds = json.union(tokenIds,json.append("",sourceToken)); tokenIds = json.append("",sourceToken)]
};{
   [H, if(! json.isEmpty(tokenIds)): tokenIds = json.difference(tokenIds,json.append("",sourceToken)); tokenIds = "[]"]
}]

<!-- check each id for name and visible -->
[H: targets = "[]"]
[H: hasSight = hasSight()]
[H, if(! hasSight()): setHasSight(1)] <!-- this works for NPC tokens too -->
[H, foreach(tokenId,tokenIds), code: {
   [H: canSee = ! json.isEmpty(canSeeToken(tokenId))]
   [H, if(canSee): targets = json.append(targets,tokenId)]
}]
[H, if(hasSight != hasSight()): setHasSight(hasSight)]

[H: macro.return = targets] 
Here is a macro I use to get distance. It accounts for elevation and token size as well.

Code: Select all

<!-- getRangedDistance(source,target): range -->
[H: source = arg(0)]
[H: target = arg(1)]

<!-- source and target must be defined -->
[H: assert(! json.isEmpty(source) && ! json.isEmpty(target),"<b>getRangedDistance(source,target): range</b> - Source or target not defined.",0)]

<!-- If elevation isnt set then set to 0 -->
[H: source.elevation = getProperty("Elevation",source)]
[H, if(! isNumber(source.elevation)): source.elevation = 0]
[H: target.elevation = getProperty("Elevation",target)]
[H, if(! isNumber(target.elevation)): target.elevation = 0]

<!-- get elevation difference and factor in size. Used big formula for processing speed -->
[H, if(source.elevation != target.elevation), code: {
   [H, if(source.elevation < target.elevation): distance.vertical = max(0,target.elevation - (source.elevation + (listFind("Large,Huge,Gargantuan,NA,Colossal",getSize(source)) + 1)*5));
      distance.vertical = max(0,source.elevation - (target.elevation + (listFind("Large,Huge,Gargantuan,NA,Colossal",getSize(target)) + 1)*5))]
};{
   [H: distance.vertical = 0]
}]

[H, if(! distance.vertical), code: {
   [H: totalDistance = getDistance(source,1,target,"ONE_TWO_ONE")]
};{
   <!-- count squares, no metric in between -->
   [H: distance.direct = getDistance(source,1,target,"ONE_ONE_ONE")]
   <!-- get number of diagonal moves -->
   [H: horizontal.diagonals = floor((getDistance(source,1,target,"NO_DIAGONALS") - distance.direct)/5)]
   <!-- its assumed when changing elevation that you move in a diagonal fashion until at elevation -->
   [H: vertical.diagonals = min(distance.direct,distance.vertical)]
   <!-- subtract horizontal diags used from vertical diags needed because we can move diag in vert and horizontal direction for same cost -->
   [H: extra.diagonals =  floor((max(vertical.diagonals,horizontal.diagonals) - horizontal.diagonals)/5)]
   <!-- manually calc distance based on number of diags moved (1-2-1) and add the leftover horizonal and vertical distances -->
   [H: totalDistance = floor((horizontal.diagonals + extra.diagonals) * 0.5) * 5 + distance.direct + distance.vertical - vertical.diagonals]
}]

[H: macro.return = totalDistance] 

User avatar
Irrlicht
Dragon
Posts: 426
Joined: Mon Feb 09, 2009 10:53 am

Re: Distance Macro Help

Post by Irrlicht »

JamzTheMan wrote:I have a sample macro that uses canSeeToken for you if you want...

Code: Select all

[h: cond = '{ range: {upto:12, distancePerCell:0, metric:"ONE_TWO_ONE"}, setStates:["isMagical"], layer:["HIDDEN"] }']
[h: tokenList = getTokenNames("json", cond)]
[h, FOREACH (token, tokenList): setLayer("TOKEN", token)]
[h: count = json.length(tokenList)]

[h, FOREACH(token, tokenList, "<BR>"), CODE:{
   [cantSee = json.isEmpty(canSeeToken(token))]
   [layer = if(cantSee == 1, "HIDDEN", "OBJECT")]
   [setLayer(layer, token)]
   [count = if(cantSee == 1, count - 1, count)]
}]

Exposed {count} magical auras.
I have no idea, after I wrote in a similar way than above, with the exact same code, canSeeToken() worked, then didn't work.
But I must say listSort() had the same behavior, this evening. Dunno what's up.
"There are many ways my Son, to find where the souls of Demons remain...
But it takes only one second of despair and of doubt until, at last, your Soul they will gain..."

Post Reply

Return to “Macros”