+
@@ -4403,8 +4477,6 @@ on('clicked:roll', async (event) => {
let rollBase = event.htmlAttributes.value + "{{fumble=[[1d10!]]}}{{fumble2=[[1d10!]]}}{{rolllocation=[[0]]}}";
let rollResult = await startRoll(rollBase);
- console.log(rollResult);
-
let computedValues = await resolveChecks(
checkFumble(rollResult),
checkLocation(rollResult)
@@ -4469,19 +4541,220 @@ on('clicked:roll_spellcasting_detailed', async (event) => {
);
});
+/////////////////////////
+// Defense Base Rolls //
+/////////////////////////
+on('clicked:dodge_base clicked:reposition_base clicked:block_base', async (event) => {
+
+ let rollBase = event.htmlAttributes.value + "{{fumble=[[1d10!]]}}";
+ let rollResult = await startRoll(rollBase);
+
+ let computedValues = await resolveChecks(
+ checkFumble(rollResult),
+ );
+
+ finishRoll(
+ rollResult.rollId,
+ computedValues
+ );
+});
+
+on('clicked:reactdodge', async (ev) => {
+ let passin = rollEscape.unescape(ev.originalRollId);
+
+ let rollBase = "@{whisper}&{template:npcdefense} {{title=DODGE}} {{name=@{character_name}}} {{roll=[[1d10!+@{dodge_base}[SKILL BASE]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}";
+ doDefense(passin, rollBase);
+});
+
+on('clicked:reactreposition', async (ev) => {
+ let passin = rollEscape.unescape(ev.originalRollId);
+
+ let rollBase = "@{whisper}&{template:npcdefense} {{title=REPOSITION}} {{name=@{character_name}}} {{roll=[[1d10!+@{reposition_base}[SKILL BASE]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}";
+ doDefense(passin, rollBase);
+});
+
+on('clicked:reactblock', async (ev) => {
+ let passin = rollEscape.unescape(ev.originalRollId);
+
+ let rollBase = "@{whisper}&{template:npcdefense} {{title=BLOCK}} {{name=@{character_name}}} {{roll=[[1d10!+@{block_base}[SKILL BASE]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}";
+ doDefense(passin, rollBase);
+});
+
+const doDefense = async (passin, rollBase) => {
+ let rollResult = await startRoll(rollBase+'{{fumble=[[1d10!]]}}{{rolllocation=[[0]]}}{{attackerRoll=[[0]]}}{{hit=[[0]]}}{{attackmultiplier=[[0]]}}{{weapondmg=[[0]]}}{{afterArmor=[[0]]}}{{locationmultiplier=[[0]]}}{{armor=[[0]]}}{{damage=[[0]]}}{{critdmg=[[0]]}}{{passthrough=[[0]]}}');
+
+ let computedValues = await resolveChecks(
+ checkFumble(rollResult),
+ );
+
+ computedValues.rolllocation = passin.rolllocation;
+ computedValues.weaponname = passin.weapon_name;
+ computedValues.attackerRoll = passin.roll;
+
+ let defense = computedValues.roll;
+ let hit = parseInt(passin.roll) - parseInt(defense);
+ computedValues.hit = hit;
+
+ let damageObject = await calculateDamage(passin, hit);
+
+ computedValues = Object.assign(computedValues, damageObject);
+ computedValues.passthrough = rollEscape.escape(damageObject);
+ console.log(computedValues);
+
+
+ finishRoll(
+ rollResult.rollId,
+ computedValues
+ );
+}
+
+const calculateDamage = async (passin, hit) => {
+
+ if(hit < 0) {
+ return undefined
+ }
+
+ let attrs = await asw.getAttrs(['npc_armor']);
+ let attackmultiplier = passin.attackmultiplier;
+ let locationmultiplier = passin.locationmultiplier;
+ let weapondmg = passin.weapon_dmg;
+ let armor = attrs['npc_armor'];
+
+ let critdmg = 0;
+ //crit dmg
+ if(hit >= 15) {
+ critdmg = 10;
+ }
+ else if(hit >= 13) {
+ critdmg = 8;
+ }
+ else if(hit >= 10) {
+ critdmg = 5;
+ }
+ else if(hit >= 7) {
+ critdmg = 3;
+ }
+
+ //(attack multiplicator x rolled dmg - SP) * location * resistance + bonus crit damage
+ let afterArmor = Math.max((attackmultiplier * weapondmg - armor), 0);
+ let damage = afterArmor * locationmultiplier + critdmg;
+
+ return {
+ attackmultiplier: attackmultiplier,
+ locationmultiplier: locationmultiplier,
+ weapondmg: weapondmg,
+ critdmg: critdmg,
+ armor: armor,
+ afterArmor: afterArmor,
+ damage: damage
+ }
+
+}
+
+on('clicked:monsterlocation', async (ev) => {
+ let passin = rollEscape.unescape(ev.originalRollId);
+ console.log(passin);
+
+ let rollBase = "@{whisper}&{template:monsterlocation} {{title=TARGET LOCATION}} {{name=@{character_name}}} {{roll=[[1d10]]}}{{attackmultiplier=[[0]]}}{{weapondmg=[[0]]}}{{afterArmor=[[0]]}}{{locationmultiplier=[[0]]}}{{armor=[[0]]}}{{damage=[[0]]}}{{critdmg=[[0]]}}"
+ let rollResult = await startRoll(rollBase);
+
+ console.log(rollResult);
+ var location = rollResult.results.roll.result;
+
+ let locationmultiplier = 1;
+ if(location === 1) {
+ locationmultiplier = 3;
+ }
+ else if(location > 1 && location < 6) {
+ locationmultiplier = 1;
+ }
+ else if(location > 5) {
+ locationmultiplier = 0.5;
+ }
+
+
+ //(attack multiplicator x rolled dmg - SP) * location * resistance + bonus crit damage
+ let damage = passin.afterArmor * locationmultiplier + passin.critdmg;
+
+ finishRoll(
+ rollResult.rollId,
+ {
+ attackmultiplier: passin.attackmultiplier,
+ locationmultiplier: locationmultiplier,
+ weapondmg: passin.weapondmg,
+ critdmg: passin.critdmg,
+ armor: passin.armor,
+ afterArmor: passin.afterArmor,
+ damage: damage
+ }
+ );
+
+});
+
+on('clicked:humanlocation', async (ev) => {
+ let passin = rollEscape.unescape(ev.originalRollId);
+ console.log(passin);
+
+ let rollBase = "@{whisper}&{template:humanlocation} {{title=TARGET LOCATION}} {{name=@{character_name}}} {{roll=[[1d10]]}}{{attackmultiplier=[[0]]}}{{weapondmg=[[0]]}}{{afterArmor=[[0]]}}{{locationmultiplier=[[0]]}}{{armor=[[0]]}}{{damage=[[0]]}}{{critdmg=[[0]]}}"
+ let rollResult = await startRoll(rollBase);
+
+ console.log(rollResult);
+ var location = rollResult.results.roll.result;
+
+ let locationmultiplier = 1;
+ if(location === 1) {
+ locationmultiplier = 3;
+ }
+ else if(location > 1 && location < 5) {
+ locationmultiplier = 1;
+ }
+ else if(location > 4) {
+ locationmultiplier = 0.5;
+ }
+
+
+ //(attack multiplicator x rolled dmg - SP) * location * resistance + bonus crit damage
+ let damage = passin.afterArmor * locationmultiplier + passin.critdmg;
+
+ finishRoll(
+ rollResult.rollId,
+ {
+ attackmultiplier: passin.attackmultiplier,
+ locationmultiplier: locationmultiplier,
+ weapondmg: passin.weapondmg,
+ critdmg: passin.critdmg,
+ armor: passin.armor,
+ afterArmor: passin.afterArmor,
+ damage: damage
+ }
+ );
+
+});
+
+
//////////////////////////////
// Weapon Template Attacks //
//////////////////////////////
on('clicked:primaryweapon clicked:secondaryweapon clicked:thirdweapon clicked:fourthweapon clicked:fifthweapon clicked:sixthweapon clicked:shield', async (event) => {
- let rollBase = event.htmlAttributes.value + "{{singlestrike=[[0]]}}{{faststrike=[[0]]}}{{strongstrike=[[0]]}}{{jointmeleeattack=[[0]]}}{{jointrangedattack=[[0]]}}";
+ let rollBase = event.htmlAttributes.value + "{{singlestrike=[[0]]}}{{faststrike=[[0]]}}{{strongstrike=[[0]]}}{{jointmeleeattack=[[0]]}}{{jointrangedattack=[[0]]}}{{passthrough=[[0]]}}";
let rollResult = await startRoll(rollBase);
let computedValues = await resolveChecks(
calculateTemplateButtons(rollBase)
);
+ // Storing all the passthrough data required for the next roll in an Object helps for larger rolls
+ computedValues.passthrough = rollEscape.escape({
+ skill: rollBase.substring(rollBase.indexOf("skill="), rollBase.indexOf("weapon_skill")+13).replace("skill=@{", "").replace("}",""),
+ weapon_name: rollBase.substring(rollBase.indexOf("weapon-name="), rollBase.indexOf("weapon_name")+12).replace("weapon-name=@{", "").replace("}",""),
+ weapon_wa: rollBase.substring(rollBase.indexOf("accuracy="), rollBase.indexOf("wa")+2).replace("accuracy=@{", "").replace("}",""),
+ weapon_dmg: rollResult.results.damage.result,
+ weapon_dmg2: rollBase.substring(rollBase.indexOf("damage="), rollBase.indexOf("weapon_bonus}")+13).replace("damage=[[", ""),
+ weapon_effect: rollBase.substring(rollBase.indexOf("effect="), rollBase.indexOf("weapon_effect")+13).replace("effect=@{", "").replace("}","")
+ })
+
finishRoll(
rollResult.rollId,
computedValues
@@ -4567,71 +4840,59 @@ const calculateTemplateButtons = async (rollBase) => {
on('clicked:templatesinglestrike', async (ev) => {
// The data we passed into the button will be stored in the originalRollId key
- let skill = ev.originalRollId;
+ let passin = rollEscape.unescape(ev.originalRollId);
+ let skill_attribute = passin.skill;
+ let weaponname = passin.weapon_name;
+ let weaponwa = passin.weapon_wa;
- let attrs = await asw.getAttrs([skill]);
+ let skill = (await asw.getAttrs([skill_attribute]))[skill_attribute];
+ let attrs = await asw.getAttrs([weaponname, weaponwa, skill]);
- let rollBase = `@{whisper}&{template:singleroll} {{title=WEAPON ATTACK}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]+0[SINGLE STRIKE]+?{WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{FIRING RANGE|Point Blank (Touch Range),+5|Close (1/4 Listed Range),+0|Medium (1/2 Listed Range),-2|Long (Listed Range),-4|Extreme (2x Listed Range),-6}[RANGE]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{rolllocation=[[0]]}}`
- let rollResult = await startRoll(rollBase);
-
- let computedValues = await resolveChecks(
- checkFumble(rollResult),
- checkLocation(rollResult)
- );
-
- finishRoll(
- rollResult.rollId,
- computedValues
- );
+ let rollBase = `@{whisper}&{template:weaponattack} {{title=${attrs[weaponname]}}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]+0[SINGLE STRIKE]+${attrs[weaponwa]}[WEAP. ACC]?{FIRING RANGE|Point Blank (Touch Range),+5|Close (1/4 Listed Range),+0|Medium (1/2 Listed Range),-2|Long (Listed Range),-4|Extreme (2x Listed Range),-6}[RANGE]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}`
+ doTemplateAttack(passin, rollBase);
});
on('clicked:templatefastattack', async (ev) => {
// The data we passed into the button will be stored in the originalRollId key
- let skill = ev.originalRollId;
+ let passin = rollEscape.unescape(ev.originalRollId);
+ let skill_attribute = passin.skill;
+ let weaponname = passin.weapon_name;
+ let weaponwa = passin.weapon_wa;
- let attrs = await asw.getAttrs([skill]);
+ let skill = (await asw.getAttrs([skill_attribute]))[skill_attribute];
+ let attrs = await asw.getAttrs([weaponname, weaponwa, skill]);
- let rollBase = `@{whisper}&{template:doubleroll} {{title=WEAPON ATTACK}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]+0[FAST STRIKES]+?{WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{1ST ATTACK LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{1ST ATTACK MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}} {{roll2=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]+0[FAST STRIKES]+?{WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{2ND ATTACK LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{2ND ATTACK MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{fumble=[[1d10!]]}}{{fumble2=[[1d10!]]}}{{rolllocation=[[0]]}}`
- let rollResult = await startRoll(rollBase);
-
- let computedValues = await resolveChecks(
- checkFumble(rollResult),
- checkLocation(rollResult)
- );
-
- finishRoll(
- rollResult.rollId,
- computedValues
- );
+ let rollBase = `@{whisper}&{template:weaponattack} {{title=${attrs[weaponname]}}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]+0[FAST STRIKES]+${attrs[weaponwa]}[WEAP. ACC]?{1ST ATTACK LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{1ST ATTACK MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}} {{roll2=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]+0[FAST STRIKES]+${attrs[weaponwa]}[WEAP. ACC]?{2ND ATTACK LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{2ND ATTACK MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{fumble=[[1d10!]]}}{{fumble2=[[1d10!]]}}{{damage2=[[${passin.weapon_dmg2}]]}}`
+ doTemplateAttack(passin, rollBase);
});
on('clicked:templatestrongattack', async (ev) => {
// The data we passed into the button will be stored in the originalRollId key
- let skill = ev.originalRollId;
+ let passin = rollEscape.unescape(ev.originalRollId);
+ let skill_attribute = passin.skill;
+ let weaponname = passin.weapon_name;
+ let weaponwa = passin.weapon_wa;
- let attrs = await asw.getAttrs([skill]);
+ let skill = (await asw.getAttrs([skill_attribute]))[skill_attribute];
+ let attrs = await asw.getAttrs([weaponname, weaponwa, skill]);
- let rollBase = `@{whisper}&{template:singleroll} {{title=WEAPON ATTACK}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[STRONG STRIKE]+?{WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{fumble=[[1d10!]]}}{{rolllocation=[[0]]}}`
- let rollResult = await startRoll(rollBase);
+ passin.attackmultiplier = 2;
- let computedValues = await resolveChecks(
- checkFumble(rollResult),
- checkLocation(rollResult)
- );
-
- finishRoll(
- rollResult.rollId,
- computedValues
- );
+ let rollBase = `@{whisper}&{template:weaponattack} {{title=${attrs[weaponname]}}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[STRONG STRIKE]+${attrs[weaponwa]}[WEAP. ACC]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{fumble=[[1d10!]]}}`
+ doTemplateAttack(passin, rollBase);
});
on('clicked:templatejointmeleeattack', async (ev) => {
// The data we passed into the button will be stored in the originalRollId key
- let skill = ev.originalRollId;
+ let passin = rollEscape.unescape(ev.originalRollId);
+ let skill_attribute = passin.skill;
+ let weaponname = passin.weapon_name;
+ let weaponwa = passin.weapon_wa;
- let attrs = await asw.getAttrs([skill]);
+ let skill = (await asw.getAttrs([skill_attribute]))[skill_attribute];
+ let attrs = await asw.getAttrs([weaponname, weaponwa, skill]);
- let rollBase = `@{whisper}&{template:doubleroll} {{title=WEAPON ATTACK}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+?{WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}} {{roll2=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+?{SECOND WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{SECOND TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{SECOND ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{rolllocation=[[0]]}}`
+ let rollBase = `@{whisper}&{template:weaponattack} {{title=${attrs[weaponname]}}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+${attrs[weaponwa]}[WEAP. ACC]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}} {{roll2=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+?{SECOND WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{SECOND TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{SECOND ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{rolllocation=[[0]]}}`
let rollResult = await startRoll(rollBase);
let computedValues = await resolveChecks(
@@ -4647,11 +4908,15 @@ on('clicked:templatejointmeleeattack', async (ev) => {
on('clicked:templatejointrangedattack', async (ev) => {
// The data we passed into the button will be stored in the originalRollId key
- let skill = ev.originalRollId;
+ let passin = rollEscape.unescape(ev.originalRollId);
+ let skill_attribute = passin.skill;
+ let weaponname = passin.weapon_name;
+ let weaponwa = passin.weapon_wa;
- let attrs = await asw.getAttrs([skill]);
+ let skill = (await asw.getAttrs([skill_attribute]))[skill_attribute];
+ let attrs = await asw.getAttrs([weaponname, weaponwa, skill]);
- let rollBase = `@{whisper}&{template:doubleroll} {{title=WEAPON ATTACK}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+?{WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{FIRING RANGE|Point Blank (Touch Range),+5|Close (1/4 Listed Range),+0|Medium (1/2 Listed Range),-2|Long (Listed Range),-4|Extreme (2x Listed Range),-6}[RANGE]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}} {{roll2=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+?{SECOND WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{FIRING RANGE|Point Blank (Touch Range),+5|Close (1/4 Listed Range),+0|Medium (1/2 Listed Range),-2|Long (Listed Range),-4|Extreme (2x Listed Range),-6}[RANGE]?{SECOND TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{SECOND ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{rolllocation=[[0]]}}`
+ let rollBase = `@{whisper}&{template:weaponattack} {{title=${attrs[weaponname]}}} {{name=@{character_name}}} {{roll=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+${attrs[weaponwa]}[WEAP. ACC]?{FIRING RANGE|Point Blank (Touch Range),+5|Close (1/4 Listed Range),+0|Medium (1/2 Listed Range),-2|Long (Listed Range),-4|Extreme (2x Listed Range),-6}[RANGE]?{TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}} {{roll2=[[1d10!+${attrs[skill]}[SKILLBASE]?{EXTRA ACTION|No,+0|Yes,-3}[EXTRA ACTION]-3[JOINT]+?{SECOND WEAPON SELECTION|@{primary_weapon_name},@{primary_wa}|@{secondary_weapon_name},@{secondary_wa}|@{third_weapon_name},@{third_wa}|@{fourth_weapon_name},@{fourth_wa}|@{fifth_weapon_name},@{fifth_wa}|@{sixth_weapon_name},@{sixth_wa}}[WEAP. ACC]?{FIRING RANGE|Point Blank (Touch Range),+5|Close (1/4 Listed Range),+0|Medium (1/2 Listed Range),-2|Long (Listed Range),-4|Extreme (2x Listed Range),-6}[RANGE]?{SECOND TARGET LOCATION|Random,+0|Head,-6|Torso,-1|Arm/Limb,-3|Leg/Tail/Wing,-2}[LOCATION]+?{SECOND ADDITIONAL MODS? (Bright Light, No Vision, Terrain etc)|0}[MOD]]]}}{{rolllocation=[[0]]}}`
let rollResult = await startRoll(rollBase);
let computedValues = await resolveChecks(
@@ -4665,6 +4930,43 @@ on('clicked:templatejointrangedattack', async (ev) => {
);
});
+const doTemplateAttack = async (passin, rollBase) => {
+ let attrs = await asw.getAttrs([passin.weapon_name, "useAttackDefenseChain"]);
+
+ let rollResult = await startRoll(rollBase+'{{rolllocation=[[0]]}}{{passthrough=[[0]]}}{{passthrough2=[[0]]}}{{useAttackDefenseChain=[[0]]}}');
+
+ let computedValues = await resolveChecks(
+ checkFumble(rollResult),
+ checkLocation(rollResult)
+ );
+
+ computedValues.useAttackDefenseChain = attrs["useAttackDefenseChain"];
+
+ // Storing all the passthrough data required for the next roll in an Object helps for larger rolls
+ computedValues.passthrough = rollEscape.escape({
+ weapon_name: attrs[passin.weapon_name],
+ roll: computedValues.roll,
+ weapon_dmg: passin.weapon_dmg,
+ attackmultiplier: !!passin.attackmultiplier ? passin.attackmultiplier : 1,
+ rolllocation: computedValues.rolllocation,
+ locationmultiplier: computedValues.locationmultiplier
+ })
+
+ computedValues.passthrough2 = rollEscape.escape({
+ weapon_name: attrs[passin.weapon_name],
+ roll: computedValues.roll2,
+ weapon_dmg: rollResult.results.damage2?.result,
+ attackmultiplier: !!passin.attackmultiplier ? passin.attackmultiplier : 1,
+ rolllocation: computedValues.rolllocation2,
+ locationmultiplier: computedValues.locationmultiplier2
+ })
+
+ finishRoll(
+ rollResult.rollId,
+ computedValues
+ );
+}
+
//////////////////////////////
// Helper Functions //
//////////////////////////////
@@ -4672,8 +4974,7 @@ on('clicked:templatejointrangedattack', async (ev) => {
const checkFumble = async (rollResult) => {
var rollComputed = rollResult.results.roll.result;
- var roll2Computed = 0;
- var rolllocation = 0;
+ var roll2Computed = undefined;
if(rollResult.results.roll.dice[0] == 1) // We check if the first die is a fumble.
{
@@ -4688,30 +4989,82 @@ const checkFumble = async (rollResult) => {
{
roll2Computed = rollResult.results.roll2.result - rollResult.results.fumble2.result-1;
}
-
- if(rollResult.results.roll2.expression.includes("0[LOCATION]")) {
- rolllocation = 1
- }
}
let results = {
roll: rollComputed,
roll2: roll2Computed,
- rolllocation: rolllocation
};
return results;
}
const checkLocation = async (rollResult) => {
+ var firstRollExpression = rollResult.results.roll.expression;
var rolllocation = 0;
+ var locationmultiplier = undefined;
- if(rollResult.results.roll.expression.includes("0[LOCATION]")) {
- rolllocation = 1
+ //RANDOM
+ if(firstRollExpression.includes("0[LOCATION]")) {
+ rolllocation = 1;
+ }
+
+ //HEAD
+ if(firstRollExpression.includes("6[LOCATION]")) {
+ rolllocation = 0;
+ locationmultiplier = 3;
+ }
+
+ //TORSO
+ if(firstRollExpression.includes("1[LOCATION]")) {
+ rolllocation = 0;
+ locationmultiplier = 1;
+ }
+
+ //ARM+LIMB || LEG + TAIL + WING
+ if(
+ firstRollExpression.includes("3[LOCATION]") ||
+ firstRollExpression.includes("2[LOCATION]")
+ ) {
+ rolllocation = 0;
+ locationmultiplier = 0.5;
+ }
+
+ var secondRollExpression = rollResult.results.roll2?.expression;
+ var rolllocation2 = undefined;
+ var locationmultiplier2 = undefined;
+
+ //RANDOM
+ if(secondRollExpression?.includes("0[LOCATION]")) {
+ rolllocation2 = 1;
+ }
+
+ //HEAD
+ if(secondRollExpression?.includes("6[LOCATION]")) {
+ rolllocation2 = 0;
+ locationmultiplier2 = 3;
+ }
+
+ //TORSO
+ if(secondRollExpression?.includes("1[LOCATION]")) {
+ rolllocation2 = 0;
+ locationmultiplier2 = 1;
+ }
+
+ //ARM+LIMB || LEG + TAIL + WING
+ if(
+ secondRollExpression?.includes("3[LOCATION]") ||
+ secondRollExpression?.includes("2[LOCATION]")
+ ) {
+ rolllocation2 = 0;
+ locationmultiplier2 = 0.5;
}
let results = {
- rolllocation: rolllocation
+ rolllocation: rolllocation,
+ locationmultiplier: locationmultiplier,
+ rolllocation2: rolllocation2,
+ locationmultiplier2: locationmultiplier2
};
return results;
}
@@ -4721,6 +5074,26 @@ const resolveChecks = async (...checks) => {
return results.reduce((r, o) => Object.assign(r, o), {});
}
+const rollEscape = {
+ chars: {
+ '"': '%quot;',
+ ',': '%comma;',
+ ':': '%colon;',
+ '}': '%rcub;',
+ '{': '%lcub;',
+ ')': '%rbrac;',
+ '(': '%lbrac;',
+ },
+ escape(str) {
+ str = (typeof(str) === 'object') ? JSON.stringify(str) : (typeof(str) === 'string') ? str : null;
+ return (str) ? `${str}`.replace(new RegExp(`[${Object.keys(this.chars)}]`, 'g'), (r) => this.chars[r]) : null;
+ },
+ unescape(str) {
+ str = `${str}`.replace(new RegExp(`(${Object.values(this.chars).join('|')})`, 'g'), (r) => Object.entries(this.chars).find(e=>e[1]===r)[0]);
+ return JSON.parse(str);
+ }
+}
+
const asw = (() => {
const setActiveCharacterId = function(charId){
let oldAcid=getActiveCharacterId();
@@ -4980,33 +5353,108 @@ const asw = (() => {
{{/skill}}
+
+
+ | {{title}} |
+ | NAME: {{name}} |
+ | 1ST ATTACK: {{computed::roll}} |
+ {{#rollWasFumble() roll}}
+ {{#^rollWasCrit() roll}}
+
+ |
+ FUMBLED BY: {{fumble}}
+ |
+
+ {{/^rollWasCrit() roll}}
+ {{/rollWasFumble() roll}}
+
+ {{#rollTotal() computed::useAttackDefenseChain 0}}
+ {{#^rollTotal() computed::rolllocation 0}}
+
+ {{/^rollTotal() computed::rolllocation 0}}
+ {{/rollTotal() computed::useAttackDefenseChain 0}}
+ {{#^rollTotal() computed::useAttackDefenseChain 0}}
+
+ {{/^rollTotal() computed::useAttackDefenseChain 0}}
+ {{#computed::roll2}}
+
+ | 2ND ATTACK: {{computed::roll2}} |
+ {{#rollWasFumble() roll2}}
+ {{#^rollWasCrit() roll2}}
+
+ |
+ FUMBLED BY: {{fumble2}}
+ |
+
+ {{/^rollWasCrit() roll2}}
+ {{/rollWasFumble() roll2}}
+
+ {{#rollTotal() computed::useAttackDefenseChain 0}}
+ {{#^rollTotal() computed::rolllocation 0}}
+
+ {{/^rollTotal() computed::rolllocation 0}}
+ {{/rollTotal() computed::useAttackDefenseChain 0}}
+ {{#^rollTotal() computed::useAttackDefenseChain 0}}
+
+ {{/^rollTotal() computed::useAttackDefenseChain 0}}
+ {{/computed::roll2}}
+
+
| {{shield-name}} |
@@ -5040,6 +5488,104 @@ const asw = (() => {
+
+
+ | {{title}} |
+ | NAME: {{name}} |
+ | SKILL ROLL: {{computed::roll}} |
+ {{#rollWasFumble() roll}}
+ {{#^rollWasCrit() roll}}
+
+ |
+ FUMBLED BY: {{fumble}}
+ |
+
+ {{/^rollWasCrit() roll}}
+ {{/rollWasFumble() roll}}
+ {{#hit}}
+
+ |
+ RESULT: {{computed::attackerRoll}} - {{computed::roll}} = {{computed::hit}}
+ |
+
+ {{/hit}}
+ {{#rollGreater() computed::hit 0}}
+ {{#rollTotal() computed::rolllocation 0}}
+
+ |
+ DAMAGE: ({{#^rollTotal() computed::attackmultiplier 1}}{{computed::attackmultiplier}} x {{/^rollTotal() computed::attackmultiplier 1}}{{computed::weapondmg}} - {{computed::armor}}) = {{computed::afterArmor}} x {{computed::locationmultiplier}} + {{computed::critdmg}} ={{computed::damage}}
+ |
+
+ {{/rollTotal() computed::rolllocation 0}}
+ {{/rollGreater() computed::hit 0}}
+
+ {{#rollGreater() computed::hit 0}}
+
+ {{/rollGreater() computed::hit 0}}
+
+
+
+
+
+ | {{title}} |
+
+
+ | NAME: {{name}} |
+
+ {{#rollTotal() roll 1}}| HEAD: If successful, your attack will deal x3 damage. |
{{/rollTotal() roll 1}}
+ {{#rollBetween() roll 2 5}}| TORSO (MONSTER): If successful, your attack will deal standard damage. |
{{/rollBetween() roll 2 5}}
+ {{#rollBetween() roll 6 7}}| RIGHT LIMB (MONSTER): If successful, your attack will deal half damage. |
{{/rollBetween() roll 6 7}}
+ {{#rollBetween() roll 8 9}}| LEFT LIMB (MONSTER): If successful, your attack will deal half damage. |
{{/rollBetween() roll 8 9}}
+ {{#rollTotal() roll 10}}| WING/TAIL (MONSTER): If successful, your attack will deal half damage. |
{{/rollTotal() roll 10}}
+
+ | TARGET LOCATION: {{roll}} |
+
+ {{#computed::damage}}
+
+ |
+ DAMAGE: ({{#^rollTotal() computed::attackmultiplier 1}}{{computed::attackmultiplier}} x {{/^rollTotal() computed::attackmultiplier 1}}{{computed::weapondmg}} - {{computed::armor}}) = {{computed::afterArmor}} x {{computed::locationmultiplier}} + {{computed::critdmg}} ={{computed::damage}}
+ |
+
+ {{/computed::damage}}
+
+
+
+
+
+
+ | {{title}} |
+
+
+ | NAME: {{name}} |
+
+ {{#rollTotal() roll 1}}| HEAD: If successful, your attack will deal x3 damage. |
{{/rollTotal() roll 1}}
+ {{#rollBetween() roll 2 4}}| TORSO (HUMANOID): If successful, your attack will deal standard damage. |
{{/rollBetween() roll 2 4}}
+ {{#rollTotal() roll 5}}| RIGHT ARM (HUMANOID): If successful, your attack will deal half damage. |
{{/rollTotal() roll 5}}
+ {{#rollTotal() roll 6}}| LEFT ARM (HUMANOID): If successful, your attack will deal half damage. |
{{/rollTotal() roll 6}}
+ {{#rollBetween() roll 7 8}}| RIGHT LEG (HUMANOID): If successful, your attack will deal half damage. |
{{/rollBetween() roll 7 8}}
+ {{#rollBetween() roll 9 10}}| LEFT LEG (HUMANOID): If successful, your attack will deal half damage. |
{{/rollBetween() roll 9 10}}
+
+ | TARGET LOCATION: {{roll}} |
+
+ {{#computed::damage}}
+
+ |
+ DAMAGE: ({{#^rollTotal() computed::attackmultiplier 1}}{{computed::attackmultiplier}} x {{/^rollTotal() computed::attackmultiplier 1}}{{computed::weapondmg}} - {{computed::armor}}) = {{computed::afterArmor}} x {{computed::locationmultiplier}} + {{computed::critdmg}} ={{computed::damage}}
+ |
+
+ {{/computed::damage}}
+
+
+