diff --git a/package.json b/package.json index 2dbf980..fe57f49 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "planar-vagabond", - "version": "0.14.4", + "version": "0.14.5", "description": "", "main": "index.js", "scripts": { diff --git a/src/assets/fragments/classes/combat-expertise.md b/src/assets/fragments/classes/combat-expertise.md new file mode 100644 index 0000000..95e0637 --- /dev/null +++ b/src/assets/fragments/classes/combat-expertise.md @@ -0,0 +1,6 @@ +1. **Berserker**: After striking a killing blow while in melee with multiple foes, may immediately make a free attack against another foe at –2. +2. **Dual-Wielder**: Gains [Dual Wielding](/rules/combat.md#dual-wielding). If DEX > 13 and wielding both a one-handed weapon and a Light weapon in off hand, may make an attack with each one. +3. **Hunter**: +1 to attack and damage rolls when in combat with foes of a specific type (ex: undead, clerics, dragons, giants, etc.). The type must be chosen when this talent is selected. +4. **Leader**: Mercenaries or retainers under command and within 60' gain a +1 bonus to morale and loyalty checks. All allies within 60' get +1 bonus to saves against fear effects. +5. **Specialist**: Expertise with a specific type of weapon (ex: whips, one-handed swords, crossbows, etc.) grants +1 to attack and damage rolls when using one. +6. **Tank**: When in melee with a foe, any attacks the foe makes at anyone else get –2. diff --git a/src/assets/scripts/tools.js b/src/assets/scripts/tools.js index 84c79d6..4eed477 100644 --- a/src/assets/scripts/tools.js +++ b/src/assets/scripts/tools.js @@ -47,10 +47,12 @@ const addRollerForm = () => { .map((roll) => { const result = dice.roll(roll), stringifiedResult = dice.stringify(result); + /* console.log( { roll, result, stringifiedResult }, isExpressionRE.test(roll) ); + */ return `${stringifiedResult.replaceAll( '!!!mods listing not yet complete!!!', '' @@ -163,17 +165,20 @@ const shuffleContainer = (parentId) => { setWildernessButton?.addEventListener('click', (e) => { e.preventDefault(); - setContainerContents( - 'js-complicationList', - [ - '
  • Encounter
  • ', - '
  • Encounter *
  • ', - '
  • Signs / Portents
  • ', - '
  • Locality / Weather (2d4)
  • ', - '
  • Lose Way (1d3 hrs)
  • ', - '
  • No Complications
  • ', - ].join('\n') - ); + (distanceText = `${ + distanceTotal * 10 + } yards
    [${distanceRolls}]`), + setContainerContents( + 'js-complicationList', + [ + '
  • Encounter
  • ', + '
  • Encounter *
  • ', + '
  • Signs / Portents
  • ', + '
  • Locality / Weather (2d4)
  • ', + '
  • Lose Way (1d3 hrs)
  • ', + '
  • No Complications
  • ', + ].join('\n') + ); }); }, addAstralComplicationForm = () => { @@ -199,7 +204,15 @@ const reactionButtonVal = [ 'Amiable', 'Friendly!', ], - rollEncounter = (sides = 1) => { + rollDistance = (type = 'W', hasDoubles) => { + // roll 2d6 for starting dungeon distance + if (type !== 'W') return rollDice(6, 2); + + // roll 4d6 (or 1d4, if any doubles above) for starting wilderness distance + return hasDoubles ? rollDice(4) : rollDice(6, 4); + }, + rollEncounter = (sides = 1, type = 'W') => { + type = `${type}`.toUpperCase(); // roll 3d8 for monsters, note doubles (first 2 / last 2 / first & last) / triples const [monsterTotal, ...monsterRolls] = rollDice(sides, 3), hasStealthParty = @@ -210,17 +223,18 @@ const reactionButtonVal = [ hasDoubles = hasStealthParty || hasStealthMonster, // roll 2d6 for reaction, note string & numeric value [reactionTotal, ...reactionRolls] = rollDice(6, 2), - // roll 4d6 (or 1d4, if any doubles above) for starting distance - [distanceTotal, ...distanceRolls] = hasDoubles - ? rollDice(4) - : rollDice(6, 4), + // roll for distance, see above + [distanceTotal, ...distanceRolls] = rollDistance(type, hasDoubles), // Derived string and scores reactionText = `${ reactionButtonVal[reactionTotal - 2] } (${reactionTotal})
    [${reactionRolls}]`, - distanceText = `${ - distanceTotal * 10 - } yards
    [${distanceRolls}]`, + distanceText = [ + `${distanceTotal * 10}`, + type === 'W' ? ' yards' : "'", + '', + `
    [${distanceRolls}]`, + ].join(''), encounterOutput = document.getElementById('js-encounterOutput'), outputTable = document.createElement('table'); @@ -235,7 +249,9 @@ const reactionButtonVal = [ const monsterRollText = `${monsterTotal}${surpriseText}
    [${monsterRolls}]`; outputTable.innerHTML = [ - `Encounter (3d${sides}): ${dayjs().format( + `${ + type === 'W' ? 'Wilderness' : 'Dungeon' + } Encounter
    (3d${sides}): ${dayjs().format( 'YYYY-MM-DD HH:mm:ss' )}`, '', @@ -253,13 +269,11 @@ const reactionButtonVal = [ formControls = document.createElement('div'); formControls.innerHTML = [ '
    ', - /* '
    Dungeon
    ', '', '', '', '
    ', - */ '
    Wilderness
    ', '', '', @@ -291,32 +305,32 @@ const reactionButtonVal = [ if (rollDungeonEncounter3d4Button) rollDungeonEncounter3d4Button.addEventListener('click', (e) => { e.preventDefault(); - rollEncounter(4); + rollEncounter(4, 'D'); }); if (rollDungeonEncounter3d6Button) rollDungeonEncounter3d6Button.addEventListener('click', (e) => { e.preventDefault(); - rollEncounter(6); + rollEncounter(6, 'D'); }); if (rollDungeonEncounter3d8Button) rollDungeonEncounter3d8Button.addEventListener('click', (e) => { e.preventDefault(); - rollEncounter(8); + rollEncounter(8, 'D'); }); if (rollWildernessEncounter3d4Button) rollWildernessEncounter3d4Button.addEventListener('click', (e) => { e.preventDefault(); - rollEncounter(4); + rollEncounter(4, 'W'); }); if (rollWildernessEncounter3d6Button) rollWildernessEncounter3d6Button.addEventListener('click', (e) => { e.preventDefault(); - rollEncounter(6); + rollEncounter(6, 'W'); }); if (rollWildernessEncounter3d8Button) rollWildernessEncounter3d8Button.addEventListener('click', (e) => { e.preventDefault(); - rollEncounter(8); + rollEncounter(8, 'W'); }); }; diff --git a/src/assets/styles/styles.css b/src/assets/styles/styles.css index 9dbdc31..2ea81b2 100644 --- a/src/assets/styles/styles.css +++ b/src/assets/styles/styles.css @@ -392,10 +392,10 @@ table th { display: flex; flex-direction: row; align-items: center; - justify-content: start; + justify-content: space-between; } -.encounterButtonsWildernessWrapper { +.encounterButtonsWrapper > * { /* margin-left: auto; */ border: 1px solid #e94e5c; border-radius: .3rem; @@ -403,6 +403,7 @@ table th { text-align: center; } + .feature hr { border: 1px 0 0 0; border-color: #885c68; diff --git a/src/layouts/partials/rulesHeader.ejs b/src/layouts/partials/rulesHeader.ejs index 5a7d323..f1afcf8 100644 --- a/src/layouts/partials/rulesHeader.ejs +++ b/src/layouts/partials/rulesHeader.ejs @@ -35,7 +35,7 @@ var title = (page.title ?? '').replace('HOSR ', '');
    - Version 0.14.2 / 2024-03-24 + Version 0.14.5 / 2024-05-27