Feat added to the core stats?

Discussion concerning lmarkus' campaign framework for D&D3.x and Pathfinder.

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

Forum rules
Discussion regarding lmarkus001's framework only. Other posts deleted without notice! :)
Post Reply
jsharen
Giant
Posts: 196
Joined: Tue Feb 05, 2008 3:39 pm

Feat added to the core stats?

Post by jsharen »

I have a player that has recently picked up the Book of Nine Swords Warblade ability to add Int bonus to confirm critical hits.

I see that critical focus has been added, so this seems like it would be easy to add but i'm not sure where to dig for the code for that or where it would hit.

Is there any chance someone could add it that is more familiar then me?

Thanks!

neofax
Great Wyrm
Posts: 1694
Joined: Tue May 26, 2009 8:51 pm
Location: Philadelphia, PA
Contact:

Re: Feat added to the core stats?

Post by neofax »

I can do this, but the more feats that are squeezed into the Attack and LibAttack macros, the harder it becomes to understand what is going on.

@Lindsay: Any chance of adding crit to the JSON like CMD/INIT/AbilityBonuses and such? If so, this could be easy to fix.

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

Re: Feat added to the core stats?

Post by aliasmask »

I started a rewrite of the attack macro awhile back, but for it to cover all possibilities, it started turning in to a very large project. My plan was to define certain aspects of the attack and then create generic mods for each so custom feats could be easily added. I was adding things like firstStrikeAttack, firstStrikeDamage for things like smite.

This was my code so far:
||| Attack |||

Code: Select all

[H, IF( json.type( macro.args ) == "UNKNOWN" ), CODE: {
   [ varsFromStrProp(eval(macro.args))]
   [ if(!Primary): abort(0)]
   [ fullRound = 1 ]
   [ flanking = 0 ]
   [ higherGround = 0 ]
   [ rangeIncrement = 0 ]
   [ isPointBlank = 0 ]
   [ fireIntoMelee = 0 ]
   [ sneak = "0d6" ]
   [ isSneak = 0 ]
   [ sneakDMG = 0 ]
   [ favEnemy = 0 ]
   [ cleave = 0 ]
   [ flurryRapid = 1 ]
   [ target1Name = "" ]
   [ target1ID = "" ]
   [ target2Name = "" ]
   [ target2ID = "" ]
};{
   [ tWeapon = json.get( macro.args, "weapon" ) ]
   [ varsFromStrProp(eval(tWeapon))]
   [ if(!Primary): abort(0)]
   [ fullRound = json.get( macro.args, "fullRound" ) ]
   [IF( json.isEmpty( fullRound ) ): fullRound = 1 ]
   [ flanking = json.get( macro.args, "flanking" ) ]
   [IF( json.isEmpty( flanking ) ): flanking = 0; flanking = flanking * 2 ]
   [ higherGround = json.get( macro.args, "higherGround" ) ]
   [IF( json.isEmpty( higherGround ) ): higherGround = 0 ]
   [ rangeIncrement = json.get( macro.args, "rangeIncrement" ) ]
   [IF( json.isEmpty( rangeIncrement ) ): rangeIncrement = 0 ]
   [ isPointBlank = json.get( macro.args, "pointBlank" ) ]
   [IF( json.isEmpty( isPointBlank ) ): isPointBlank = 0 ]
   [ fireIntoMelee = json.get( macro.args, "fireIntoMelee" ) ]
   [IF( json.isEmpty( fireIntoMelee ) ): fireIntoMelee = 0; fireIntoMelee = -4 * fireIntoMelee ]
   [ sneak = json.get( macro.args, "sneak" ) ]
   [IF( json.isEmpty( sneak ) ): sneak = "0d6" ]
   [ isSneak = if( sneak == "0d6" || sneak == 0, 0, 1 ) ]
   [ favEnemy = json.get( macro.args, "favEnemy" ) ]
   [IF( json.isEmpty( favEnemy ) ): favEnemy = 0 ]
   [ isCleave = json.get( macro.args, "cleave" ) ]
   [IF( json.isEmpty( isCleave ) ): isCleave = 0 ]
   [ flurryRapid = json.get( macro.args, "flurryRapid" ) ]
   [IF( json.isEmpty( flurryRapid ) ): flurryRapid = 1 ]
   [ target1Name = json.get( macro.args, "target1Name" ) ]
   [IF( json.isEmpty( target1Name ) ): target1Name = "" ]
   [ target1ID = json.get( macro.args, "target1ID" ) ]
   [IF( json.isEmpty( target1ID ) ): target1ID = "" ]
   [ target2Name = json.get( macro.args, "target2Name" ) ]
   [IF( json.isEmpty( target2Name ) ): target2Name = "" ]
   [ target2ID = json.get( macro.args, "target2ID" ) ]
   [IF( json.isEmpty( target2ID ) ): target2ID = "" ]
}]

[H: gTok = "Lib:GlobalsSRDPF" ]
[H: system = getLibProperty("System", gTok)]

<!-- Get Active Temp Mod Sets -->
[H: aTMS = json.get( PrivateJSON, "ActiveTempModSets" ) ]
[H, IF( json.type( aTMS ) == "UNKNOWN" ), CODE: {
   [ PrivateJSON = json.set( PrivateJSON, "ActiveTempModSets", "[]" ) ]
   [ aTMS = "[]" ]
}]

<!-- Active Mod Setters -->
<!-- Note: Active mod set will have defined name, attack bonus, damage bonus, 1stAttack, ... where values/formulas will be pulled from -->
[H, if(system == "Pathfinder"): padaID = "Power Attack/Deadly Aim"; padaID = "Power Attack"]
[H, if(json.contains(aTMS,padaID)): hasPowerAttack = 1; hasPowerAttack = 0]
<!-- I'll add this to feats and attack macro as option to activate eventually -->
[H, if(json.contains(aTMS,"Smite")): hasSmite = 1; hasSmite = 0]

<!-- Check for Cleave option -->
[H, if(isCleave), code: {
   [H: tGreatCleave = getStrProp(Feats, "GreatCleave")]
   [H: tGreatCleave = if(tGreatCleave == "", 0, tGreatCleave)]
   [H, if(tGreatCleave): tCleave = 1; tCleave = 0]
};{
   [H: tCleave = getStrProp(Feats, "Cleave")]
   [H: tCleave = if(tCleave == "", 0, tCleave)]
}]

<!-- Calc number of attacks -->
[H: jCombat = json.get(PrivateJSON, "Combat")]
[H, IF( json.isEmpty( jCombat ) ): tMagicExtraAttack = 0; tMagicExtraAttack = json.get( jCombat, "magicExtraAttack" ) ]
[H, IF( !isNumber( tMagicExtraAttack ) ): tMagicExtraAttack = 0 ]
[H: FAN = max( 1, floor((BAB - 1) / 5) + 1 ) ]
[H: tHaste = if((Primary == 1) && (state.Haste || tMagicExtraAttack == 1 ), 1, 0)]
[H: isFlurryRapid = if( flurryRapid <= 0 && Manufactured == 1, 1, 0 ) ]
[H, if(!fullRound || Primary == 2 || isCleave): numAttacks = 1; numAttacks = if(Manufactured,FAN,Quantity) + tHaste + isFlurryRapid]
<!-- Special Attacks -->
[H, if(tHaste): extraAttack = json.append("","Haste"); extraAttack = ""]
[H, if(isFlurryRapid): extraAttack = json.append(attackSpecial,"Rapid")]

<!-- GET ATTACK MODS -->
[H: attackMod = ""]
[H: attackModName = ""]

[H, if(AtkBonus == "*"): autoHit = 1; autoHit = 0]
[H: rangeMod = 0]

[H, if(!autoHit), code: {
   <!-- Get Stat Mod -->
   [H: tWeaponFinesse = getStrProp(Feats, "WeaponFinesse")]
   [H: tWeaponFinesse = if(tWeaponFinesse == "", 0, tWeaponFinesse)]
   [H, if(Ranged): statAttackMod = DexB; statAttackMod = if(tWeaponFinesse && Finesse, max(StrB, DexB), StrB)]
   [H: attackMod = json.append(attackMod,statAttackMod)]
   [H: attackModName = json.append(attackModName,"Stat Bonus")]
   <!-- Get Multiple Weapon attack mod -->
   [H, if( OHLight != 2 && fullRound), code: {
      [H: tTwoWeaponFighting = getStrProp(Feats, "TwoWeaponFighting")]
      [H: tTwoWeaponFighting = if(tTwoWeaponFighting == "", 0, tTwoWeaponFighting)]
      [H: tMultiAttack = getStrProp(Feats, "MultiAttack")]
      [H: tMultiAttack = if(tMultiAttack == "", 0, tMultiAttack)]
      [H: MultAtkMP = if(Manufactured == 1, -6 + (tTwoWeaponFighting * 2), 0)]
      [H: MultAtkM = if(Manufactured == 1, -10 + (tTwoWeaponFighting * 6), -5 + (tMultiAttack * 3))]
      [H: atkMult = if(Primary == 1, min(0, max(MultAtkMP, MultAtkMP+(OHLight*2))), min(0, max(MultAtkM, MultAtkM+(OHLight*2))))]
      [H: attackMod = json.append(attackMod,atkMult)]
      [H: attackModName = json.append(attackModName,"Multi Attack")]
   };{}]
   <!-- Get Range Mod -->
   [H, if(Ranged), code: {
      [H: tPointBlankShot = getStrProp(Feats, "PointBlankShot")]
      [H: tPointBlankShot = if(tPointBlankShot == "", 0, tPointBlankShot)]
      [H: rangeMod = if( rangeIncrement == 0, if( tPointBlankShot && isPointBlank, 1, 0 ), -2 * ( rangeIncrement ) ) ]
      [H: attackMod = json.append(attackMod,rangeMod)]
      [H: attackModName = json.append(attackModName,"Range")]
   };{}]
   <!-- Get Size Mod -->
   [H, if(SizeM), code: {
      [H: attackMod = json.append(attackMod,SizeM)]
      [H: attackModName = json.append(attackModName,"Size")]
   };{}]
   <!-- Get Misc Attack Mod -->
   <!-- All the Misc attack mods will be replaced with the respective feat/ability name -->
   [H, if(MiscATK), code: {
      [H: attackMod = json.append(attackMod,MiscATK)]
      [H: attackModName = json.append(attackModName,"Misc")]
   };{}]
   <!-- Get Aiming Attack Mod -->
   [H, if(fireIntoMelee), code: {
      [H: attackMod = json.append(attackMod,fireIntoMelee)]
      [H: attackModName = json.append(attackModName,"Aiming")]
   };{}]
   <!-- Get Flanking Attack Mod -->
   [H, if(flanking), code: {
      [H: attackMod = json.append(attackMod,flanking)]
      [H: attackModName = json.append(attackModName,"Flanking")]
   };{}]
   <!-- Get Weapon Attack Mod -->
   [H, if(AtkBonus), code: {
      [H: attackMod = json.append(attackMod,AtkBonus)]
      [H: attackModName = json.append(attackModName,"Weapon")]
   };{}]
};{}]

<!-- GET DAMAGE MODS - Damage done independant of target's defenses -->
[H: damageMod = "{}"]

<!-- Get Power Attack Damage -->
[H, if( hasPowerAttack && !autoHit), code: {
   [H: powerAttackBAB = json.get( json.get( json.get( json.get( json.get( PrivateJSON, "CustomModSetValues" ), padaID ), "mods" ), "globalMod" ), "v" )]
   [H, if(system == "Pathfinder"), code: {
      [H: pa2H = 1.5 ]
      [H: paMultiplier = 2 ]
      [H: paOffhand = 0.5 ]
      [H, if(Ranged): padaID = "Deadly Aim"; padaID = "Power Attack"]
   };{
      [H: tImprovedPA = getStrProp(Feats, "ImprovedPA")]
      [H: tImprovedPA = if(tImprovedPA == "", 0, tImprovedPA)]
      [H: tSupremePA = getStrProp(Feats, "SupremePA")]
      [H: tSupremePA = if(tSupremePA == "", 0, tSupremePA)]
      [H: paMultiplier = max(1,tImprovedPA * 1.5,tSupremePA * 2)]
      [H: pa2H = 2 ]
      [H: paOffhand = 0 ]      
   }]
   [H, if(TwoHanded): paDMG = floor( paMultiplier * powerAttackBAB * pa2H); paDMG = floor( paMultiplier * powerAttackBAB )]
   <!-- Note: Weapon should have type defined (unarmed,light,natural?(for creatures and monks - this effects power attack),oneHand,twoHand,Range,Thrown) -->
   [H, if(OHLight == 1 || (system == "Pathfinder" && Ranged)): paDMG = paDMG * paOffhand] 
   [H, if(paDMG), code {
      [H: damageMod = json.set(damageMod,padaID,paDMG)]
   };{}]
};{}]

<!-- Get Stat Damage Mod -->
[H, if(!autoHit), code: {
   [H, if(Ranged), code: {
      <!-- two handed is a bow, other is thrown weapon -->
      [H, if(TwoHanded): damageStatMod = min(DmgBonusCap,StrB); damageStatMod = StrB]
   };{
      <!-- A monk's flurry of blows is always StrB despite weapon type -->
      [H, if(TwoHanded && !isFlurryRapid): damageStatMod = Str2hB; damageStatMod = StrB]
      [H, if(Primary == 2 && !isFlurryRapid): damageStatMod = min(StrB,StrSecB)]
   }]
   [H, if(damageStatMod), code: {
      [H: damageMod = json.set(damageMod,"Stat Bonus",damageStatMod)]
   };{}]
   <!-- Point Blank damage mod -->
   [H, if(Ranged && rangeMod > 0), code {
      [H: damageMod = json.set(damageMod,"Point Blank",rangeMod)]
   };{}]
};{}]

<!-- GET EXTRA DAMAGE MODS - Optional damage based on target's defenses -->
<!-- ( Sneak, Skirmish, Good, Evil, Law, Chaos, Bane, Effect(DC check) ) -->
<!-- Some of these will need to be activated in the attack window -->
[H: xDamageMod = "{}"]

<!-- The extra damage and extra damage name can be a list of values separated by a comma and evaluated -->
<!-- Because of Comma Separated List, put note for user to use "," for a comma in output -->
[H: xDamageList = json.fromList(DmgExtra)]
[H: xDamageNameList = json.fromList(DmgExtraName)]
[H, if(isSneak), code: {
   [h: xDamageList = json.append(xDamageList,sneak)]
   [H: xDamageNameList = json.append(xDamageNameList,"Sneak")]
};{}]
[H: hasXDamage = 0]
[H, if(json.length(xDamageList) > 0), code: {
   [H: firstDamage = json.get(xDamageList,0)]
   [H, if(firstDamage != "0" && firstDamage != "0d6"): hasXDamage = 1]
};{}]
<!-- Lets validate the xDamage fields -->
[H, if(hasXDamage), code: {
   [H: xDamageNameListLen = json.length(xDamageNameList)]
   [H: xDamageListLen = json.length(xDamageList)]
   [H, c(xDamageListLen), code: {
      [H, if(xDamageListLen > xDamageNameListLen): 
         xDamageMod = json.set(xDamageMod,"xDamage" + roll.count,strformat("{%s}",json.get(xDamageList,roll.count))); 
         xDamageMod = json.set(xDamageMod,json.get(xDamageNameList,roll.count),strformat("{%s}",json.get(xDamageList,roll.count)))
      ]
   }]
};{}]

<!-- GET EXTRA CRIT DAMAGE MODS - Optional damage based on target's defenses with a critical -->
[H: xCritDamageMod = "{}"]

<!-- The extra damage and extra damage name can be a list of values separated by a comma and evaluated -->
[H: xCritDamageList = json.fromList(DmgExtraCrit)]
[H: xCritDamgeNameList = json.fromList(DmgExtraCritName)]
[H: hasXCritDamage = 0]
[H, if(json.length(xCritDamageList) > 0), code: {
   [H: firstDamage = json.get(xCritDamageList,0)]
   [H, if(firstDamage != "0" && firstDamage != "0d6"): hasXCritDamage = 1]
};{}]
<!-- Validate the xCritDamage fields -->
[H, if(hasXCritDamage), code: {
   [H: xCritDamageNameListLen = json.length(xCritDamageNameList)]
   [H: xCritDamageListLen = json.length(xCritDamageList)]
   [H, c(xCritDamageListLen), code: {
      [H, if(xCritDamageListLen > xCritDamageNameListLen): 
         xCritDamageMod = json.set(xCritDamageMod,"xCritDamage" + roll.count,json.get(xCritDamageList,roll.count)); 
         xCritDamageMod = json.set(xCritDamageMod,json.get(xCritDamageNameList,roll.count)
      ]
   }]
};{}]

<!-- LETS MAKE SOME ROLLS and see what Criticals -->
[H, C(numAttacks), code: {
   [H: rollResult = 1d20]
   [H, if(rollResult >= CritRange): critRoll = 1d20; critRoll = 0]
   <!-- Build data struct to pass -->
   [H: damageRecord = "{}"]
   [H: weaponDamage = val(Damage)]
   [H, if(!weaponDamage && Damage != "" && Damage != "0d6"): weaponDamage = 1]
   [H, if(weaponDamage): damageRecord = json.set(damageRecord,"Weapon",weaponDamage)]
   [H: modDamage = 0]
   [H, if(weaponDamage && !json.isEmpty(damageMod)), code:  {
      [H: damageRecord = json.set(damageRecord,"Weapon Mods",damageMod)]
      [H: damageModList = json.fields(damageMod)]
      [H, foreach(dMod,damageMod): modDamage = modDamage + json.get(damageMod,dMod)]
   };{}]
   [H, if(weaponDamage && critRoll), code: {
      [H: critWeaponDamage = 0]
      [H, C(CritMult-1): critWeaponDamage = critWeaponDamage + max(1,val(Damage)) + modDamage]
      [H: damageRecord = json.set(damageRecord,"Crit",critWeaponDamage)]
   };{}]
   [H: xDamageModList = json.fields(xDamageMod)]
   [H, if(hasXDamage): eXDamageMod = json.evaluate(xDamageMod)]
   [H, foreach(xDMod,xDamageModList): damageRecord = json.set(damageRecord,xDMod,json.get(eXDamageMod,xDMod))]
...
But I'm going to revisit this code and see if I can finish it up. I'll have to look at the most recent version to make sure I don't forget anything new that was added. One main thing I want to accomplish is cleaning up the attack input screen and output for attack. One of the cool things will be the unlimited number of adders for weapon damage and the ability to uncheck them before applying damage (ie my flaming sword may not be flaming at the moment).

User avatar
lmarkus001
Great Wyrm
Posts: 1867
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: Feat added to the core stats?

Post by lmarkus001 »

I just added this feat tree in. So it will be available next update!

jsharen
Giant
Posts: 196
Joined: Tue Feb 05, 2008 3:39 pm

Re: Feat added to the core stats?

Post by jsharen »

Thanks!!!

Post Reply

Return to “D&D 3.5/Pathfinder 1e Campaign Macros”