Skip to content

Commit

Permalink
Add action point details to weapon attack messages
Browse files Browse the repository at this point in the history
Closes #110
  • Loading branch information
kmoschcau committed Jan 8, 2022
1 parent 3216d71 commit dbee97a
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 78 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Breaking

- old weapon attack messages might display incorrectly

### Added

- notifications for errors in weapon macros created from source
- a value replacing rule element
([#95](https://github.com/Wasteland-Ventures-Group/WV-VTT-module/issues/95))
- weapon attack details on not executed attacks
([#111](https://github.com/Wasteland-Ventures-Group/WV-VTT-module/issues/111))
- action point details on weapon attack messages
([#110](https://github.com/Wasteland-Ventures-Group/WV-VTT-module/issues/110))

### Changed

Expand Down
8 changes: 8 additions & 0 deletions src/main/handlebars/chatMessages/weaponAttack.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
<div class="spacing-bottom">{{localize "wv.weapons.attacks.ranges"}}: {{template.raw.displayRanges}}</div>
{{#if details}} {{!backward compatibility}}
<div class="spacing-bottom">{{localize "wv.weapons.modifiers.hit.range"}}: {{localize template.keys.rangeBracket}} ({{details.range.distance}})</div>
<div class="spacing-bottom">
<div>{{localize "wv.weapons.attacks.ap.title"}}</div>
<ul class="no-margin">
<li>{{localize "wv.weapons.attacks.ap.previous"}}: {{details.ap.previous}}</li>
<li>{{localize "wv.weapons.attacks.ap.cost"}}: {{details.ap.cost}}</li>
<li>{{localize "wv.weapons.attacks.ap.remaining"}}: {{details.ap.remaining}}</li>
</ul>
</div>
<div class="spacing-bottom">
<div>{{localize "wv.sheets.actor.secondary.criticals.title"}}</div>
<ul class="no-margin">
Expand Down
7 changes: 7 additions & 0 deletions src/main/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
},
"labels": {
"alias": "Sprecheralias:",
"actionPoints": "Aktionspunkte:",
"criticalFailure": "Kritische Fehlerchance:",
"criticalSuccess": "Kritische Erfolgschance:",
"genericModifier": "Modifikator:",
Expand Down Expand Up @@ -260,6 +261,12 @@
},
"weapons": {
"attacks": {
"ap": {
"cost": "Kosten",
"previous": "Vorher",
"remaining": "Übrig",
"title": "AP"
},
"damageRoll": "Schaden",
"details": "Details",
"execute": "Ausführen",
Expand Down
7 changes: 7 additions & 0 deletions src/main/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
},
"labels": {
"alias": "Speaker alias:",
"actionPoints": "Action points:",
"criticalFailure": "Critical failure rate:",
"criticalSuccess": "Critical success rate:",
"genericModifier": "Modifier:",
Expand Down Expand Up @@ -260,6 +261,12 @@
},
"weapons": {
"attacks": {
"ap": {
"cost": "Cost",
"previous": "Previous",
"remaining": "Remaining",
"title": "AP"
},
"damageRoll": "Damage",
"details": "Details",
"execute": "Execute",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@ export type WeaponAttackFlags = NotExecutedAttackFlags | ExecutedAttackFlags;
export interface CommonWeaponAttackFlags {
type: "weaponAttack";
attackName: string;
details?: {
details: {
ap: {
previous: number;
cost: number;
remaining: number;
};
criticals: {
failure: number;
success: number;
Expand Down
145 changes: 68 additions & 77 deletions src/main/typescript/item/weapon/attack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import Prompt, {
TextInputSpec
} from "../../applications/prompt.js";
import { CONSTANTS, SpecialName } from "../../constants.js";
import type { Specials } from "../../data/actor/properties.js";
import type { AttackSource } from "../../data/item/weapon/attack/source.js";
import type DragData from "../../dragData.js";
import Formulator from "../../formulator.js";
Expand Down Expand Up @@ -54,6 +53,7 @@ export default class Attack {
}
const {
alias,
ap: previousAp,
modifier: promptHitModifier,
range,
skillTotal,
Expand All @@ -80,6 +80,7 @@ export default class Attack {
range,
specials
);
const outOfRange = ranges.RangeBracket.OUT_OF_RANGE === rangeBracket;

// Calculate damage dice ---------------------------------------------------
const strengthDamageDiceMod = this.getStrengthDamageDiceMod(
Expand All @@ -104,38 +105,72 @@ export default class Attack {
critFailure
);

// Calculate AP ------------------------------------------------------------
const remainingAp = outOfRange
? previousAp
: token?.inCombat
? previousAp - this.data.ap
: previousAp;
const notEnoughAp = 0 > remainingAp;

// Create common attack flags ----------------------------------------------
const commonFlags: deco.CommonWeaponAttackFlags =
this.createCommonWeaponAttackFlags(
critFailure,
critSuccess,
strengthDamageDiceMod,
rangeDamageDiceMod,
damageDice,
skillTotal,
rangeModifier,
promptHitModifier,
hitTotal,
rangeBracket,
range,
specials
);
const commonFlags: Required<deco.CommonWeaponAttackFlags> = {
type: "weaponAttack",
attackName: this.name,
details: {
ap: {
cost: this.data.ap,
previous: previousAp,
remaining: remainingAp
},
criticals: {
failure: critFailure,
success: critSuccess
},
damage: {
base: {
base: this.data.damage.base,
modifiers: this.getDamageBaseModifierFlags(),
total: this.data.damage.base
},
dice: {
base: this.data.damage.dice,
modifiers: this.getDamageDiceModifierFlags(
strengthDamageDiceMod,
rangeDamageDiceMod
),
total: damageDice
}
},
hit: {
base: skillTotal,
modifiers: this.getHitModifierFlags(rangeModifier, promptHitModifier),
total: hitTotal
},
range: {
bracket: rangeBracket,
distance: range
}
},
ownerSpecials: specials,
weaponImage: this.weapon.img,
weaponName: this.weapon.data.name,
weaponSystemData: this.weapon.systemData
};

// Check range -------------------------------------------------------------
if (rangeBracket === ranges.RangeBracket.OUT_OF_RANGE) {
if (outOfRange) {
this.createOutOfRangeMessage(commonData, commonFlags);
return;
}

// Check AP and subtract in combat -----------------------------------------
if (actor?.getActiveTokens(true).some((token) => token.inCombat)) {
const currentAp = actor.data.data.vitals.actionPoints.value;
const apUse = this.data.ap;
if (currentAp < apUse) {
if (token?.inCombat) {
if (notEnoughAp) {
this.createNotEnoughApMessage(commonData, commonFlags);
return;
}
actor.updateActionPoints(currentAp - apUse);
actor?.updateActionPoints(remainingAp);
}

// Hit roll ----------------------------------------------------------------
Expand Down Expand Up @@ -211,6 +246,13 @@ export default class Attack {
label: i18n.localize("wv.prompt.labels.alias"),
value: actor?.name
},
ap: {
type: "number",
label: i18n.localize("wv.prompt.labels.actionPoints"),
value: actor?.actionPoints.value,
min: 0,
max: actor?.actionPoints.max ?? 20
},
modifier: {
type: "number",
label: i18n.localize("wv.prompt.labels.genericModifier"),
Expand Down Expand Up @@ -368,61 +410,6 @@ export default class Attack {
};
}

/** Get the default ChatMessage flags for this Weapon Attack. */
protected createCommonWeaponAttackFlags(
critFailure: number,
critSuccess: number,
strengthDamageDiceMod: number,
rangeDamageDiceMod: number,
damageDice: number,
skillTotal: number,
rangeModifier: number,
promptHitModifier: number,
hitTotal: number,
rangeBracket: ranges.RangeBracket,
range: number,
ownerSpecials: Partial<Specials>
): Required<deco.CommonWeaponAttackFlags> {
return {
type: "weaponAttack",
attackName: this.name,
details: {
criticals: {
failure: critFailure,
success: critSuccess
},
damage: {
base: {
base: this.data.damage.base,
modifiers: this.getDamageBaseModifierFlags(),
total: this.data.damage.base
},
dice: {
base: this.data.damage.dice,
modifiers: this.getDamageDiceModifierFlags(
strengthDamageDiceMod,
rangeDamageDiceMod
),
total: damageDice
}
},
hit: {
base: skillTotal,
modifiers: this.getHitModifierFlags(rangeModifier, promptHitModifier),
total: hitTotal
},
range: {
bracket: rangeBracket,
distance: range
}
},
ownerSpecials,
weaponImage: this.weapon.img,
weaponName: this.weapon.data.name,
weaponSystemData: this.weapon.systemData
};
}

/** Create a weapon attack message, signaling out of range. */
protected createOutOfRangeMessage(
commonData: ChatMessageDataConstructorData,
Expand Down Expand Up @@ -535,6 +522,7 @@ export function isWeaponAttackDragData(
*/
type PromptSpec = {
alias: TextInputSpec;
ap: NumberInputSpec;
modifier: NumberInputSpec;
range: NumberInputSpec;
skillTotal: NumberInputSpec;
Expand All @@ -547,6 +535,9 @@ type ExternalData = {
/** The chat message alias of the executing actor */
alias: string;

/** The current action points of the actor */
ap: number;

/** A possible modifier for the attack */
modifier: number;

Expand Down
13 changes: 13 additions & 0 deletions src/main/typescript/lang.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export interface LangSchema {
labels: {
/** The label for a speaker alias */
alias: string;
/** The label for actor action points */
actionPoints: string;
/** The label for a critical failure chance */
criticalFailure: string;
/** The label for a critical success chance */
Expand Down Expand Up @@ -498,6 +500,17 @@ export interface LangSchema {
weapons: {
/** Labels related to Weapon Attacks */
attacks: {
/** Labels related to the AP cost of an attack */
ap: {
/** The label for the AP cost of the attack */
cost: string;
/** The label for the current AP of the actor (before the attack) */
previous: string;
/** The label for the remaining AP of the actor (after the attack) */
remaining: string;
/** The title label for the AP section */
title: string;
};
/** The label for the damage roll of an attack */
damageRoll: string;
/** The label for the details list of an attack */
Expand Down

0 comments on commit dbee97a

Please sign in to comment.