Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9321f9cb5b |
+2
-2
@@ -7,8 +7,8 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Spell cast dialog has a **Modyfikator liczby kości** (dice modifier, +/-). It changes the number of dice in the casting test for every casting mode (standard, sacred, witch, blood, prayer); the pool never drops below 0. The over-limit Nadczarowanie test uses the same modified pool. API: `CastOptions.diceModifier`, `applyDiceModifier()` in `rules.ts`.
|
||||
### Changed
|
||||
- English text "Cannot be unraveled" is now "Cannot be dispelled" on the spell cast card.
|
||||
|
||||
## [1.5.1] - 2026-10-22
|
||||
|
||||
|
||||
+1
-3
@@ -363,10 +363,8 @@
|
||||
},
|
||||
"spellCast": {
|
||||
"title": "Casting spell",
|
||||
"diceModifier": "Dice modifier",
|
||||
"diceModifierHint": "+/- dice in the casting test (the pool never drops below 0)",
|
||||
"manaSpent": "Mana spent",
|
||||
"cannotBeUnraveled": "Cannot be unraveled",
|
||||
"cannotBeUnraveled": "Cannot be dispelled",
|
||||
"zealSpent": "Zeal spent",
|
||||
"bloodSpent": "Blood spent",
|
||||
"hekateMode": "Hekate mode (Sacred + Witch hybrid)",
|
||||
|
||||
@@ -383,8 +383,6 @@
|
||||
},
|
||||
"spellCast": {
|
||||
"title": "Rzucanie zaklęcia",
|
||||
"diceModifier": "Modyfikator liczby kości",
|
||||
"diceModifierHint": "+/- kości w Teście rzucania (liczba kości nie spadnie poniżej 0)",
|
||||
"manaSpent": "Wydana mana",
|
||||
"cannotBeUnraveled": "Nie da się rozpleść",
|
||||
"cast": "Rzuć",
|
||||
|
||||
@@ -76,12 +76,6 @@ export async function askCastOptions(spell: SpellForDialog, actor: ActorForDialo
|
||||
<input type="number" name="bloodSpent" value="${baseBlood}" min="${baseBlood}" max="${max}"/>
|
||||
</div>`);
|
||||
}
|
||||
fields.push(`
|
||||
<div class="form-group">
|
||||
<label>${game.i18n.localize('HBM.spellCast.diceModifier')}</label>
|
||||
<input type="number" name="diceModifier" value="0" step="1"/>
|
||||
<small>${game.i18n.localize('HBM.spellCast.diceModifierHint')}</small>
|
||||
</div>`);
|
||||
if (mode !== 'sacred') {
|
||||
fields.push(`
|
||||
<div class="form-group">
|
||||
@@ -168,8 +162,6 @@ export async function askCastOptions(spell: SpellForDialog, actor: ActorForDialo
|
||||
}
|
||||
if (fd.zealSpent != null) opts.zealSpent = Math.max(1, Number(fd.zealSpent) || 1);
|
||||
if (fd.bloodSpent != null) opts.bloodSpent = Math.max(baseBlood, Number(fd.bloodSpent) || baseBlood);
|
||||
const diceMod = Math.trunc(Number(fd.diceModifier) || 0);
|
||||
if (diceMod !== 0) opts.diceModifier = diceMod;
|
||||
if (fd.hekateMode) opts.hekateMode = 'witch';
|
||||
if (fd.castAsPrayer) opts.castAsPrayer = true;
|
||||
if (fd.bypassSuperspellWarning) opts.bypassSuperspellWarning = true;
|
||||
|
||||
@@ -259,8 +259,3 @@ export function negotiationBand(successes: number): number {
|
||||
export function fearThresholdMod(sourceId: string | undefined, targetId: string | undefined): number {
|
||||
return sourceId && targetId && sourceId === targetId ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Casting test pool with the player's +/- dice modifier (whole dice, never below 0). */
|
||||
export function applyDiceModifier(pool: number, modifier?: number): number {
|
||||
return Math.max(0, Math.trunc(Number(pool) || 0) + Math.trunc(Number(modifier) || 0));
|
||||
}
|
||||
|
||||
+5
-10
@@ -16,7 +16,7 @@
|
||||
|
||||
import { HbmTSRoll } from '../dice/ts-roll';
|
||||
import { TS_DEFAULT_REQUIRED, TS_DEFAULT_THRESHOLD, getMagicPowerEntry } from '../constants';
|
||||
import { manaToBorrow, exhaustionDice, manaExhaustionTier, overLimitTest, applyDiceModifier, SPELL_DAMAGE_PROFILES } from './rules';
|
||||
import { manaToBorrow, exhaustionDice, manaExhaustionTier, overLimitTest, SPELL_DAMAGE_PROFILES } from './rules';
|
||||
import { validateCast } from './spell-validation';
|
||||
import type { ValidationResult } from './spell-validation';
|
||||
import { rollSpellDamage } from './spell-damage';
|
||||
@@ -76,8 +76,6 @@ export interface CastOptions {
|
||||
bypassSuperspellWarning?: boolean;
|
||||
bypassNonCombatBlock?: boolean;
|
||||
groupCasters?: string[];
|
||||
/** Modifier to the number of dice in the casting test (+/-, pool never below 0). */
|
||||
diceModifier?: number;
|
||||
/** Override required successes (used by variableSuccesses summons). */
|
||||
requiredOverride?: number;
|
||||
speaker?: ChatMessage.SpeakerData;
|
||||
@@ -278,10 +276,9 @@ async function castStandard(actor: CastableActor, spell: SpellLike, opts: CastOp
|
||||
const magicDice = typeof a.magic.dicePool === 'number'
|
||||
? a.magic.dicePool
|
||||
: getMagicPowerEntry(a.magic.actual ?? a.magic.value ?? 0).dicePool;
|
||||
const basePool = opts.castAsPrayer
|
||||
const pool = opts.castAsPrayer
|
||||
? a.soul.value + (actor.system.skills?.devotion?.value ?? 0)
|
||||
: magicDice + (actor.system.skills?.magicalAbilities?.value ?? 0);
|
||||
const pool = applyDiceModifier(basePool, opts.diceModifier);
|
||||
const flavor = opts.castAsPrayer
|
||||
? `${spell.name} - ${game.i18n.localize('HBM.spell.castingMode.prayer')}`
|
||||
: `${spell.name}`;
|
||||
@@ -352,7 +349,7 @@ async function castSacred(actor: CastableActor, spell: SpellLike, opts: CastOpti
|
||||
const zealCost = Math.max(1, opts.zealSpent ?? 1);
|
||||
await actor.update({ 'system.attributes.zeal.value': a.zeal.value - zealCost });
|
||||
|
||||
const pool = applyDiceModifier(a.soul.value + (actor.system.skills?.devotion?.value ?? 0), opts.diceModifier);
|
||||
const pool = a.soul.value + (actor.system.skills?.devotion?.value ?? 0);
|
||||
const flavor = `${spell.name} - ${game.i18n.localize('HBM.spell.castingMode.sacred')}`;
|
||||
const required = opts.requiredOverride ?? spell.system.difficulty.successes ?? TS_DEFAULT_REQUIRED;
|
||||
const roll = HbmTSRoll.fromParams({
|
||||
@@ -382,10 +379,9 @@ async function castWitch(actor: CastableActor, spell: SpellLike, opts: CastOptio
|
||||
const magicDice = typeof (a.magic as any).dicePool === 'number'
|
||||
? (a.magic as any).dicePool
|
||||
: getMagicPowerEntry((a.magic as any).actual ?? a.magic.value ?? 0).dicePool;
|
||||
const basePool = opts.castAsPrayer
|
||||
const pool = opts.castAsPrayer
|
||||
? a.soul.value + (actor.system.skills?.devotion?.value ?? 0)
|
||||
: magicDice + (actor.system.skills?.magicalAbilities?.value ?? 0);
|
||||
const pool = applyDiceModifier(basePool, opts.diceModifier);
|
||||
const flavor = opts.castAsPrayer
|
||||
? `${spell.name} - ${game.i18n.localize('HBM.spell.castingMode.prayer')} (${required}/${maxSymbols})`
|
||||
: `${spell.name} - ${game.i18n.localize('HBM.spell.castingMode.witch')} (${required}/${maxSymbols})`;
|
||||
@@ -425,10 +421,9 @@ async function castBlood(actor: CastableActor, spell: SpellLike, opts: CastOptio
|
||||
const magicDice = typeof (a.magic as any).dicePool === 'number'
|
||||
? (a.magic as any).dicePool
|
||||
: getMagicPowerEntry((a.magic as any).actual ?? a.magic.value ?? 0).dicePool;
|
||||
const basePool = opts.castAsPrayer
|
||||
const pool = opts.castAsPrayer
|
||||
? a.soul.value + (actor.system.skills?.devotion?.value ?? 0)
|
||||
: magicDice + (actor.system.skills?.magicalAbilities?.value ?? 0);
|
||||
const pool = applyDiceModifier(basePool, opts.diceModifier);
|
||||
const flavor = opts.castAsPrayer
|
||||
? `${spell.name} - ${game.i18n.localize('HBM.spell.castingMode.prayer')}`
|
||||
: `${spell.name} - ${game.i18n.localize('HBM.spell.castingMode.blood')}`;
|
||||
|
||||
@@ -104,14 +104,3 @@ describe('blood, elixirs, trade', () => {
|
||||
expect([0, 1, 2, 3, 4, 5, 6, 7, 12].map(negotiationBand)).toEqual([0, 1, 1, 2, 2, 3, 3, 4, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyDiceModifier', () => {
|
||||
it('adds a signed modifier and never goes below 0', async () => {
|
||||
const { applyDiceModifier } = await import('../../src/logic/rules');
|
||||
expect(applyDiceModifier(5, 2)).toBe(7);
|
||||
expect(applyDiceModifier(5, -2)).toBe(3);
|
||||
expect(applyDiceModifier(2, -5)).toBe(0);
|
||||
expect(applyDiceModifier(4)).toBe(4);
|
||||
expect(applyDiceModifier(4, 1.9)).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user