diff --git a/TheWitcher/translation.json b/TheWitcher/translation.json index 7bac57b025..4205333e1d 100644 --- a/TheWitcher/translation.json +++ b/TheWitcher/translation.json @@ -297,6 +297,12 @@ "show-jointattacks-u": "Show joint attacks", "character-u": "CHARACTER", "show-magic-and-spells": "Show magic and spell tab", + "use-pc-defense-bases": "Show defense bases", + "use-pc-attack-defense-chain": "Use PC attack-defense-chain", "background-u": "BACKGROUND", - "show-multiple-reputations": "Show multiple reputations" + "show-multiple-reputations": "Show multiple reputations", + "defense-bases": "DEFENSE BASES", + "dodge-base": "DODGE BASE", + "reposition-base": "REPOSITION BASE", + "block-base": "BLOCK BASE" } diff --git a/TheWitcher/witchersheet.css b/TheWitcher/witchersheet.css index 56bea24351..2182378f65 100644 --- a/TheWitcher/witchersheet.css +++ b/TheWitcher/witchersheet.css @@ -368,6 +368,35 @@ input.repeating_npcskill_score { box-shadow: inset 0px 0px 4px 0px #262626; } +/* Left Side */ + +.special-stack-grid { + margin-left: -10px; +} + +.npc-defenses { + width: 520px; +} + +.npc-defense-container { + display: flex; + justify-content: space-evenly; +} + +.npc-defenses .defense-title { + padding-right: 0px; +} + +.npc-defenses .defense-name { + padding-left: 0px; +} + +.npc-defense { + width: min-content; + display: block; + text-align: center; +} + /* ----------- */ .charsheet { @@ -4990,6 +5019,11 @@ textarea.armor-text { color: #ebebeb; } +/* -- PC Defense Bases */ +.pc-defense-base:not(:checked) ~ .pc-defense-base { + display: none; +} + /* Spells & Signs*/ span.repeating_spell_gradient { @@ -6618,6 +6652,66 @@ button[type=roll].repeating_npc_weapon_roll:active { vertical-align: middle; } +/* DEFENSE BASES */ + +label.defense-title { + display: inline-block; + margin-top: 10px; + margin-bottom: 10px; + padding-left: 12px; + padding-top: 4px; + padding-bottom: 4px; + font-weight: 1; + font-size: 12px; + color: #FFF; + margin-bottom: 2px; + border-radius: 15px; + background: #444444; +} + +span.defense-name { + display: inline-block; + line-height: 0.65; + padding-left: 15px; + width: 130px; + font-size: 14px; +} + +.charsheet button[type=action].defense_skill_roll::before { + font-family: "dicefontd10"; + content: "0"; + font-size: 17px; +} + +button[type=action].defense_skill_roll { + color: #000; + display: inline-block; + background-color: #e8e8e8; + text-align: center; + font-weight: bold; + margin-top: 5px; + width:18px; + height:18px; + border-radius: 100px; + border-color: #000; + border: 1px solid; + box-shadow: inset 1px 1px 1px 1px rgba(0,0,0,0.30); +} + +button[type=action].defense_skill_roll:hover { + color: #878787; + border-color: #878787; + border: 1px solid; +} + +button[type=action].defense_skill_roll:active { + color: #d6d6d6; + border-color: #d6d6d6; + border: 1px solid; + box-shadow: inset 2px 1px 1px 1px #878787; + +} + /* ROLL TEMPLATES */ .sheet-rolltemplate-npcweapon table, @@ -6633,12 +6727,16 @@ button[type=roll].repeating_npc_weapon_roll:active { .sheet-rolltemplate-blueskill table, .sheet-rolltemplate-mainskill table, .sheet-rolltemplate-doubleroll table, +.sheet-rolltemplate-weaponattack table, .sheet-rolltemplate-singleroll table, .sheet-rolltemplate-saveroll table, .sheet-rolltemplate-target table, +.sheet-rolltemplate-humanlocation table, +.sheet-rolltemplate-monsterlocation table, .sheet-rolltemplate-fumble table, .sheet-rolltemplate-save table, -.sheet-rolltemplate-initiative table { +.sheet-rolltemplate-initiative table, +.sheet-rolltemplate-npcdefense table { width: 95%; border: 1px solid; color: black; @@ -6659,12 +6757,16 @@ button[type=roll].repeating_npc_weapon_roll:active { .sheet-rolltemplate-blueskill th, .sheet-rolltemplate-mainskill th, .sheet-rolltemplate-doubleroll th, +.sheet-rolltemplate-weaponattack th, .sheet-rolltemplate-singleroll th, .sheet-rolltemplate-saveroll th, .sheet-rolltemplate-target th, +.sheet-rolltemplate-humanlocation th, +.sheet-rolltemplate-monsterlocation th, .sheet-rolltemplate-fumble th, .sheet-rolltemplate-save th, -.sheet-rolltemplate-initiative th { +.sheet-rolltemplate-initiative th, +.sheet-rolltemplate-npcdefense th { color: #ffffff; padding: 2px; text-align: center; @@ -6686,11 +6788,15 @@ button[type=roll].repeating_npc_weapon_roll:active { .sheet-rolltemplate-blueskill .userscript-template-title, .sheet-rolltemplate-mainskill .userscript-template-title, .sheet-rolltemplate-doubleroll .userscript-template-title, +.sheet-rolltemplate-weaponattack .userscript-template-title, .sheet-rolltemplate-singleroll .userscript-template-title, .sheet-rolltemplate-saveroll .userscript-template-title, .sheet-rolltemplate-target .userscript-template-title, +.sheet-rolltemplate-humanlocation .userscript-template-title, +.sheet-rolltemplate-monsterlocation .userscript-template-title, .sheet-rolltemplate-fumble .userscript-template-title, .sheet-rolltemplate-save .userscript-template-title, +.sheet-rolltemplate-npcdefense .userscript-template-title, .sheet-rolltemplate-initiative .userscript-template-title { font-weight: bold; } @@ -6708,11 +6814,15 @@ button[type=roll].repeating_npc_weapon_roll:active { .sheet-rolltemplate-blueskill td, .sheet-rolltemplate-mainskill td, .sheet-rolltemplate-doubleroll td, +.sheet-rolltemplate-weaponattack td, .sheet-rolltemplate-singleroll td, .sheet-rolltemplate-saveroll td, .sheet-rolltemplate-target td, +.sheet-rolltemplate-humanlocation td, +.sheet-rolltemplate-monsterlocation td, .sheet-rolltemplate-fumble td, .sheet-rolltemplate-save td, +.sheet-rolltemplate-npcdefense td, .sheet-rolltemplate-initiative td { padding: 8px; border-bottom: 1px solid black; @@ -6731,11 +6841,15 @@ button[type=roll].repeating_npc_weapon_roll:active { .sheet-rolltemplate-blueskill .inlinerollresult, .sheet-rolltemplate-mainskill .inlinerollresult, .sheet-rolltemplate-doubleroll .inlinerollresult, +.sheet-rolltemplate-weaponattack .inlinerollresult, .sheet-rolltemplate-singleroll .inlinerollresult, .sheet-rolltemplate-saveroll .inlinerollresult, .sheet-rolltemplate-target .inlinerollresult, +.sheet-rolltemplate-humanlocation .inlinerollresult, +.sheet-rolltemplate-monsterlocation .inlinerollresult, .sheet-rolltemplate-fumble .inlinerollresult, .sheet-rolltemplate-save .inlinerollresult, +.sheet-rolltemplate-npcdefense .inlinerollresult, .sheet-rolltemplate-initiative .inlinerollresult { display: inline-block; min-width: 1.5em; @@ -6789,15 +6903,21 @@ button[type=roll].repeating_npc_weapon_roll:active { /* Target */ -.sheet-rolltemplate-target th { +.sheet-rolltemplate-target th, +.sheet-rolltemplate-humanlocation th, +.sheet-rolltemplate-monsterlocation th { background-color: #546696; } -.sheet-rolltemplate-target tr:nth-child(odd) { +.sheet-rolltemplate-target tr:nth-child(odd), +.sheet-rolltemplate-humanlocation tr:nth-child(odd), +.sheet-rolltemplate-monsterlocation tr:nth-child(odd) { background-color: #b1b4d8; } -.sheet-rolltemplate-target tr:nth-child(even) { +.sheet-rolltemplate-target tr:nth-child(even), +.sheet-rolltemplate-humanlocation tr:nth-child(even), +.sheet-rolltemplate-monsterlocation tr:nth-child(even) { background-color: #e0e0e0; } @@ -6817,15 +6937,18 @@ button[type=roll].repeating_npc_weapon_roll:active { /* singleroll */ -.sheet-rolltemplate-singleroll th { +.sheet-rolltemplate-singleroll th, +.sheet-rolltemplate-npcdefense th { background-color: #595959; } -.sheet-rolltemplate-singleroll tr:nth-child(odd) { +.sheet-rolltemplate-singleroll tr:nth-child(odd), +.sheet-rolltemplate-npcdefense tr:nth-child(odd) { background-color: #b7b7b7; } -.sheet-rolltemplate-singleroll tr:nth-child(even) { +.sheet-rolltemplate-singleroll tr:nth-child(even), +.sheet-rolltemplate-npcdefense tr:nth-child(even) { background-color: #e0e0e0; } @@ -6843,6 +6966,20 @@ button[type=roll].repeating_npc_weapon_roll:active { background-color: #e0e0e0; } +/* weaponattack */ + +.sheet-rolltemplate-weaponattack th { + background-color: #595959; +} + +.sheet-rolltemplate-weaponattack tr:nth-child(odd) { + background-color: #b7b7b7; +} + +.sheet-rolltemplate-weaponattack tr:nth-child(even) { + background-color: #e0e0e0; +} + /* Prof skills */ .sheet-rolltemplate-mainskill th { @@ -6981,6 +7118,7 @@ button[type=roll].repeating_npc_weapon_roll:active { /* API Button */ .sheet-rolltemplate-singleroll a[href^="~"], .sheet-rolltemplate-doubleroll a[href^="~"], +.sheet-rolltemplate-weaponattack a[href^="~"], .sheet-rolltemplate-mainskill a[href^="~"], .sheet-rolltemplate-blueskill a[href^="~"], .sheet-rolltemplate-greenskill a[href^="~"], @@ -7004,4 +7142,11 @@ button[type=roll].repeating_npc_weapon_roll:active { border-radius: 30px; background-color: #968754; color: white; +} + +.sheet-rolltemplate-npcdefense .sheet-rolltemplate-calculation .inlinerollresult, +.sheet-rolltemplate-humanlocation .sheet-rolltemplate-calculation .inlinerollresult, +.sheet-rolltemplate-monsterlocation .sheet-rolltemplate-calculation .inlinerollresult { + border: none; + background-color: inherit; } \ No newline at end of file diff --git a/TheWitcher/witchersheet.html b/TheWitcher/witchersheet.html index d419ead0a2..32f6dae4c6 100644 --- a/TheWitcher/witchersheet.html +++ b/TheWitcher/witchersheet.html @@ -2363,207 +2363,227 @@ | -
- - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- -
- -
- -
- - -
-
-
- - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - - - - -
-
-
-
- - - - - - -
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ + +
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+ +
+ +
+
+ Dodge Base + + +
+
+ Reposition Base + + +
+
+ Block Base + + +
+
@@ -3352,6 +3372,14 @@ Show Magic & Spells tab + +

@@ -3445,6 +3473,26 @@

+
+ +
+
+ Dodge Base + + +
+
+ Reposition Base + + +
+
+ Block Base + + +
+
+
@@ -3587,7 +3635,27 @@

-
+
+ +
+
+ Dodge Base + + +
+
+ Reposition Base + + +
+
+ Block Base + + +
+
+
+
@@ -3611,7 +3679,7 @@
-
+
@@ -3625,7 +3693,7 @@
-
+
@@ -3647,7 +3715,7 @@
-
+
@@ -3698,6 +3766,12 @@ + + + + + +
@@ -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 = (() => {
{{#rollTotal() computed::singlestrike 1}}
- [Single Strike](~{{name}}|templatesinglestrike||{{skill}}) + [Single Strike](~{{name}}|templatesinglestrike||{{computed::passthrough}})
{{/rollTotal() computed::singlestrike 1}} {{#rollTotal() computed::faststrike 1}}
- [Fast Strike](~{{name}}|templatefastattack||{{skill}}) + [Fast Strike](~{{name}}|templatefastattack||{{computed::passthrough}})
{{/rollTotal() computed::faststrike 1}} {{#rollTotal() computed::strongstrike 1}}
- [Strong Strike](~{{name}}|templatestrongattack||{{skill}}) + [Strong Strike](~{{name}}|templatestrongattack||{{computed::passthrough}})
{{/rollTotal() computed::strongstrike 1}} {{#rollTotal() computed::jointmeleeattack 1}}
- [Joint Attack](~{{name}}|templatejointmeleeattack||{{skill}}) + [Joint Attack](~{{name}}|templatejointmeleeattack||{{computed::passthrough}})
{{/rollTotal() computed::jointmeleeattack 1}} {{#rollTotal() computed::jointrangedattack 1}}
- [Joint Attack](~{{name}}|templatejointrangedattack||{{skill}}) + [Joint Attack](~{{name}}|templatejointrangedattack||{{computed::passthrough}})
{{/rollTotal() computed::jointrangedattack 1}}
{{/skill}} + + + + + + {{#rollWasFumble() roll}} + {{#^rollWasCrit() roll}} + + + + {{/^rollWasCrit() roll}} + {{/rollWasFumble() roll}} +
{{title}}
NAME: {{name}}
1ST ATTACK: {{computed::roll}}
+ FUMBLED BY: {{fumble}} +
+ {{#rollTotal() computed::useAttackDefenseChain 0}} + {{#^rollTotal() computed::rolllocation 0}} +
+
+ [Location](~{{name}}|location) +
+
+ {{/^rollTotal() computed::rolllocation 0}} + {{/rollTotal() computed::useAttackDefenseChain 0}} + {{#^rollTotal() computed::useAttackDefenseChain 0}} +
+
+ [Dodge](~selected|reactdodge||{{computed::passthrough}}) +
+
+ [Reposition](~selected|reactreposition||{{computed::passthrough}}) +
+
+ [Block](~selected|reactblock||{{computed::passthrough}}) +
+
+ {{/^rollTotal() computed::useAttackDefenseChain 0}} + {{#computed::roll2}} + + + {{#rollWasFumble() roll2}} + {{#^rollWasCrit() roll2}} + + + + {{/^rollWasCrit() roll2}} + {{/rollWasFumble() roll2}} +
2ND ATTACK: {{computed::roll2}}
+ FUMBLED BY: {{fumble2}} +
+ {{#rollTotal() computed::useAttackDefenseChain 0}} + {{#^rollTotal() computed::rolllocation 0}} +
+
+ [Location](~{{name}}|location) +
+
+ {{/^rollTotal() computed::rolllocation 0}} + {{/rollTotal() computed::useAttackDefenseChain 0}} + {{#^rollTotal() computed::useAttackDefenseChain 0}} +
+
+ [Dodge](~selected|reactdodge||{{computed::passthrough2}}) +
+
+ [Reposition](~selected|reactreposition||{{computed::passthrough2}}) +
+
+ [Block](~selected|reactblock||{{computed::passthrough2}}) +
+
+ {{/^rollTotal() computed::useAttackDefenseChain 0}} + {{/computed::roll2}} +
+ @@ -5040,6 +5488,104 @@ const asw = (() => {
{{shield-name}}
+ + + + + + {{#rollWasFumble() roll}} + {{#^rollWasCrit() roll}} + + + + {{/^rollWasCrit() roll}} + {{/rollWasFumble() roll}} + {{#hit}} + + + + {{/hit}} + {{#rollGreater() computed::hit 0}} + {{#rollTotal() computed::rolllocation 0}} + + + + {{/rollTotal() computed::rolllocation 0}} + {{/rollGreater() computed::hit 0}} +
{{title}}
NAME: {{name}}
SKILL ROLL: {{computed::roll}}
+ FUMBLED BY: {{fumble}} +
+ RESULT: {{computed::attackerRoll}} - {{computed::roll}} = {{computed::hit}} +
+ 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}} +
+ {{#rollGreater() computed::hit 0}} +
+ {{#^rollTotal() computed::rolllocation 0}} +
+ [Location(Monster)](~{{name}}|monsterlocation||{{computed::passthrough}}) +
+
+ [Location(Humanoid)](~{{name}}|humanlocation||{{computed::passthrough}}) +
+ {{/^rollTotal() computed::rolllocation 0}} +
+ {{/rollGreater() computed::hit 0}} +
+ + + + + + + + + + {{#rollTotal() roll 1}}{{/rollTotal() roll 1}} + {{#rollBetween() roll 2 5}}{{/rollBetween() roll 2 5}} + {{#rollBetween() roll 6 7}}{{/rollBetween() roll 6 7}} + {{#rollBetween() roll 8 9}}{{/rollBetween() roll 8 9}} + {{#rollTotal() roll 10}}{{/rollTotal() roll 10}} + + + + {{#computed::damage}} + + + + {{/computed::damage}} +
{{title}}
NAME: {{name}}
HEAD: If successful, your attack will deal x3 damage.
TORSO (MONSTER): If successful, your attack will deal standard damage.
RIGHT LIMB (MONSTER): If successful, your attack will deal half damage.
LEFT LIMB (MONSTER): If successful, your attack will deal half damage.
WING/TAIL (MONSTER): If successful, your attack will deal half damage.
TARGET LOCATION: {{roll}}
+ 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() roll 1}}{{/rollTotal() roll 1}} + {{#rollBetween() roll 2 4}}{{/rollBetween() roll 2 4}} + {{#rollTotal() roll 5}}{{/rollTotal() roll 5}} + {{#rollTotal() roll 6}}{{/rollTotal() roll 6}} + {{#rollBetween() roll 7 8}}{{/rollBetween() roll 7 8}} + {{#rollBetween() roll 9 10}}{{/rollBetween() roll 9 10}} + + + + {{#computed::damage}} + + + + {{/computed::damage}} +
{{title}}
NAME: {{name}}
HEAD: If successful, your attack will deal x3 damage.
TORSO (HUMANOID): If successful, your attack will deal standard damage.
RIGHT ARM (HUMANOID): If successful, your attack will deal half damage.
LEFT ARM (HUMANOID): If successful, your attack will deal half damage.
RIGHT LEG (HUMANOID): If successful, your attack will deal half damage.
LEFT LEG (HUMANOID): If successful, your attack will deal half damage.
TARGET LOCATION: {{roll}}
+ 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}} +
+
+