feat: weapon wound kind (1.5.1)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 00:05:57 +02:00
co-authored by Claude Sonnet 5.5
parent 7b1f6794ad
commit 23027cf1b3
10 changed files with 68 additions and 3 deletions
+9
View File
@@ -5,6 +5,15 @@ All notable changes to the **HbM RPG v3** Foundry VTT system.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.5.1] - 2026-10-22
### Added
- Weapons have a wound kind (`system.weapon.woundKind`: tnąca / kłująca / obuchowa, empty = unspecified), editable on the gear sheet.
- Gear parser reads `wound` / `rana` frontmatter from vault weapon notes. Missing or unknown values stay unspecified; nothing is guessed.
### Notes
- Only cutting and piercing weapons feed passive Krew; unspecified weapons give none. Callers of `applyDamage` still need to pass the weapon's `woundKind`.
## [1.5.0] - 2026-10-20
Vault answers (Foundry Sync Brief §17): blood magic and Runiczny Pancerz.
+7
View File
@@ -239,6 +239,13 @@
"physical": "Physical",
"magical": "Magical",
"environmental": "Environmental",
"woundKind": "Wound kind",
"wound": {
"none": "Unspecified",
"cutting": "Cutting",
"piercing": "Piercing",
"blunt": "Blunt"
},
"properties": "Properties",
"damageReduction": "Damage Reduction",
"damageReductionBonus": "DR Bonus",
+7
View File
@@ -259,6 +259,13 @@
"physical": "Fizyczne",
"magical": "Magiczne",
"environmental": "Środowiskowe",
"woundKind": "Rodzaj rany",
"wound": {
"none": "Nieokreślony",
"cutting": "Tnąca",
"piercing": "Kłująca",
"blunt": "Obuchowa"
},
"properties": "Właściwości",
"damageReduction": "Redukcja Obrażeń",
"damageReductionBonus": "Premia do Redukcji",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hbm-rpg-v3-foundry",
"version": "1.5.0",
"version": "1.5.1",
"description": "Foundry VTT system for Homebrew Magic: Role Playing Game v3",
"private": true,
"type": "module",
+11
View File
@@ -38,6 +38,15 @@ export const parseGear: ParserFn = async (ctx: ParserContext): Promise<ParsedDoc
return docs;
};
/** Vault `wound` / `rana` frontmatter → woundKind ('' when absent or unknown – never guessed). */
export function parseWoundKind(raw: string | undefined): '' | 'cutting' | 'piercing' | 'blunt' {
const v = (raw ?? '').trim().toLowerCase();
if (['cutting', 'tnąca', 'tnaca', 'tnący', 'tnacy'].includes(v)) return 'cutting';
if (['piercing', 'kłująca', 'klujaca', 'kłujący', 'klujacy'].includes(v)) return 'piercing';
if (['blunt', 'obuchowa', 'obuchowy', 'miażdżąca', 'miazdzaca'].includes(v)) return 'blunt';
return '';
}
function extractMarkdown(raw: string): { frontmatter: Record<string, string>; content: string } {
const fm: Record<string, string> = {};
let content = raw;
@@ -91,6 +100,7 @@ function buildGearDoc(name: string, fm: Record<string, string>, content: string,
weapon: {
damage: '',
damageType: 'physical',
woundKind: '',
properties: [],
},
armor: {
@@ -105,6 +115,7 @@ function buildGearDoc(name: string, fm: Record<string, string>, content: string,
if (category === 'weapon') {
system.weapon.damage = fm.damage || fm.obrazenia || '';
system.weapon.woundKind = parseWoundKind(fm.wound || fm.rana);
if (fm.traits || fm.cechy) {
system.weapon.properties = parseList(fm.traits || fm.cechy);
}
+4
View File
@@ -192,6 +192,10 @@ export type GearCategory = (typeof GEAR_CATEGORIES)[number];
export const DAMAGE_TYPES = ['physical', 'magical', 'environmental'] as const;
export type DamageType = (typeof DAMAGE_TYPES)[number];
/** Weapon wound kinds ('' = unspecified). Cutting / piercing feed the passive Krew talents. */
export const WOUND_KINDS = ['', 'cutting', 'piercing', 'blunt'] as const;
export type WoundKind = (typeof WOUND_KINDS)[number];
/** Armor types */
export const ARMOR_TYPES = ['light', 'medium', 'heavy', 'shield'] as const;
export type ArmorType = (typeof ARMOR_TYPES)[number];
+3 -1
View File
@@ -6,7 +6,7 @@ import {
makeArrayField,
fields,
} from './fields';
import { GEAR_CATEGORIES, DAMAGE_TYPES, ARMOR_TYPES, RARITIES } from '../constants';
import { GEAR_CATEGORIES, DAMAGE_TYPES, ARMOR_TYPES, RARITIES, WOUND_KINDS } from '../constants';
/**
* Unified Gear item - discriminated by `category`.
@@ -39,6 +39,8 @@ export class GearData extends foundry.abstract.TypeDataModel {
choices: DAMAGE_TYPES as unknown as readonly string[],
}),
properties: makeArrayField(new f.StringField({ blank: false })),
// Rodzaj rany: cutting (tnąca) / piercing (kłująca) / blunt (obuchowa); '' = not specified.
woundKind: makeStringField('', { blank: true, choices: WOUND_KINDS as unknown as readonly string[] }),
}),
armor: new f.SchemaField({
+1 -1
View File
@@ -2,7 +2,7 @@
"id": "hbm-rpg-v3",
"title": "Homebrew Magic: RPG v3",
"description": "System Foundry VTT dla Homebrew Magic: Role Playing Game v3 - autorska gra fabularna z mechaniką puli kości d6 (TS - Trudność:Sukcesy).",
"version": "1.5.0",
"version": "1.5.1",
"manifest": "https://vtt-content.octoturge.com/packages/system/hbm-rpg-v3.json",
"download": "https://vtt-content.octoturge.com/packages/system/hbm-rpg-v3-v1.5.0.zip",
"compatibility": {
+8
View File
@@ -31,6 +31,14 @@
<option value="environmental" {{#if (eq system.weapon.damageType 'environmental')}}selected{{/if}}>{{localize 'HBM.gear.environmental'}}</option>
</select>
</label>
<label>{{localize 'HBM.gear.woundKind'}}:
<select name="system.weapon.woundKind">
<option value="" {{#if (eq system.weapon.woundKind '')}}selected{{/if}}>{{localize 'HBM.gear.wound.none'}}</option>
<option value="cutting" {{#if (eq system.weapon.woundKind 'cutting')}}selected{{/if}}>{{localize 'HBM.gear.wound.cutting'}}</option>
<option value="piercing" {{#if (eq system.weapon.woundKind 'piercing')}}selected{{/if}}>{{localize 'HBM.gear.wound.piercing'}}</option>
<option value="blunt" {{#if (eq system.weapon.woundKind 'blunt')}}selected{{/if}}>{{localize 'HBM.gear.wound.blunt'}}</option>
</select>
</label>
</fieldset>
{{/if}}
+17
View File
@@ -0,0 +1,17 @@
import { describe, it, expect } from 'vitest';
import { parseWoundKind } from '../../scripts/parsers/gear-parser';
describe('gear-parser parseWoundKind', () => {
it('maps Polish and English labels', () => {
expect(parseWoundKind('tnąca')).toBe('cutting');
expect(parseWoundKind('Kłująca')).toBe('piercing');
expect(parseWoundKind('obuchowa')).toBe('blunt');
expect(parseWoundKind('piercing')).toBe('piercing');
});
it('never guesses: missing or unknown stays unspecified', () => {
expect(parseWoundKind(undefined)).toBe('');
expect(parseWoundKind('')).toBe('');
expect(parseWoundKind('ognista')).toBe('');
});
});