diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d7d6370..b84c3213 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
# Releases
-## Version 3.0.96 [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt)
+## Version 3.0.97 (So far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt)
+
+- Fix apply knockback dialog for Firefox.
+- Applying knockback now rolls knockback skinned dice.
+- Using STUN for END now uses skinned dice. [#1212](https://github.com/dmdorman/hero6e-foundryvtt/issues/1212)
+
+## Version 3.0.96
- Fixes for combat tracker edge cases.
diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs
index 497038c6..09925452 100644
--- a/module/item/item-attack.mjs
+++ b/module/item/item-attack.mjs
@@ -8,7 +8,7 @@ import { performAdjustment, renderAdjustmentChatCards } from "../utility/adjustm
import { getRoundedDownDistanceInSystemUnits, getSystemDisplayUnits } from "../utility/units.mjs";
import { HeroSystem6eItem, RequiresASkillRollCheck } from "../item/item.mjs";
import { ItemAttackFormApplication } from "../item/item-attack-application.mjs";
-import { HeroRoller } from "../utility/dice.mjs";
+import { DICE_SO_NICE_CUSTOM_SETS, HeroRoller } from "../utility/dice.mjs";
import { clamp } from "../utility/compatibility.mjs";
import { calculateVelocityInSystemUnits } from "../ruler.mjs";
import { Attack } from "../utility/attack.mjs";
@@ -320,6 +320,8 @@ export async function AttackToHit(item, options) {
// -------------------------------------------------
const setManeuver = actor.items.find((o) => o.type == "maneuver" && o.name === "Set" && o.system.active);
+ let stunForEndHeroRoller = null;
+
const heroRoller = new HeroRoller()
.makeSuccessRoll()
.addNumber(11, "Base to hit")
@@ -567,7 +569,10 @@ export async function AttackToHit(item, options) {
return;
}
- const stunForEndHeroRoller = new HeroRoller().makeBasicRoll().addDice(stunDice);
+ stunForEndHeroRoller = new HeroRoller()
+ .setPurpose(DICE_SO_NICE_CUSTOM_SETS.STUN_FOR_END)
+ .makeBasicRoll()
+ .addDice(stunDice);
await stunForEndHeroRoller.roll();
const stunRenderedResult = await stunForEndHeroRoller.render();
stunDamageForEnd = stunForEndHeroRoller.getBasicTotal();
@@ -917,6 +922,7 @@ export async function AttackToHit(item, options) {
rolls: targetData
.map((target) => target.roller?.rawRolls())
.flat()
+ .concat(stunForEndHeroRoller?.rawRolls())
.filter(Boolean),
user: game.user._id,
content: cardHtml,
@@ -1095,11 +1101,15 @@ export async function _onRollKnockback(event) {
2,
item.actor,
)}${getSystemDisplayUnits(item.actor.system.is5e)} they are knocked into a solid object,
- to a maximum of the PD + BODY of the object hit.
+ to a maximum of the PD + BODY of the object hit.
+
+
A character takes 1d6 damage for every ${getRoundedDownDistanceInSystemUnits(
4,
item.actor,
- )}${getSystemDisplayUnits(item.actor.system.is5e)} knocked back if no object intervenes.
+ )}${getSystemDisplayUnits(item.actor.system.is5e)} they are knocked back if no object intervenes.
+
+
The character typically winds up prone.
@@ -1110,10 +1120,10 @@ export async function _onRollKnockback(event) {
knockbackResultTotal / 2,
)}" data-dtype="Number" />
-
+
- NOTE: Don't forget to move the token to the appropriate location as KB movement is not automated.
+ NOTE: Don't forget to move the token to the appropriate location as KB movement is not automated.
`;
@@ -1149,7 +1159,10 @@ export async function _onRollKnockback(event) {
async function _rollApplyKnockback(token, knockbackDice) {
const actor = token.actor;
- const damageRoller = new HeroRoller().addDice(parseInt(knockbackDice), "Knockback").makeNormalRoll();
+ const damageRoller = new HeroRoller()
+ .setPurpose(DICE_SO_NICE_CUSTOM_SETS.KNOCKBACK)
+ .addDice(parseInt(knockbackDice), "Knockback")
+ .makeNormalRoll();
await damageRoller.roll();
const damageRenderedResult = await damageRoller.render();
@@ -1157,7 +1170,8 @@ async function _rollApplyKnockback(token, knockbackDice) {
// Bogus attack item
const pdContentsAttack = `
-
+
+
`;
const pdAttack = await new HeroSystem6eItem(HeroSystem6eItem.itemDataFromXml(pdContentsAttack, actor), {
@@ -1262,8 +1276,9 @@ async function _rollApplyKnockback(token, knockbackDice) {
const speaker = ChatMessage.getSpeaker({ actor: actor });
const chatData = {
+ type: CONST.CHAT_MESSAGE_TYPES.ROLL,
+ rolls: damageRoller.rawRolls(),
user: game.user._id,
-
content: cardHtml,
speaker: speaker,
};
@@ -2220,6 +2235,8 @@ export async function _onApplyDamageToSpecificToken(event, tokenId) {
const speaker = ChatMessage.getSpeaker({ actor: item.actor });
const chatData = {
+ type: CONST.CHAT_MESSAGE_TYPES.ROLL,
+ rolls: damageDetail.knockbackRoller?.rawRolls(),
user: game.user._id,
content: cardHtml,
speaker: speaker,
@@ -2796,11 +2813,9 @@ async function _calcKnockback(body, item, options, knockbackMultiplier) {
}
knockbackRoller = new HeroRoller()
+ .setPurpose(DICE_SO_NICE_CUSTOM_SETS.KNOCKBACK)
.makeBasicRoll()
- .addNumber(
- body * (knockbackMultiplier > 1 ? knockbackMultiplier : 1), // TODO: Consider supporting multiplication in HeroRoller
- "Max potential knockback",
- )
+ .addNumber(body * (knockbackMultiplier > 1 ? knockbackMultiplier : 1), "Max potential knockback")
.addNumber(-parseInt(options.knockbackResistance || 0), "Knockback resistance")
.addDice(-Math.max(0, knockbackDice));
await knockbackRoller.roll();
diff --git a/module/utility/dice.mjs b/module/utility/dice.mjs
index 36177442..f8c3fd0f 100644
--- a/module/utility/dice.mjs
+++ b/module/utility/dice.mjs
@@ -1,17 +1,6 @@
import { isGameV12OrLater } from "./compatibility.mjs";
-const DICE_SO_NICE_CUSTOM_SETS = {
- STUNx: {
- colorset: "Stun Multiplier",
- foreground: "white",
- background: "blue",
- edge: "blue",
- material: "wood",
- fontScale: {
- d6: 1.1,
- },
- visibility: "visible",
- },
+export const DICE_SO_NICE_CUSTOM_SETS = {
HIT_LOC: {
colorset: "Hit Location - Body Part",
foreground: "black",
@@ -34,10 +23,41 @@ const DICE_SO_NICE_CUSTOM_SETS = {
},
visibility: "visible",
},
+ KNOCKBACK: {
+ colorset: "Knockback",
+ foreground: "black",
+ background: "orange",
+ edge: "orange",
+ material: "wood",
+ fontScale: {
+ d6: 1.1,
+ },
+ visibility: "visible",
+ },
+ STUNx: {
+ colorset: "Stun Multiplier",
+ foreground: "white",
+ background: "blue",
+ edge: "blue",
+ material: "wood",
+ fontScale: {
+ d6: 1.1,
+ },
+ visibility: "visible",
+ },
+ STUN_FOR_END: {
+ colorset: "STUN for END",
+ foreground: "white",
+ background: "brown",
+ edge: "brown",
+ material: "wood",
+ fontScale: {
+ d6: 1.1,
+ },
+ visibility: "visible",
+ },
};
-const DICE_SO_NICE_CATEGORY_NAME = "Hero System 6e (Unofficial) V2";
-
// v11/v12 compatibility shim.
// TODO: Cleanup eslint file with these terms
const Die = CONFIG.Dice.terms.d;
@@ -48,46 +68,26 @@ const OperatorTerm = CONFIG.Dice.termTypes.OperatorTerm;
const RollTermClass = foundry.dice?.terms.RollTerm ? foundry.dice.terms.RollTerm : RollTerm;
/**
- * Add colour sets into Dice So Nice! This allows users to see what the colour set is for each function.
+ * Add our custom colour sets into Dice So Nice! This allows users to see what the colour set is for each function.
* Players can then choose to use that theme for maximum confusion as to which are their rolls and which
- * are the extras for hit location or stun multiplier.
+ * are the extras for hit location, stun multiplier, etc.
*/
Hooks.once("diceSoNiceReady", (diceSoNice) => {
- diceSoNice.addColorset(
- {
- ...{
- name: "Stun Multiplier",
- description: "Stun Multiplier Dice",
- category: DICE_SO_NICE_CATEGORY_NAME,
- },
- ...DICE_SO_NICE_CUSTOM_SETS.STUNx,
- },
- "default",
- );
-
- diceSoNice.addColorset(
- {
- ...{
- name: "Hit Location - Body Part",
- description: "Hit Location - Body Part Dice",
- category: DICE_SO_NICE_CATEGORY_NAME,
- },
- ...DICE_SO_NICE_CUSTOM_SETS.HIT_LOC,
- },
- "default",
- );
-
- diceSoNice.addColorset(
- {
- ...{
- name: "Hit Location - Body Side",
- description: "Hit Location - Body Side Dice",
- category: DICE_SO_NICE_CATEGORY_NAME,
+ Object.keys(DICE_SO_NICE_CUSTOM_SETS).forEach((key) => {
+ const customSet = DICE_SO_NICE_CUSTOM_SETS[key];
+
+ diceSoNice.addColorset(
+ {
+ ...{
+ name: customSet.colorset,
+ description: `${customSet.colorset} Dice`,
+ category: game.system.title,
+ },
+ ...customSet,
},
- ...DICE_SO_NICE_CUSTOM_SETS.HIT_LOC_SIDE,
- },
- "default",
- );
+ "default",
+ );
+ });
});
/**
@@ -322,6 +322,20 @@ export class HeroRoller {
return this;
}
+ /**
+ *
+ * @param {DICE_SO_NICE_CUSTOM_SETS} purpose
+ * @returns {HeroRoller}
+ */
+ setPurpose(purpose) {
+ if (purpose && game.settings.get(game.system.id, "DiceSkinning")) {
+ if (!this._options) this._options = {};
+ this._options.appearance = foundry.utils.deepClone(purpose);
+ }
+
+ return this;
+ }
+
/**
* V11 and V12 (or later) behave differently. V11 can have a operatorTerm to start
* terms but it cannot have negative dice terms. V12, on the other hand, cannot handle
@@ -966,14 +980,8 @@ export class HeroRoller {
if (this._type === HeroRoller.ROLL_TYPE.KILLING && !this._useHitLocation) {
// NOTE: It appears there is no standard effect for the STUNx per APG p 53
// although there don't appear to be any mention of this in other books.
- this._killingStunMultiplierHeroRoller = new HeroRoller(
- game.settings.get(game.system.id, "DiceSkinning")
- ? {
- appearance: foundry.utils.deepClone(DICE_SO_NICE_CUSTOM_SETS.STUNx),
- }
- : {},
- this._buildRollClass,
- )
+ this._killingStunMultiplierHeroRoller = new HeroRoller({}, this._buildRollClass)
+ .setPurpose(DICE_SO_NICE_CUSTOM_SETS.STUNx)
.makeBasicRoll()
.addDieMinus1Min1(this._killingStunMultiplier === "1d6-1" ? 1 : 0)
.addHalfDice(this._killingStunMultiplier === "1d3" ? 1 : 0);
@@ -997,14 +1005,8 @@ export class HeroRoller {
let locationName;
if (this._alreadyHitLocation === "none") {
- this._hitLocationRoller = new HeroRoller(
- game.settings.get(game.system.id, "DiceSkinning")
- ? {
- appearance: foundry.utils.deepClone(DICE_SO_NICE_CUSTOM_SETS.HIT_LOC),
- }
- : {},
- this._buildRollClass,
- )
+ this._hitLocationRoller = new HeroRoller({}, this._buildRollClass)
+ .setPurpose(DICE_SO_NICE_CUSTOM_SETS.HIT_LOC)
.makeBasicRoll()
.addDice(3);
await this._hitLocationRoller.roll();
@@ -1021,14 +1023,8 @@ export class HeroRoller {
CONFIG.HERO.sidedLocations.has(locationName) &&
this._alreadyHitLocationSide === "none"
) {
- this._hitSideRoller = new HeroRoller(
- game.settings.get(game.system.id, "DiceSkinning")
- ? {
- appearance: foundry.utils.deepClone(DICE_SO_NICE_CUSTOM_SETS.HIT_LOC_SIDE),
- }
- : {},
- this._buildRollClass,
- )
+ this._hitSideRoller = new HeroRoller({}, this._buildRollClass)
+ .setPurpose(DICE_SO_NICE_CUSTOM_SETS.HIT_LOC_SIDE)
.makeBasicRoll()
.addDice(1);
await this._hitSideRoller.roll();