Is there functionality already built into a Maptool function that will tell you which facing or arc that a target is getting hit, depending on the source of the attack?
Example - a ship is fired upon by a target dead ahead. The function is passed the token ID of the attack's source and returns 'Front'.
Programatically determine firing arc?
Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice
Re: Programatically determine firing arc?
im guessing not in the core of MT, but a lot has been developed on top. Im however not quite sure what you mean. Can you elaborate on what it is you want?
GETTING STARTED WITH MAPTOOLS - TUTORIALS, DOCS, VIDEOS, TOOLS, ETC
DISCORD (the new MT forum!)
My stuff
Excel Tools: Table and Light editors
MT Tools: Bag of Tricks: Tools for Maptool, Dungeon Builder I, Dungeon Builder II,onMouseOverEvent.
Frameworks: Dark Heresy, Rogue Trader, Deathwatch, Black Crusade, Only War, SET Card Game, RoboRally
Wiki: Debugging Tutorial, Speed Up Your Macros, Working With Two CODE Levels, Shortcut Keys, Avoiding Stack Overflow, READ THIS
DISCORD (the new MT forum!)
My stuff
Excel Tools: Table and Light editors
MT Tools: Bag of Tricks: Tools for Maptool, Dungeon Builder I, Dungeon Builder II,onMouseOverEvent.
Frameworks: Dark Heresy, Rogue Trader, Deathwatch, Black Crusade, Only War, SET Card Game, RoboRally
Wiki: Debugging Tutorial, Speed Up Your Macros, Working With Two CODE Levels, Shortcut Keys, Avoiding Stack Overflow, READ THIS
Re: Programatically determine firing arc?
The framework I'm building requires a user to initiate an attack by selecting the attacking token, in this case, a vehicle. The attacker chooses a target and the macro resolves the attack, by passing a set of values to the defending token.
Then a second macro is launched by the defending player selecting the defending token. Vehicles in the game can have shields and can deploy the shields at different strength across 4 different sides: front, left, rear, right.
It'd be cool to have functionality that could be called by the second macro that would determine the side of the defender that is struck, so that the appropriate shield level is automatically applied. The correct side of the defender (front, left, rear, right) would simply depend on the position of the attacker relative to the defender.
Does this make sense?
Then a second macro is launched by the defending player selecting the defending token. Vehicles in the game can have shields and can deploy the shields at different strength across 4 different sides: front, left, rear, right.
It'd be cool to have functionality that could be called by the second macro that would determine the side of the defender that is struck, so that the appropriate shield level is automatically applied. The correct side of the defender (front, left, rear, right) would simply depend on the position of the attacker relative to the defender.
Does this make sense?
Re: Programatically determine firing arc?
Facing of the targeted token matters as well. You can use the math functions to determine the angle and adjust for facing. A simpler method would be to use the slope method (rise/run or ydiff/xdiff). I have some code to determine flanking that could be of use. One thing to consider is the grid. If snapped to grid and on token layer then the x,y is in the center of token otherwise it's in the top left.
Here is the core of my flanking test. Let me know if you have any questions.
As for your specific problem, here is some pseudo-code:
Here is the core of my flanking test. Let me know if you have any questions.
||| CODE |||
Code: Select all
@@ @isFlanking
@PROPS@ fontColor=black ; autoExecute=true ; fontSize=11pt ; sortBy=68 ; color=lime ; playerEditable=false ; applyToSelected=false ; group=AM Updates ; tooltip= ; minWidth=94 ;
[H: a.id = arg(0)]
[H: t.id = arg(1)]
[H: f.id = arg(2)]
<!-- Since we are dealing with grid squares, the distance to the center of grid is 0.5 -->
[H: offset = 0.5]
[H: infinity = 1000000000]
[H: a.strProps = am.defineTokenCoords.sub(a.id,0,0,"a")]
[H: t.strProps = am.defineTokenCoords.sub(t.id,0,0,"t")]
[H: f.strProps = am.defineTokenCoords.sub(f.id,0,0,"f")]
<!-- Check vertical and horizontal -->
[H: isFlanking = 0]
[H: canFlank = if(((a.y1 > t.y1 && t.y1 > f.y1) || (a.y1 < t.y1 && t.y1 < f.y1)) || ((a.x1 > t.x1 && t.x1 > f.x1) || (a.x1 < t.x1 && t.x1 < f.x1)),1,0)]
[H, if(canFlank), code: {
[H: isFlanking = if((max(a.x1,t.x1,f.x1) <= min(a.x2,t.x2,f.x2)-1) && ((a.y1 > t.y1 && t.y1 > f.y1) || (a.y1 < t.y1 && t.y1 < f.y1)),1,0)]
[H, if(! isFlanking): isFlanking = if((max(a.y1,t.y1,f.y1) <= min(a.y2,t.y2,f.y2)-1) && ((a.x1 > t.x1 && t.x1 > f.x1) || (a.x1 < t.x1 && t.x1 < f.x1)),1,0)]
};{}]
<!-- if attacker and ally is size 1, just check path through target -->
[H, if(canFlank && ! isFlanking && a.size == 1 && f.size == 1), code: {
[H: denominator = f.cx - a.cx]
[H,if(denominator): slope = (f.cy - a.cy)/infinity; slope = (f.cy - a.cy)/denominator]
[H: b = a.cy - (a.cx * slope)]
[H, if(slope > 1 || slope < -1), code: {
[H, if((t.y1 - b)/slope >= t.x1 && (t.y1 - b)/slope <= t.x2 && (t.y2 - b)/slope >= t.x1 && (t.y2 - b)/slope <= t.x2): isFlanking = 1]
};{
[H, if(slope * t.x1 + b >= t.y1 && slope * t.x1 + b <= t.y2 && slope * t.x2 + b >= t.y1 && slope * t.x2 + b <= t.y2): isFlanking = 1]
}]
};{}]
<!-- Do slope test: ULtoLR, LLtoUR, URtoLL and LRtoUL. I just redefine coords to ULtoLR to avoid duplicating formulas -->
[H, if(canFlank && ! isFlanking), code: {
[H, if(a.x1_ <= t.cx && a.y1_ <= t.cy && f.x2_ >= t.cx && f.y2_ >= t.cy): isFlanking = am.slopeRangeCheck(a.strProps,t.strProps,f.strProps)]
[H, if(! isFlanking && a.x1_ <= t.cx && a.y2_ >= t.cy && f.x2_ >= t.cx && f.y1_ <= t.cy): isFlanking = am.slopeRangeCheck(am.defineTokenCoords.sub(a.id,0,2*(t.cy-a.cy),"a",0),t.strProps,am.defineTokenCoords.sub(f.id,0,2*(t.cy-f.cy),"f",0))]
[H, if(! isFlanking && a.x2_ >= t.cx && a.y1_ <= t.cy && f.x1_ <= t.cx && f.y2_ >= t.cy): isFlanking = am.slopeRangeCheck(am.defineTokenCoords.sub(a.id,2*(t.cx-a.cx),0,"a",0),t.strProps,am.defineTokenCoords.sub(f.id,2*(t.cx-f.cx),0,"f",0))]
[H, if(! isFlanking && a.x2_ >= t.cx && a.y2_ >= t.cy && f.x1_ <= t.cx && f.y1_ <= t.cy): isFlanking = am.slopeRangeCheck(am.defineTokenCoords.sub(f.id,0,0,"a",0),t.strProps,am.defineTokenCoords.sub(a.id,0,0,"f",0))]
};{}]
[H: macro.return = isFlanking]
!!
@@ @getFlankers
@PROPS@ fontColor=black ; autoExecute=true ; fontSize=11pt ; sortBy=68 ; color=lime ; playerEditable=false ; applyToSelected=false ; group=AM Updates ; tooltip= ; minWidth=94 ;
<!-- getFlankers(attacker,target,allies): flankers -->
[H: attackerId = arg(0)]
[H: targetId = arg(1)]
[H: allyIds = arg(2)]
[H: flankers = ""]
[H, if(json.type(allyIds) == "UNKNOWN" && ! json.isEmpty(allyIds)): allyIds = json.fromList(allyIds)]
[H, if(json.contains(allyIds,attackerId)): allyIds = json.difference(allyIds,json.append("",attackerId))]
[H, if(json.contains(allyIds,targetId)): allyIds = json.difference(allyIds,json.append("",targetId))]
[H, foreach(allyId,allyIds), code: {
[H: isFlanking = am.isFlanking(attackerId,targetId,allyId)]
[H, if(isFlanking): flankers = listAppend(flankers,allyId)]
}]
[H: macro.return = flankers]
!!
@@ @slopeRangeCheck
@PROPS@ fontColor=black ; autoExecute=true ; fontSize=11pt ; sortBy=68 ; color=lime ; playerEditable=false ; applyToSelected=false ; group=AM Updates ; tooltip= ; minWidth=94 ;
<!-- slopeRangeCheck(attackerProps,targetProps,allyProps): isFlanking -->
[H: varsFromStrProp(arg(0))]
[H: varsFromStrProp(arg(1))]
[H: varsFromStrProp(arg(2))]
<!-- Since we are dealing with grid squares, the distance to the center of grid is 0.5 -->
[H: offset = 0.5]
[H: infinity = 1000000000]
[H: isFlanking = 0]
<!-- get slope1, left-most slope -->
[H, if(a.x2_ >= t.x1): slope1 = infinity; slope1 = (t.y1 - a.y1_)/(t.x1 - a.x2_)]
<!-- slope offset is used to determine best point of reference. Either UL or LR depending on slope relation to slope of 1 -->
[H, if(slope1 < 1), code: {
[H: slope1.offset = t.size]
[H: slope1 = ((t.y1 + slope1.offset) - a.y1_)/((t.x1 + slope1.offset) - a.x2_)]
};{
[H: slope1.offset = 0]
}]
<!-- get slope2, right-most slope -->
[H, if(a.y2_ >= t.y1): slope2 = 0; slope2 = (t.y1 - a.y2_)/(t.x1 - a.x1_)]
[H, if(slope2 > 1), code: {
[H: slope2.offset = t.size]
[H: slope2 = ((t.y1 + slope2.offset) - a.y2_)/((t.x1 + slope2.offset) - a.x1_)]
};{
[H: slope2.offset = 0]
}]
<!-- get max left and right values -->
[H, if(slope1 == infinity): isLeft = 1; isLeft = 0]
[H, if(slope2 == 0): isRight = 1; isRight = 0]
<!-- Check left slope for left of ally and border crossover -->
[H, if(! isLeft), code: {
[H: slope1.b = t.y1 + slope1.offset - ((t.x1 + slope1.offset) * slope1)]
[H: slope1.x1 = slope1 * f.x1_ + slope1.b]
[H, if(slope1.x1 > f.y2_): isLeft = 1]
[H, if(! isLeft), code: {
<!-- also check to see if line intersects flanker wall -->
[H: slope1.x2 = slope1 * f.x2_ + slope1.b]
[H, if((slope1.x1 <= f.y2_ && slope1.x1 >= f.y1_) || (slope1.x2 <= f.y2_ && slope1.x2 >= f.y1_)): isFlanking = 1]
};{}]
[H, if(! isLeft && ! isFlanking), code: {
[H: slope1.y1 = (f.y1_ - slope1.b)/slope1]
[H: slope1.y2 = (f.y2_ - slope1.b)/slope1]
[H, if((slope1.y1 <= f.x2_ && slope1.y1 >= f.x1_) || (slope1.y2 <= f.x2_ && slope1.y2 >= f.x1_)): isFlanking = 1]
};{}]
};{}]
[H, if(! isRight && ! isFlanking), code: {
[H: slope2.b = t.y1 + slope2.offset - ((t.x1 + slope2.offset) * slope2)]
[H: slope2.y1 = (f.y1_ - slope2.b)/slope2]
[H, if(slope2.y1 > f.x2_): isRight = 1]
<!-- also check to see if line intersects flanker wall -->
[H, if(! isRight), code: {
[H: slope2.y2 = (f.y2_ - slope2.b)/slope2]
[H, if((slope2.y1 <= f.x2_ && slope2.y1 >= f.x1_) || (slope2.y2 < f.x2_ && slope2.y2 >= f.x1_)): isFlanking = 1]
};{}]
<!-- check through other flanker walls -->
[H, if(! isRight && ! isFlanking), code: {
[H: slope2.x1 = slope2 * f.x1_ + slope2.b]
[H: slope2.x2 = slope2 * f.x2_ + slope2.b]
[H, if((slope2.x1 <= f.y2_ && slope2.x1 >= f.y1_) || (slope2.x2 < f.y2_ && slope2.x2 >= f.y1_)): isFlanking = 1]
};{}]
};{}]
<!-- The 2 slopes surround the flanker which means all points of flanker are flanking -->
[H, if(isLeft && isRight): isFlanking = 1]
[H: macro.return = isFlanking]
!!
@@ @defineTokenCoords.sub
@PROPS@ fontColor=black ; autoExecute=true ; fontSize=11pt ; sortBy=68 ; color=lime ; playerEditable=true ; applyToSelected=false ; group=AM Updates ; tooltip= ; minWidth=147 ;
[H: id.sub = arg(0)]
[H, if(argCount() >= 3), code: {
[H: offsetX.sub = arg(1)]
[H: offsetY.sub = arg(2)]
};{
[H: offsetX.sub = 0]
[H: offsetY.sub = 0]
}]
[H, if(argCount() >= 4): prefix.sub = arg(3); prefix.sub = strformat("t%{id.sub}")]
[H, if(argCount() >= 5): preDefine.sub = arg(4); preDefine.sub = 1]
[H: indent.sub = 0.5]
[H: x.sub = getTokenX(0,id.sub)]
[H: y.sub = getTokenY(0,id.sub)]
[H: size.sub = max(1,listFind("Medium,Large,Huge,Gargantuan,Empty,Colossal",getSize(id.sub))+1)]
[H: strProp.sub = strformat("%{prefix.sub}.x1=%s; %{prefix.sub}.y1=%s; %{prefix.sub}.x2=%s; %{prefix.sub}.y2=%s; %{prefix.sub}.size=%{size.sub}; %{prefix.sub}.cx=%s; %{prefix.sub}.cy=%s; %{prefix.sub}.x1_=%s; %{prefix.sub}.y1_=%s; %{prefix.sub}.x2_=%s; %{prefix.sub}.y2_=%s;", x.sub+offsetX.sub, y.sub+offsetY.sub, x.sub+size.sub+offsetX.sub, y.sub+size.sub+offsetY.sub, x.sub+(size.sub/2)+offsetX.sub, y.sub+(size.sub/2)+offsetY.sub, x.sub+offsetX.sub+indent.sub, y.sub+offsetY.sub+indent.sub, x.sub+size.sub+offsetX.sub-indent.sub, y.sub+size.sub+offsetY.sub-indent.sub)]
<!--
x1 - left x
x2 - right x
y1 - up y
y2 - down y
size - token grid unit size
cx - center x
cy - center y
x1_ - indented left x
x2_ - indented right x
y1_ - indented up y
y2_ - indented down y
-->
[H, if(preDefine.sub): varsFromStrProp(strProp.sub)]
[H: macro.return = strProp.sub]
!!
Code: Select all
getAttackerDetails()
determine slope and direction to attacker.
convert slope and direction to an angle.
add the facing to angle to determine side hit.
note: MT has an inverted y-axis for coords but not for token facing.
Downloads:
- Notepad++ MapTool addon
- RPEdit details (v1.3)
- Coding Tips: Modularity and Design
- Videos: Macro Writing Tools
Re: Programatically determine firing arc?
yes this makes sense and yes this is possible...but quite a bit of work to program...
its a LOT easier to indeed split everything up into two separate macros, launched individually by the user. (e.g. ATTACK and DEFEND or RESOLVE). the attack macro can allow 'target based selection' (search the forum) and consist of a form where you can set all the attack parameters, when done everything is stored on a lib:token.
Then the defend macro is launched, retrieves all stored values and calculates the end result and applies it. Have a look at my warhammer framework which has this (only the defend part is done automatically).
edit: ah Alias already replied...
its a LOT easier to indeed split everything up into two separate macros, launched individually by the user. (e.g. ATTACK and DEFEND or RESOLVE). the attack macro can allow 'target based selection' (search the forum) and consist of a form where you can set all the attack parameters, when done everything is stored on a lib:token.
Then the defend macro is launched, retrieves all stored values and calculates the end result and applies it. Have a look at my warhammer framework which has this (only the defend part is done automatically).
edit: ah Alias already replied...
GETTING STARTED WITH MAPTOOLS - TUTORIALS, DOCS, VIDEOS, TOOLS, ETC
DISCORD (the new MT forum!)
My stuff
Excel Tools: Table and Light editors
MT Tools: Bag of Tricks: Tools for Maptool, Dungeon Builder I, Dungeon Builder II,onMouseOverEvent.
Frameworks: Dark Heresy, Rogue Trader, Deathwatch, Black Crusade, Only War, SET Card Game, RoboRally
Wiki: Debugging Tutorial, Speed Up Your Macros, Working With Two CODE Levels, Shortcut Keys, Avoiding Stack Overflow, READ THIS
DISCORD (the new MT forum!)
My stuff
Excel Tools: Table and Light editors
MT Tools: Bag of Tricks: Tools for Maptool, Dungeon Builder I, Dungeon Builder II,onMouseOverEvent.
Frameworks: Dark Heresy, Rogue Trader, Deathwatch, Black Crusade, Only War, SET Card Game, RoboRally
Wiki: Debugging Tutorial, Speed Up Your Macros, Working With Two CODE Levels, Shortcut Keys, Avoiding Stack Overflow, READ THIS
Re: Programatically determine firing arc?
How do you handle having 2 sides exposed to the attacker? Percentage chance to hit either side or is it always a straight shot to the center of token? Corner shots when need you to chose a method of which shield to hit.
Downloads:
- Notepad++ MapTool addon
- RPEdit details (v1.3)
- Coding Tips: Modularity and Design
- Videos: Macro Writing Tools
Re: Programatically determine firing arc?
Not sure if this is the same thing, but I handle this with a "vision" macro that is aligned with the "real" token, the rotated according. It has different vision types (since front/back vision "cone" is different from the left/right vision "cone") and then uses the "isVisible" function to determine if points on the target are visible or not (to account for vision blocking).
I can share code if you think it would help but its pretty specific to my application.
I can share code if you think it would help but its pretty specific to my application.