Skip to content

Commit

Permalink
Add a weapon modifier listing to attack messages
Browse files Browse the repository at this point in the history
Closes #76.
  • Loading branch information
kmoschcau committed Dec 9, 2021
1 parent 1dea326 commit 171c3a1
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 31 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- automatic range measuring when using targeting
([#75](https://github.com/Wasteland-Ventures-Group/WV-VTT-module/issues/75))
- a listing of modifiers for hit changes in weapon attack system messages
([#76](https://github.com/Wasteland-Ventures-Group/WV-VTT-module/issues/76))
- weapons
- Alien Blaster
- Arcane Glove
Expand Down
6 changes: 3 additions & 3 deletions src/handlebars/items/weaponSheet.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@
<label>{{localize "wv.sheets.item.weapon.ranges.range"}}</label>
<label>{{localize "wv.sheets.item.weapon.ranges.distance"}}</label>
<label>{{localize "wv.sheets.item.weapon.ranges.modifier"}}</label>
<label>{{localize "wv.sheets.item.weapon.ranges.short"}}</label>
<label>{{localize "wv.weapons.ranges.brackets.short"}}</label>
<span class="resource-display">{{sheet.ranges.short.distance}}</span>
<span class="resource-display">{{sheet.ranges.short.modifier}}</span>
{{#if sheet.ranges.medium}}
<label>{{localize "wv.sheets.item.weapon.ranges.medium"}}</label>
<label>{{localize "wv.weapons.ranges.brackets.medium"}}</label>
<span class="resource-display">{{sheet.ranges.medium.distance}}</span>
<span class="resource-display">{{sheet.ranges.medium.modifier}}</span>
{{/if}}
{{#if sheet.ranges.long}}
<label>{{localize "wv.sheets.item.weapon.ranges.long"}}</label>
<label>{{localize "wv.weapons.ranges.brackets.long"}}</label>
<span class="resource-display">{{sheet.ranges.long.distance}}</span>
<span class="resource-display">{{sheet.ranges.long.modifier}}</span>
{{/if}}
Expand Down
20 changes: 16 additions & 4 deletions src/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,8 @@
"holdout": "Versteckbar",
"ranges": {
"distance": "Distanz",
"long": "Weit",
"medium": "Mittel",
"modifier": "Modifikator",
"range": "Reichweite",
"short": "Kurz",
"title": "Reichweiten"
},
"reload": {
Expand Down Expand Up @@ -257,13 +254,28 @@
"execute": "Ausführen",
"notEnoughAp": "Nicht genug AP um das zu nutzen!",
"outOfRange": "Das Ziel ist außer Reichweite!",
"range": "Reichweite",
"ranges": "Waffenreichweite",
"results": {
"criticalHit": "Volltreffer!",
"criticalMiss": "Voll daneben!",
"hit": "Treffer!",
"miss": "Daneben!"
}
},
"modifiers": {
"hit": {
"interactive": "Interaktiv",
"range": "Distanz"
},
"total": "Gesamt"
},
"ranges": {
"brackets": {
"long": "Weit",
"medium": "Mittel",
"short": "Kurz",
"pointBlank": "Kürzeste"
}
}
}
}
Expand Down
20 changes: 16 additions & 4 deletions src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,8 @@
"holdout": "Holdout Weapon",
"ranges": {
"distance": "Distance",
"long": "Long",
"medium": "Medium",
"modifier": "Modifier",
"range": "Range",
"short": "Short",
"title": "Ranges"
},
"reload": {
Expand Down Expand Up @@ -257,13 +254,28 @@
"execute": "Execute",
"notEnoughAp": "Not enough AP to use!",
"outOfRange": "The target is out of range!",
"range": "Range",
"ranges": "Weapon Ranges",
"results": {
"criticalHit": "Critical Hit!",
"criticalMiss": "Critical Miss!",
"hit": "Hit!",
"miss": "Miss!"
}
},
"modifiers": {
"hit": {
"interactive": "Interactive",
"range": "Range"
},
"total": "Total"
},
"ranges": {
"brackets": {
"long": "Long",
"medium": "Medium",
"short": "Short",
"pointBlank": "Point-Blank"
}
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/sass/chatMessage.sass
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,18 @@
&.success
color: #18520b
filter: sepia(0.5) hue-rotate(60deg)

.modifier-listing
grid-template-columns: auto, min-content

& > *
width: 100%

.listing-total
border:
bottom:
style: double
width: 3px
top:
style: solid
width: 1px
4 changes: 4 additions & 0 deletions src/sass/common.sass
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ h5
justify-self: right
text-align: right

.number-display
justify-self: right
text-align: right

input:invalid
color: $color-error

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { CONSTANTS } from "../../../constants.js";
import type { Specials } from "../../../data/actor/properties.js";
import type WeaponDataProperties from "../../../data/item/weapon/properties.js";
import { getDisplayRanges } from "../../../data/item/weapon/ranges.js";
import {
getDisplayRanges,
RangeBracket
} from "../../../data/item/weapon/ranges.js";
import { getGame } from "../../../foundryHelpers.js";
import type { Critical } from "../../../rolls/criticalsModifiers.js";
import type { HookParams } from "../index.js";
Expand Down Expand Up @@ -160,17 +163,85 @@ function getDetailsElement(flags: WeaponAttackFlags): HTMLElement {
"wv.weapons.attacks.details"
);

const detailsList = document.createElement("ul");

const rangesItem = document.createElement("li");
rangesItem.textContent =
getGame().i18n.localize("wv.weapons.attacks.range") +
const detailsDiv = document.createElement("div");
detailsDiv.textContent =
getGame().i18n.localize("wv.weapons.attacks.ranges") +
": " +
getDisplayRanges(flags.weaponSystemData, flags.ownerSpecials);

detailsList.append(rangesItem);
detailsDiv.append(...createModifierListing(flags));

return createDetailSection(detailsTitleSpan, detailsDiv);
}

function createModifierListing(flags: WeaponAttackFlags): HTMLElement[] {
if (!flags.executed) throw new Error(NOT_EXECUTED_MESSAGE);
if (!flags.details) return [];

const modifierListingDiv = document.createElement("div");
modifierListingDiv.classList.add("modifier-listing", "grid-2cols");

const skillText = document.createElement("span");
skillText.textContent = getGame().i18n.localize(
"wv.sheets.item.weapon.skill"
);

const skillNumber = document.createElement("span");
skillNumber.classList.add("number-display");
skillNumber.textContent = flags.details.hit.base.toString();

modifierListingDiv.append(skillText, skillNumber);

let bracketKey;
switch (flags.details.range.bracket) {
case RangeBracket.LONG:
bracketKey = "wv.weapons.ranges.brackets.long";
break;
case RangeBracket.MEDIUM:
bracketKey = "wv.weapons.ranges.brackets.medium";
break;
case RangeBracket.SHORT:
bracketKey = "wv.weapons.ranges.brackets.short";
break;
case RangeBracket.POINT_BLANK:
bracketKey = "wv.weapons.ranges.brackets.pointBlank";
}
if (bracketKey) {
const distanceText = document.createElement("span");
distanceText.textContent =
getGame().i18n.localize("wv.weapons.modifiers.hit.range") +
": " +
getGame().i18n.localize(bracketKey) +
` (${flags.details.range.distance})`;

const distanceNumber = document.createElement("span");
distanceNumber.classList.add("number-display");
distanceNumber.textContent = flags.details.range.modifier.toString();

modifierListingDiv.append(distanceText, distanceNumber);
}
flags.details.hit.modifiers.forEach((modifier) => {
const modifierText = document.createElement("span");
modifierText.textContent = getGame().i18n.localize(modifier.key);

const modifierNumber = document.createElement("span");
modifierNumber.classList.add("number-display");
modifierNumber.textContent = modifier.amount.toString();

modifierListingDiv.append(modifierText, modifierNumber);
});

const totalText = document.createElement("span");
totalText.classList.add("listing-total");
totalText.textContent = getGame().i18n.localize("wv.weapons.modifiers.total");

const totalNumber = document.createElement("span");
totalNumber.classList.add("number-display", "listing-total");
totalNumber.textContent = flags.details.hit.total.toString();

modifierListingDiv.append(totalText, totalNumber);

return createDetailSection(detailsTitleSpan, detailsList);
return [document.createElement("hr"), modifierListingDiv];
}

/** Create a roll summary element. */
Expand Down Expand Up @@ -260,6 +331,18 @@ export interface NotExecutedAttackFlags extends CommonWeaponAttackFlags {
export interface ExecutedAttackFlags extends CommonWeaponAttackFlags {
executed: true;
ownerSpecials?: Partial<Specials> | undefined;
details?: {
hit: {
base: number;
modifiers: ModifierFlags[];
total: number;
};
range: {
bracket: RangeBracket;
distance: number;
modifier: number;
};
};
rolls: {
damage: {
formula: string;
Expand All @@ -274,3 +357,8 @@ export interface ExecutedAttackFlags extends CommonWeaponAttackFlags {
};
};
}

export interface ModifierFlags {
amount: number;
key: string;
}
42 changes: 37 additions & 5 deletions src/typescript/item/weapon/attack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Formulator from "../../formulator.js";
import type { Specials } from "../../data/actor/properties.js";
import type DragData from "../../dragData.js";
import { CONSTANTS, SpecialName } from "../../constants.js";
import type { WeaponAttackFlags } from "../../hooks/renderChatMessage/decorateSystemMessage/decorateWeaponAttack.js";
import type * as deco from "../../hooks/renderChatMessage/decorateSystemMessage/decorateWeaponAttack.js";
import * as ranges from "../../data/item/weapon/ranges.js";
import * as interact from "../../interaction.js";
import * as helpers from "../../helpers.js";
Expand Down Expand Up @@ -51,7 +51,7 @@ export default class Attack {
}
const {
alias,
modifier,
modifier: promptHitModifier,
range,
skillTotal,
critSuccess,
Expand Down Expand Up @@ -93,7 +93,7 @@ export default class Attack {

const hitRoll = new Roll(
Formulator.skill(skillTotal)
.modify(rangeModifier + (modifier ?? 0))
.modify(rangeModifier + (promptHitModifier ?? 0))
.criticals({ success: critSuccess, failure: critFailure })
.toString()
).evaluate({ async: false });
Expand All @@ -103,7 +103,37 @@ export default class Attack {
Formulator.damage(this.data.damage.base, damageDice).toString()
).evaluate({ async: false });

this.createAttackMessage(speaker, specials, hitRoll, damageRoll, options);
let hitTotal = skillTotal + rangeModifier;
const hitModifiers = [];
if (promptHitModifier) {
hitTotal += promptHitModifier;
hitModifiers.push({
amount: promptHitModifier,
key: "wv.weapons.modifiers.hit.interactive"
});
}

const details: NonNullable<deco.ExecutedAttackFlags["details"]> = {
hit: {
base: skillTotal,
modifiers: hitModifiers,
total: hitTotal
},
range: {
bracket: rangeBracket,
distance: range,
modifier: rangeModifier
}
};

this.createAttackMessage(
speaker,
specials,
details,
hitRoll,
damageRoll,
options
);
}

/** Get the system formula representation of the damage of this attack. */
Expand Down Expand Up @@ -267,7 +297,7 @@ export default class Attack {
}

/** Get the default ChatMessage flags for this Weapon Attack. */
protected get defaultChatMessageFlags(): WeaponAttackFlags {
protected get defaultChatMessageFlags(): deco.WeaponAttackFlags {
return {
type: "weaponAttack",
weaponName: this.weapon.data.name,
Expand Down Expand Up @@ -316,6 +346,7 @@ export default class Attack {
protected async createAttackMessage(
speaker: foundry.data.ChatMessageData["speaker"]["_source"],
specials: Partial<Specials>,
details: NonNullable<deco.ExecutedAttackFlags["details"]>,
hitRoll: Roll,
damageRoll: Roll,
options?: RollOptions
Expand All @@ -334,6 +365,7 @@ export default class Attack {
...this.defaultChatMessageFlags,
executed: true,
ownerSpecials: specials,
details: details,
rolls: {
damage: {
formula: damageRoll.formula,
Expand Down
Loading

0 comments on commit 171c3a1

Please sign in to comment.