diff --git a/Draw Steel/draw-steel.css b/Draw Steel/draw-steel.css index fc1df81a2877..ac2413c7835a 100644 --- a/Draw Steel/draw-steel.css +++ b/Draw Steel/draw-steel.css @@ -1332,7 +1332,9 @@ label[for*='minimize']::before { .ability-details:has([name="attr_has_power_roll"]:not(:checked)) > .power-roll .power-roll-mods label[data-i18n="potency"], .ability-details:has([name="attr_has_potency"]:not(:checked)) .potency-tier-1, .ability-details:has([name="attr_has_potency"]:not(:checked)) .potency-tier-2, -.ability-details:has([name="attr_has_potency"]:not(:checked)) .potency-tier-3 { +.ability-details:has([name="attr_has_potency"]:not(:checked)) .potency-tier-3, +.ability-details:has([name="attr_has_power_roll"]:not(:checked)) > .power-roll .power-roll-mods input[name="attr_has_pre_effect"], +.ability-details:has([name="attr_has_power_roll"]:not(:checked)) > .power-roll .power-roll-mods label[data-i18n="pre-effect"] { display: none; } @@ -1350,14 +1352,21 @@ label[for*='minimize']::before { display: none; } -.ability-card .ability-effect, .ability-card .ability-trigger { +.ability-card .ability-effect, +.ability-card .ability-trigger { height: auto; } +.ability-card .ability-pre-effect { + height: auto; + margin-bottom: 0.2em; +} + .ability-custom, .ability-malice, .ability-trigger > span, -.ability-effect > span { +.ability-effect > span, +.ability-pre-effect > span { white-space-collapse: preserve; } @@ -2069,6 +2078,7 @@ label[for="npc-checkbox"] { background-color: var(--color-bg-section); border: var(--border-main); border-radius: var(--border-radius); + margin-bottom: 14px; } .sheet-rolltemplate-monsterability .sheet-effect-text, diff --git a/Draw Steel/draw-steel.html b/Draw Steel/draw-steel.html index b2fd872e9ba1..5daabf3619c9 100644 --- a/Draw Steel/draw-steel.html +++ b/Draw Steel/draw-steel.html @@ -7,6 +7,10 @@

style="height: 60px" />

+ + + +
@@ -203,6 +207,10 @@
+
+ + +
@@ -314,31 +322,31 @@
- + - + - +
- + - + - +
- - - - - - + + + + + +
@@ -350,19 +358,19 @@
- + - + - + - + - +
- +
@@ -377,8 +385,8 @@
- - + +
@@ -398,8 +406,8 @@
- - + +
@@ -625,6 +633,11 @@

+
+ + + +
@@ -725,6 +738,11 @@

+ +
+ + +
@@ -1039,6 +1057,11 @@

+
+ + + +
@@ -1097,6 +1120,11 @@

+ +
+ + +
@@ -1553,7 +1581,7 @@

- @@ -1613,6 +1641,11 @@

+
+ + + +
@@ -1671,6 +1704,11 @@

+ +
+ + +
@@ -1707,6 +1745,37 @@

+ +
+ +
+ + + + + + +
+ + +
+ + +
+ + +
+
+ + +
+
+
+
+
@@ -2485,6 +2554,11 @@
+ +
+ + + +
@@ -2548,6 +2622,11 @@
+ +
+ : + +
@@ -2649,6 +2728,12 @@
{{trigger}}
{{/rollTotal() hasTrigger 1}} + {{#rollTotal() hasPreEffect 1}} +
+
:
+
{{pre_effect}}
+
+ {{/rollTotal() hasPreEffect 1}} {{#rollTotal() hasPowerRoll 1}}
{{powerRoll}}:
@@ -2903,6 +2988,12 @@
{{trigger}}
{{/rollTotal() hasTrigger 1}} + {{#rollTotal() hasPreEffect 1}} +
+
:
+
{{pre_effect}}
+
+ {{/rollTotal() hasPreEffect 1}} {{#rollTotal() hasPowerRoll 1}}
{{powerRoll}}:
@@ -3068,7 +3159,7 @@
SIMPLE: 'simpleroll', } /** - ** Builds and executes a roll using the rolltemplate + ** Processes two sources of information and constructs the roll string used by Roll20. Then it executes the roll. ** @param {string} templateName The template to be used for the roll ** @param {Array} directMap A dictionary of rolltemplate keys and their values. ** @param {Array} rollValueMap A dictionary of rolltemplate keys and their values that will be added as inline rolls. @@ -3345,11 +3436,12 @@
'subtype', 'malice_cost', 'trait', 'solo_1_name', 'solo_1_text', 'solo_2_name', 'solo_2_text', 'special', 'malice', 'spend', 'custom', 'spend_cost', 'custom_label', + 'pre_effect' ]; const allAbilityToggles = [ 'has_power_roll', 'has_effect', 'has_special', 'has_malice', - 'has_spend', 'has_custom', + 'has_spend', 'has_custom', 'has_pre_effect' ]; const abPrefix = abilityPrefix ? `${abilityPrefix}_` : ''; @@ -3357,7 +3449,7 @@
// 2. Define the character-level attributes needed for both. const globalAttrsToGet = prefix === 'repeating_npcabilities' ? [] // Monster abilities need nothing - : ['resource_name','kit_melee_damage_1','kit_melee_damage_2','kit_melee_damage_3','kit_ranged_damage_1','kit_ranged_damage_2','kit_ranged_damage_3' ]; // Player abilities need resource names and kit damage info + : ['resource_name','kit_melee_damage_1','kit_melee_damage_2','kit_melee_damage_3','kit_ranged_damage_1','kit_ranged_damage_2','kit_ranged_damage_3', 'mag_psi_dam', 'sanct_weap' ]; // Player abilities need resource names and kit damage info const characteristics = isPowerRoll ? ['might', 'agility', 'reason', 'intuition', 'presence'] : []; @@ -3441,6 +3533,7 @@
const weapon = getTranslationByKey('weapon'); const abilityKeywords = attrs[`${prefix}_${abilityPrefix}keywords`] ? attrs[`${prefix}_${abilityPrefix}keywords`].split(',').map(item => item.trim()) : ''; const kit = []; + let otherdamage = ""; if (abilityKeywords.includes(weapon)) { if (abilityKeywords.includes(melee)) { kit.push(parseInt(attrs['kit_melee_damage_1'])); @@ -3451,7 +3544,23 @@
kit.push(parseInt(attrs['kit_ranged_damage_2'])); kit.push(parseInt(attrs['kit_ranged_damage_3'])); } + if (attrs['sanct_weap']) { + const sanctweaptrans = getTranslationByKey('sanct-weap'); + otherdamage = otherdamage + '+' + attrs['sanct_weap'] + `[${sanctweaptrans}]`; + } } + if (attrs['mag_psi_dam']) { + const psionic = getTranslationByKey('psionic'); + const magic = getTranslationByKey('magic'); + + if (abilityKeywords.includes(psionic)) { + otherdamage = otherdamage + '+' + attrs['mag_psi_dam'] + `[${psionic}]`; + } + else if (abilityKeywords.includes(magic)) { + otherdamage = otherdamage + '+' + attrs['mag_psi_dam'] + `[${magic}]`; + } + } + @@ -3471,11 +3580,12 @@
target: attrs[`${prefix}_${abilityPrefix}target`] || '', trigger: attrs[`${prefix}_${abilityPrefix}trigger`] || '', characteristic: characteristic, - tier1: attrs[`${prefix}_${abilityPrefix}tier_1`] ? damageCalculation(attrs[`${prefix}_${abilityPrefix}tier_1`], kit[0] || 0) : '', - tier2: attrs[`${prefix}_${abilityPrefix}tier_2`] ? damageCalculation(attrs[`${prefix}_${abilityPrefix}tier_2`], kit[1] || 0) : '', - tier3: attrs[`${prefix}_${abilityPrefix}tier_3`] ? damageCalculation(attrs[`${prefix}_${abilityPrefix}tier_3`], kit[2] || 0) : '', + tier1: attrs[`${prefix}_${abilityPrefix}tier_1`] ? damageCalculation(attrs[`${prefix}_${abilityPrefix}tier_1`], kit[0] || 0, otherdamage) : '', + tier2: attrs[`${prefix}_${abilityPrefix}tier_2`] ? damageCalculation(attrs[`${prefix}_${abilityPrefix}tier_2`], kit[1] || 0, otherdamage) : '', + tier3: attrs[`${prefix}_${abilityPrefix}tier_3`] ? damageCalculation(attrs[`${prefix}_${abilityPrefix}tier_3`], kit[2] || 0, otherdamage) : '', special: attrs[`${prefix}_${abilityPrefix}special`] || '', effect: attrs[`${prefix}_${abilityPrefix}effect`] || '', + pre_effect: attrs[`${prefix}_${abilityPrefix}pre_effect`] || '', malprop: attrs[`${prefix}_${abilityPrefix}malice`] || '', malpropcost: attrs[`${prefix}_${abilityPrefix}cost`] || '', solo1name: attrs[`${prefix}_${abilityPrefix}solo_1_name`] || '', @@ -3497,6 +3607,7 @@
hasPowerRoll: attrs[`${prefix}_has_power_roll`] === 'on' ? 1 : 0, hasSpecial: attrs[`${prefix}_has_special`] === 'on' ? 1 : 0, hasEffect: hasEffect, + hasPreEffect: attrs[`${prefix}_has_pre_effect`] === 'on' ? 1 : 0, hasMalprop: attrs[`${prefix}_has_malice`] === 'on' ? 1 : 0, hasSpend: attrs[`${prefix}_has_spend`] === 'on' ? 1 : 0, hasCustom: attrs[`${prefix}_has_custom`] === 'on' ? 1 : 0, @@ -3574,7 +3685,7 @@
} if (other) { - //Add Other Damage + damageCalc.push(other); } // rebuild the tier effect String const newTierEffect = '[[' + @@ -3636,7 +3747,8 @@
'hide': false, 'search': false, 'standup': false, - 'consumable': false + 'consumable': false, + 'opportunity_attack': false }; for (const [action, hasPowerRoll] of Object.entries(simpleActionHandlers)) { @@ -4223,32 +4335,13 @@
const disengage = calculateCombatStat(allFeatures, 'Disengage', 1); const stability = calculateCombatStat(allFeatures, 'Stability', 0); - // --- Stamina & Recoveries (including Kit stats) --- - // 1) Base stamina from Bonus features (class, ancestry, etc.) - const staminaFromBonuses = allFeatures - .filter(f => f.type === FEATURE_TYPES.BONUS && f.data.field === 'Stamina') - .reduce((sum, f) => { - const base = Number(f.data.value) || 0; - const perLevel = Number(f.data.valuePerLevel) || 0; - return sum + base + ((level - 1) * perLevel); - }, 0); - - // 2) Stamina from Kits (KitStats), using the highest equipped kit value - // This mirrors how calculateCombatStat uses KIT_STATS for Speed/Stability. - const staminaFromKits = Math.max( - 0, // avoid -Infinity if no kits - ...allFeatures - .filter(f => f.type === FEATURE_TYPES.KIT_STATS) - .map(f => Number(f.stamina) || 0) - ); - - // 3) Final maximum Stamina - const stamina_max = staminaFromBonuses + staminaFromKits; - - const recoveries_max = allFeatures - .filter(f => f.type === FEATURE_TYPES.BONUS && f.data.field === 'Recoveries') - .reduce((sum, f) => sum + (Number(f.data.value) || 0), 0); + const stamina_max = allFeatures + .filter(f => f.type === FEATURE_TYPES.BONUS && f.data.field === 'Stamina') + .reduce((sum, f) => sum + (f.data.value + ((level - 1) * f.data.valuePerLevel)), 0); + const recoveries_max = allFeatures + .filter(f => f.type === FEATURE_TYPES.BONUS && f.data.field === 'Recoveries') + .reduce((sum, f) => sum + f.data.value, 0); const immunity = getDamageModifiers(allFeatures, level, 'Immunity'); const weakness = getDamageModifiers(allFeatures, level, 'Weakness'); @@ -4809,4 +4902,4 @@
calculateSurgePotency(); }); - + \ No newline at end of file diff --git a/Draw Steel/translation.json b/Draw Steel/translation.json index 2af4ff095b78..3203b2827512 100644 --- a/Draw Steel/translation.json +++ b/Draw Steel/translation.json @@ -143,6 +143,7 @@ "trigger": "Trigger", "power-roll": "Power Roll", "effect": "Effect", + "pre-effect": "Pre-roll Effect", "knockback": "Knockback", "knockback-keywords": "Melee, Weapon", "one-creature": "One Creature", @@ -217,6 +218,8 @@ "custom-text": "Custom text", "characteristic-roll-tooltip": "Click to make a power roll with this characteristic.", "roll-d3-tooltip": "Roll 1d3", + "psionic": "Psionic", + "magic": "Magic", "startcombat": "Set Heroic Resource to Victories", "endcombat": "Reset Heroic Resource, Surges and Temporary Stamina", "respite": "Reset Stamina, Recoveries and Victories",