diff --git a/Shadowrun5th/Shadowrun5th.css b/Shadowrun5th/Shadowrun5th.css
index 8501b2a56e..d579dacdea 100644
--- a/Shadowrun5th/Shadowrun5th.css
+++ b/Shadowrun5th/Shadowrun5th.css
@@ -40,7 +40,6 @@ input[type=number]::-webkit-inner-spin-button {
}
.tabs .tab{
- margin-left:-11%;
text-align:center;
color:#eeeeee;
background-color: #550000;
@@ -1774,6 +1773,8 @@ div.collapsible {
color: #aaaaee;
font-weight: normal;
font-size: 13px;
+ width: fit-content;
+ min-width: 25%;
}
.settings span {
@@ -1781,6 +1782,17 @@ div.collapsible {
font-size: 13px;
}
+.sub-settings {
+ padding-left: 10px;
+}
+
+.setting-select {
+ width: fit-content;
+ margin-left: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
summary{
display:list-item;
}
@@ -1815,17 +1827,42 @@ label.settings-checkbox {
text-align:center;
}
+.settings-counterstrike {
+ display: none;
+}
+
+.counterstrikeToggle[value="on"] ~ .settings-counterstrike
+{
+ display:flex;
+}
+
+.settings-parry {
+ display: none;
+}
+
+.parryToggle[value="on"] ~ .settings-parry
+{
+ display:flex;
+}
+
+.settings-riposte {
+ display: none;
+}
+
+.riposteToggle[value="on"] ~ .settings-riposte
+{
+ display:flex;
+}
+
.settings select,
.settings input[type=number]
{
- vertical-align:top;
background-color:#444444;
color:inherit;
border:none;
box-shadow:none;
height:100%;
box-sizing:border-box;
- margin:0px;
text-align:center;
}
diff --git a/Shadowrun5th/Shadowrun5th.html b/Shadowrun5th/Shadowrun5th.html
index 20071be99d..2179958d76 100644
--- a/Shadowrun5th/Shadowrun5th.html
+++ b/Shadowrun5th/Shadowrun5th.html
@@ -13161,21 +13161,82 @@ Click again to switch to Manual and Remote">
Defense settings
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -13406,8 +13467,13 @@ Click again to switch to Manual and Remote">
+
+
+
+
+
@@ -16992,6 +17058,54 @@ const rollReactDodge = async (ev) => {
});
}
+/////////////////////
+// Melee Defense //
+/////////////////////
+
+on('clicked:reactmelee', async (ev) => {
+ await rollMelee(ev);
+});
+const rollMelee = async (ev) => {
+ let character = await asw.getAttrs(['character_name']);
+ let defendOption = await asw.getAttrs(['showBlock', 'showCounterstrike', 'showParry', 'showRiposte']);
+
+ console.log(defendOption);
+
+ if(defendOption.showBlock == "on" && defendOption.showCounterstrike == 0 && defendOption.showParry == 0 && defendOption.showRiposte == 0) {
+ await rollReactBlock(ev);
+ return;
+ }
+
+ if(defendOption.showBlock == 0 && defendOption.showCounterstrike == "on" && defendOption.showParry == 0 && defendOption.showRiposte == 0) {
+ await rollReactCounterstrike(ev);
+ return;
+ }
+
+ if(defendOption.showBlock == 0 && defendOption.showCounterstrike == 0 && defendOption.showParry == "on" && defendOption.showRiposte == 0) {
+ await rollReactParry(ev);
+ return;
+ }
+
+ if(defendOption.showBlock == 0 && defendOption.showCounterstrike == 0 && defendOption.showParry == 0 && defendOption.showRiposte == "on") {
+ await rollReactRiposte(ev);
+ return;
+ }
+
+ let rollBase = `/w ${character.character_name} &{template:meleedefend}{{passthrough=[[0]]}}{{showBlock=[[0]]}}{{showCounterstrike=[[0]]}}{{showParry=[[0]]}}{{showRiposte=[[0]]}}`;
+ if(defendOption.showBlock == 0 && defendOption.showCounterstrike == 0 && defendOption.showParry == 0 && defendOption.showRiposte == 0) {
+ rollBase += '{{configuration= Melee Defense is not configured. Set it under Character -> Defense Settings -> Melee}}'
+ }
+ let defendRoll = await startRoll(rollBase);
+
+ finishRoll(defendRoll.rollId, {
+ passthrough: ev.originalRollId,
+ showBlock: defendOption.showBlock == "on" ? 1 : 0,
+ showCounterstrike: defendOption.showCounterstrike == "on" ? 1 : 0,
+ showParry: defendOption.showParry == "on" ? 1 : 0,
+ showRiposte: defendOption.showRiposte == "on" ? 1 : 0,
+ });
+}
+
on('clicked:reactparry', async (ev) => {
await rollReactParry(ev);
});
@@ -17008,7 +17122,7 @@ const rollReactParry = async (ev) => {
let rollBase = `${attrs.gmroll}&{template:reactdefend}{{defend=Parry}}{{nethits=[[0]]}}{{attackAp=[[0]]}}{{attackDamage=[[0]]}}{{attackDmgType=[[0]]}}{{passthrough=[[0]]}}{{name=${attrs.character_name}}}{{attacker_name=${attackRoll.attacker}}}{{rea=[[${attrs.rea_total}}]]}}{{int=[[${attrs.int_total}]]}}{{bonus=[[${attrs.avoid_bonus}]]}}{{skill=${skill[Object.keys(skill)[0]]}}}{{skilldice=[[${skill[Object.keys(skill)[1]]}+${skill[Object.keys(skill)[2]]}]]}}{{mod=[[?{Situational Modifiers|0}]]}}{{edge=${attrs.edgeroll}}}{{edge_num=[[${attrs.edg_total}]]}}{{limit=[[${limit.parryAccuracy}]]}}{{wounds_num=[[${attrs.wound_total}]]}}{{sustainedspells=[[${attrs.sustainedspells}*2]]}}{{result=[[(${attrs.avoid_total}+[[${skill[Object.keys(skill)[1]]}+${skill[Object.keys(skill)[2]]}]]+?{Situational Modifiers}-[[${attrs.wound_total}]]-[[${attrs.sustainedspells}*2]]+[[${attrs.edgn}*${attrs.edg_total}]])${attrs.standardroll}${attrs.edgeroll}kh[[${limit.parryAccuracy}]] ]]}}{{description= -5 Initiative}}`;
if(limit.parryAccuracy == 0) {
- rollBase += '{{configuration= Set Limit under Character -> Defense Settings}}'
+ rollBase += '{{configuration= Set Limit under Character -> Defense Settings -> Melee}}'
}
let defendRoll = await startRoll(rollBase);
@@ -17032,6 +17146,69 @@ const rollReactParry = async (ev) => {
});
}
+on('clicked:reactriposte', async (ev) => {
+ await rollReactRiposte(ev);
+});
+const rollReactRiposte = async (ev) => {
+ // The data we passed into the button will be stored in the originalRollId key
+ let attackRoll = rollEscape.unescape(ev.originalRollId);
+ let attrs = await asw.getAttrs(['gmroll','character_name','rea_total', 'edgeroll','edg_total','wound_total','sustainedspells','edgn','standardroll']);
+
+ let choosenSkill = await asw.getAttrs(['parrySkill']);
+ let skill = await asw.getAttrs(['skillname' + choosenSkill.parrySkill, 'skillrating' + choosenSkill.parrySkill, 'skillbonus' + choosenSkill.parrySkill]);
+ let limit = await asw.getAttrs(['parryAccuracy']);
+ let riposte = await asw.getAttrs(['riposteDamage', 'riposteAp', 'riposteWeaponDmgtype']);
+
+ let rollBase = `${attrs.gmroll}&{template:reactdefend}{{defend=Riposte}}{{nethits=[[0]]}}{{counterhits=[[0]]}}{{attackAp=[[0]]}}{{attackDamage=[[0]]}}{{attackDmgType=[[0]]}}{{passthrough=[[0]]}}{{name=${attrs.character_name}}}{{attacker_name=${attackRoll.attacker}}}{{rea=[[${attrs.rea_total}}]]}}{{skill=${skill[Object.keys(skill)[0]]}}}{{skilldice=[[${skill[Object.keys(skill)[1]]}+${skill[Object.keys(skill)[2]]}]]}}{{mod=[[?{Situational Modifiers|0}]]}}{{edge=${attrs.edgeroll}}}{{edge_num=[[${attrs.edg_total}]]}}{{limit=[[${limit.parryAccuracy}]]}}{{wounds_num=[[${attrs.wound_total}]]}}{{sustainedspells=[[${attrs.sustainedspells}*2]]}}{{result=[[(${attrs.rea_total}+[[${skill[Object.keys(skill)[1]]}+${skill[Object.keys(skill)[2]]}]]+?{Situational Modifiers}-[[${attrs.wound_total}]]-[[${attrs.sustainedspells}*2]]+[[${attrs.edgn}*${attrs.edg_total}]])${attrs.standardroll}${attrs.edgeroll}kh[[${limit.parryAccuracy}]] ]]}}{{description= -7 Initiative, +2 DV taken (already included)}}`;
+
+ if(limit.parryAccuracy == 0 || riposte.riposteDamage == 0) {
+ rollBase += '{{configuration= Configure Riposte under Character -> Defense Settings -> Melee}}'
+ }
+
+ let defendRoll = await startRoll(rollBase);
+
+ let defendSuccesses = defendRoll.results.result.result;
+ let nethits = attackRoll.attackHits - defendSuccesses;
+ let counterhits = defendSuccesses - attackRoll.attackHits;
+
+ let attackDamage;
+ if(nethits > counterhits) {
+ attackDamage = attackRoll.attackDV + nethits + 2;
+ } else {
+ attackDamage = parseInt(riposte.riposteDamage) + parseInt(counterhits);
+ }
+
+ let attackAp;
+ if(nethits > counterhits) {
+ attackAp = attackRoll.attackAp;
+ } else {
+ attackAp = riposte.riposteAp;
+ }
+
+ let attackDmgType;
+ if(nethits > counterhits) {
+ attackDmgType = attackRoll.attackDmgType;
+ } else {
+ attackDmgType = riposte.riposteWeaponDmgtype;
+ }
+
+
+ let rollData = {
+ attackAp: attackAp,
+ attackDamage: attackDamage,
+ attackDmgType: attackDmgType,
+ }
+
+ finishRoll(defendRoll.rollId, {
+ nethits: nethits,
+ counterhits: counterhits,
+ attackAp: attackAp,
+ attackDamage: attackDamage,
+ attackDmgType: attackDmgType,
+ passthrough: rollEscape.escape(rollData),
+ });
+}
+
on('clicked:reactblock', async (ev) => {
await rollReactBlock(ev);
});
@@ -17064,6 +17241,66 @@ const rollReactBlock = async (ev) => {
});
}
+on('clicked:reactcounterstrike', async (ev) => {
+ await rollReactCounterstrike(ev);
+});
+const rollReactCounterstrike = async (ev) => {
+ // The data we passed into the button will be stored in the originalRollId key
+ let attackRoll = rollEscape.unescape(ev.originalRollId);
+ let attrs = await asw.getAttrs(['gmroll','character_name','rea_total', 'edgeroll','edg_total','wound_total','sustainedspells','edgn','standardroll']);
+ let skill = await asw.getAttrs(['skillrating51', 'skillbonus51']);
+ let limit = await asw.getAttrs(['skilllimitbonus51', 'physical_total']);
+ let counterstrike = await asw.getAttrs(['counterstrikeDamage', 'counterstrikeAp', 'counterstrikeWeaponDmgtype']);
+
+ let rollBase = `${attrs.gmroll}&{template:reactdefend}{{defend=Block}}{{nethits=[[0]]}}{{counterhits=[[0]]}}{{attackAp=[[0]]}}{{attackDamage=[[0]]}}{{attackDmgType=[[0]]}}{{passthrough=[[0]]}}{{name=${attrs.character_name}}}{{attacker_name=${attackRoll.attacker}}}{{rea=[[${attrs.rea_total}}]]}}{{skill=@{skillname51}}}{{skilldice=[[${skill.skillrating51}+${skill.skillbonus51}]]}}{{mod=[[?{Situational Modifiers|0}]]}}{{edge=${attrs.edgeroll}}}{{edge_num=[[${attrs.edg_total}]]}}{{limit=[[${limit.physical_total}+${limit.skilllimitbonus51}]]}}{{wounds_num=[[${attrs.wound_total}]]}}{{sustainedspells=[[${attrs.sustainedspells}*2]]}}{{result=[[(${attrs.rea_total}+[[${skill.skillrating51}+${skill.skillbonus51}]]+?{Situational Modifiers}-[[${attrs.wound_total}]]-[[${attrs.sustainedspells}*2]]+[[${attrs.edgn}*${attrs.edg_total}]])${attrs.standardroll}${attrs.edgeroll}kh[[${limit.physical_total}+${limit.skilllimitbonus51}]] ]]}}{{description= -7 Initiative}}`;
+ if(counterstrike.counterstrikeDamage == 0) {
+ rollBase += '{{configuration= Configure Counterstrike under Character -> Defense Settings -> Melee}}'
+ }
+ console.log(rollBase);
+ let defendRoll = await startRoll(rollBase);
+
+ let defendSuccesses = defendRoll.results.result.result;
+ let nethits = attackRoll.attackHits - defendSuccesses;
+ let counterhits = defendSuccesses - attackRoll.attackHits;
+
+ let attackDamage;
+ if(nethits > counterhits) {
+ attackDamage = attackRoll.attackDV + nethits + 2;
+ } else {
+ attackDamage = parseInt(counterstrike.counterstrikeDamage) + parseInt(counterhits);
+ }
+
+ let attackAp;
+ if(nethits > counterhits) {
+ attackAp = attackRoll.attackAp;
+ } else {
+ attackAp = counterstrike.counterstrikeAp;
+ }
+
+ let attackDmgType;
+ if(nethits > counterhits) {
+ attackDmgType = attackRoll.attackDmgType;
+ } else {
+ attackDmgType = counterstrike.counterstrikeWeaponDmgtype;
+ }
+
+
+ let rollData = {
+ attackAp: attackAp,
+ attackDamage: attackDamage,
+ attackDmgType: attackDmgType,
+ }
+
+ finishRoll(defendRoll.rollId, {
+ nethits: nethits,
+ counterhits: counterhits,
+ attackAp: attackAp,
+ attackDamage: attackDamage,
+ attackDmgType: attackDmgType,
+ passthrough: rollEscape.escape(rollData),
+ });
+}
+
on('clicked:reactsoak', async (ev) => {
await rollReactSoak(ev);
});
@@ -17074,7 +17311,8 @@ const rollReactSoak = async (ev) => {
let attrs = await asw.getAttrs(['gmroll','character_name','bod_total', 'armor_total', 'soak_bonus', 'edgeroll','edg_total','edgn','standardroll']);
//AP only reduces armor
- let armorPenetration = attrs.armor_total + dmgRoll.attackAp < 0 ? -attrs.armor_total : dmgRoll.attackAp;
+ let armorPenetration = parseInt(attrs.armor_total) + parseInt(dmgRoll.attackAp) < 0 ? -attrs.armor_total : dmgRoll.attackAp;
+ console.log(armorPenetration);
if(attrs.armor_total == 0){
armorPenetration = 0;
@@ -18078,10 +18316,7 @@ var TAS = TAS || (function(){
[Dodge](~selected|reactdodge||{{computed::passthrough}})
- [Parry](~selected|reactparry||{{computed::passthrough}})
-
-
- [Block](~selected|reactblock||{{computed::passthrough}})
+ [Melee](~selected|reactmelee||{{computed::passthrough}})
@@ -18592,9 +18827,11 @@ var TAS = TAS || (function(){
| > Reaction({{rea}}) |
-
- | > Intuition({{int}}) |
-
+ {{#int}}
+
+ | > Intuition({{int}}) |
+
+ {{/int}}
{{#^rollTotal() bonus 0}}
| > Bonus({{bonus}}) |
@@ -18650,125 +18887,195 @@ var TAS = TAS || (function(){
{{/edge}}
{{^edge}}
{{#limit}}
-
- |
-
- |
-
-
- |
- Limit
- |
-
-
- |
- > Limit({{limit}})
- |
-
+
+ |
+
+ |
+
+
+ |
+ Limit
+ |
+
+
+ |
+ > Limit({{limit}})
+ |
+
{{/limit}}
{{/edge}}
{{#result}}
-
- |
-
- |
-
-
- |
- Result
- |
-
- {{#rollGreater() result 1}}
-
- | {{result}} Successes |
-
- {{/rollGreater() result 1}}
- {{#rollTotal() result 1}}
-
- | {{result}} Success |
-
- {{/rollTotal() result 1}}
- {{#rollTotal() result 0}}
-
- | {{result}} Successes |
-
- {{/rollTotal() result 0}}
+
+ |
+
+ |
+
+
+ |
+ Result
+ |
+
+ {{#rollGreater() result 1}}
+
+ | {{result}} Successes |
+
+ {{/rollGreater() result 1}}
+ {{#rollTotal() result 1}}
+
+ | {{result}} Success |
+
+ {{/rollTotal() result 1}}
+ {{#rollTotal() result 0}}
+
+ | {{result}} Successes |
+
+ {{/rollTotal() result 0}}
{{/result}}
+
{{#nethits}}
-
- |
-
- |
-
-
- |
- Attacker Nethits
- |
-
- {{#rollGreater() computed::nethits 0}}
-
- | {{computed::nethits}} Nethits |
-
-
- |
- Damage taken
- |
-
-
- | AP({{computed::attackAp}}) ~ DV({{computed::attackDamage}}{{computed::attackDmgType}}) |
-
- {{/rollGreater() computed::nethits 0}}
- {{#^rollGreater() computed::nethits 0}}
-
- | 0 Nethits |
-
- {{/^rollGreater() computed::nethits 0}}
+
+ |
+
+ |
+
+
+ |
+ Attacker Nethits
+ |
+
+ {{#rollGreater() computed::nethits 0}}
+
+ | {{computed::nethits}} Nethits |
+
+
+ |
+ Damage taken
+ |
+
+
+ | AP({{computed::attackAp}}) ~ DV({{computed::attackDamage}}{{computed::attackDmgType}}) |
+
+ {{/rollGreater() computed::nethits 0}}
+ {{#^rollGreater() computed::nethits 0}}
+
+ | 0 Nethits |
+
+ {{/^rollGreater() computed::nethits 0}}
{{/nethits}}
+ {{#counterhits}}
+ {{#rollGreater() computed::counterhits 0}}
+
+ |
+
+ |
+
+
+ |
+ Defender Nethits
+ |
+
+
+ | {{computed::counterhits}} Nethits |
+
+
+ |
+ Damage taken
+ |
+
+
+ | AP({{computed::attackAp}}) ~ DV({{computed::attackDamage}}{{computed::attackDmgType}}) |
+
+ {{/rollGreater() computed::counterhits 0}}
+ {{/counterhits}}
+
{{#description}}
-
- |
-
- |
-
-
- |
- Description
- |
-
-
- |
- {{description}}
- |
-
+
+ |
+
+ |
+
+
+ |
+ Description
+ |
+
+
+ |
+ {{description}}
+ |
+
{{/description}}
{{#configuration}}
-
- |
-
- |
-
-
- |
- Configuration
- |
-
-
- |
- {{configuration}}
- |
-
+
+ |
+
+ |
+
+
+ |
+ Configuration
+ |
+
+
+ |
+ {{configuration}}
+ |
+
{{/configuration}}
{{#rollGreater() computed::nethits 0}}
+ {{#^rollTotal() computed::passthrough 0}}
+
+ {{/^rollTotal() computed::passthrough 0}}
+ {{/rollGreater() computed::nethits 0}}
+
+ {{#rollGreater() computed::counterhits 0}}
{{#^rollTotal() computed::passthrough 0}}
{{/^rollTotal() computed::passthrough 0}}
- {{/rollGreater() computed::nethits 0}}
+ {{/rollGreater() computed::counterhits 0}}
+
+
+
+
+ {{#configuration}}
+ {{configuration}}
+ {{/configuration}}
+
+