diff --git a/.vscode/settings.json b/.vscode/settings.json index e736c4dd..015b22af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -28,5 +28,12 @@ "statusBar.debuggingForeground": "#e7e7e7" }, "peacock.color": "#643312", - "cSpell.words": ["ageorgedev", "Halfling", "Halflings", "Khorvaire"] + "cSpell.words": [ + "ageorgedev", + "Halfling", + "Halflings", + "Khorvaire", + "spellcasting", + "Warforged" + ] } diff --git a/apps/game-tools/src/data/dnd-characters/common/common-feats.ts b/apps/game-tools/src/data/dnd-characters/common/common-feats.ts index 61f20ee9..fd7ec461 100644 --- a/apps/game-tools/src/data/dnd-characters/common/common-feats.ts +++ b/apps/game-tools/src/data/dnd-characters/common/common-feats.ts @@ -17,3 +17,9 @@ export const SAVAGE_ATTACKER: Feature = { description: "Once per turn, you may roll a weapon's damage dice twice and use either rolls.", }; + +export const SKILLED: Feature = { + name: 'Skilled', + description: + 'You have additional proficiency in any combination of 3 skills or tools (already considered in this sheet)', +}; diff --git a/apps/game-tools/src/data/dnd-characters/common/index.ts b/apps/game-tools/src/data/dnd-characters/common/index.ts index 6417e2b9..15083694 100644 --- a/apps/game-tools/src/data/dnd-characters/common/index.ts +++ b/apps/game-tools/src/data/dnd-characters/common/index.ts @@ -1,3 +1,4 @@ export * from './common-class-features'; export * from './common-feats'; export * from './common-species-traits'; +export * from './species-traits'; diff --git a/apps/game-tools/src/data/dnd-characters/common/species-traits.ts b/apps/game-tools/src/data/dnd-characters/common/species-traits.ts new file mode 100644 index 00000000..edb91445 --- /dev/null +++ b/apps/game-tools/src/data/dnd-characters/common/species-traits.ts @@ -0,0 +1,29 @@ +import type { Feature } from '@ageorgedev/dnd-character-sheet'; + +export const WarforgedTraits: Feature[] = [ + { + name: 'Construct resilience', + description: + 'You have Resistance to Poison damage. You also have Advantage on saving throws you make to avoid or end the Poisoned condition.', + }, + { + name: 'Integrated Protection', + description: + "You have +1 AC (already considered in this sheet). Your armour can't be removed against your will", + }, + { + name: "Sentry's Rest", + description: + "You don't need to sleep, and magic can't put you to sleep. You can finish a Long Rest in 6hrs if you spend that time inactive and motionless. You appear inert but are still conscious.", + }, + { + name: 'Specialized Design', + description: + 'You gain one skill proficiency and one tool proficiency of your choice (already considered in this sheet)', + }, + { + name: 'Tireless', + description: + "You don't need to consume water, food, and don't need to breathe", + }, +]; diff --git a/apps/game-tools/src/data/dnd-characters/index.ts b/apps/game-tools/src/data/dnd-characters/index.ts index 546ff3c4..4431808f 100644 --- a/apps/game-tools/src/data/dnd-characters/index.ts +++ b/apps/game-tools/src/data/dnd-characters/index.ts @@ -10,6 +10,8 @@ import { ElnorinData } from './elnorin-lunarrest/elnorin-lunarrest'; import ElnorinSpellBook from './elnorin-lunarrest/elnorrin-spellbook.pdf?url'; import { GonvarData } from './gonvar-feathertide/gonvar-feathertide'; import { OmarinData } from './omarin-kenate/omarin-kenate'; +import { RustyData } from './rusty/rusty'; +import RustySpellBook from './rusty/rusty-spellbook.pdf'; import { SaoraData } from './saora-embervale/saora-embervale'; import SaoraSpellBook from './saora-embervale/saora-spellbook.pdf?url'; import { SplitterData } from './splitter/splitter'; @@ -46,7 +48,14 @@ const characters: BasePack[] = [ { data: ElnorinData, spellBook: ElnorinSpellBook }, { data: GonvarData }, { data: ClawData, spellBook: ClawSpellBook }, - { data: OmarinData }, + { + data: OmarinData, + visualAdjustments: { + classFeaturesFontSize: 'small', + speciesAndFeatsFontSize: 'small', + speciesAndFeatsCombinedPanel: true, + }, + }, { data: SplitterData, visualAdjustments: { @@ -64,6 +73,11 @@ const characters: BasePack[] = [ notesRows: 0, }, }, + { + data: RustyData, + spellBook: RustySpellBook, + // TODO: alternate title for spellbook + }, ]; const processAndGroup = compose( diff --git a/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts b/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts index 71ba0e0b..b14f131e 100644 --- a/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts +++ b/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts @@ -102,7 +102,14 @@ export const OmarinData: Character = { notes: 'Ammo (80/20, Bolt), Loading, 2-handed', }, ], - equipment: ['Bedroll, Backpack, Lantern', 'Flasks of oil (x4)'], + equipment: [ + "Backpack, Bedroll, Iron Pot, Shovel, Traveller's clothes, Rope, Tinderbox, Waterskin", + "Carpenter's Tools, Tinker's Tools", + 'Oil flasks = 4', + 'Torches = 10', + 'Ration (days) = 10', + "Healer's Kit charges = 10", + ], armorProficiencies: [ ArmorProficiency.LightArmor, ArmorProficiency.MediumArmor, @@ -156,7 +163,6 @@ export const OmarinData: Character = { SECOND_WIND, TACTICAL_MIND, ACTION_SURGE, - // TODO: the actual manuevers COMBAT_SUPERIORITY, { name: 'Uncanny Metabolism', @@ -169,6 +175,27 @@ export const OmarinData: Character = { refresh: { kind: 'long-rest' }, }, }, + { + name: 'Pushing Attack', + cost: '1 Superiority dice', + description: ` + on Hit, add Superiority Dice to damage. If target size is Large or smaller, they make a Str. save (DC <%= abilitySaveDCs.DEX %>); on failure be pushed 15 ft directly away from you. + `, + }, + { + name: 'Goading Attack', + cost: '1 Superiority dice', + description: ` + on Hit, add Superiority Dice to damage. Target must succeed on Wis. save (DC <%= abilitySaveDCs.DEX %>); on failure, have Disadvantage on attack rolls against any target other than you, until the end of your next turn + `, + }, + { + name: 'Maneuvering Attack', + cost: '1 Superiority dice', + description: ` + on Hit, add Superiority Dice to damage. Choose a willing creature who can see or hear you; they can use their Reaction immediately to move half their speed without provoking Opportunity attacks. + `, + }, ], speciesTraits: [ darkvision('120ft'), @@ -208,9 +235,5 @@ export const OmarinData: Character = { numberOfCantrips: 0, numberOfPreparedSpells: 0, }, - - backstory: ` -
This is the story of Omarin
-Early years Had a lot of fun
- `, + // TODO: work on the backstory and appearance }; diff --git a/apps/game-tools/src/data/dnd-characters/rusty/rusty-spellbook.pdf b/apps/game-tools/src/data/dnd-characters/rusty/rusty-spellbook.pdf new file mode 100644 index 00000000..497b80aa Binary files /dev/null and b/apps/game-tools/src/data/dnd-characters/rusty/rusty-spellbook.pdf differ diff --git a/apps/game-tools/src/data/dnd-characters/rusty/rusty.ts b/apps/game-tools/src/data/dnd-characters/rusty/rusty.ts new file mode 100644 index 00000000..48294780 --- /dev/null +++ b/apps/game-tools/src/data/dnd-characters/rusty/rusty.ts @@ -0,0 +1,175 @@ +import { + Ability, + type Character, + Skill, + SPELL, + withSpellMods, +} from '@ageorgedev/dnd-character-sheet'; +import { SKILLED, WarforgedTraits } from '../common'; + +export const RustyData: Character = { + name: 'Rusty', + species: 'Warforged', + background: 'Archaeologist', + classes: [ + { + name: 'Artificer', + level: 3, + subclass: 'Artillerist', + }, + ], + abilities: { + [Ability.Strength]: 8, + [Ability.Dexterity]: 16, + [Ability.Constitution]: 14, + [Ability.Intelligence]: 17, + [Ability.Wisdom]: 10, + [Ability.Charisma]: 8, + }, + savingThrowProficiencies: [Ability.Constitution, Ability.Intelligence], + skillProficiencies: [ + Skill.Athletics, + Skill.Acrobatics, + Skill.Arcana, + Skill.History, + Skill.Investigation, + Skill.Perception, + Skill.Survival, + Skill.Intimidation, + ], + skillExpertise: [], + baseArmorClass: 16, + speed: 30, + hitPoints: { + maximum: 27, + }, + armorProficiencies: ['Light Armor', 'Medium Armor', 'Shield'], + weaponProficiencies: [], + toolProficiencies: [ + "Cartographer's Tools", + "Thieves' Tools", + "Tinker's Tools", + "Smith's Tools", + "Navigator's Tools", + "Woodcarver's Tools", + ], + languages: ['Common', 'Elvish'], + attacks: [ + { + kind: 'weapon', + name: 'Heavy Crossbow', + ability: Ability.Dexterity, + damage: [{ dice: '1d10', type: 'Piercing' }], + notes: 'Ammunition (80/320 bolt), 2-handed, Loading', + }, + { + kind: 'weapon', + name: 'Dagger', + ability: Ability.Dexterity, + damage: [{ dice: '1d4', type: 'Piercing' }], + notes: 'Finesse, Light, Thrown (range 20/60)', + }, + { + kind: 'spell-with-attack', + name: 'Fire Bolt', + damage: [{ dice: '1d10', type: 'Fire' }], + notes: '120 ft range', + }, + ], + features: [ + { + name: "Tinker's Magic", + castingTime: 'Action', + description: + "While holding Tinker's Tools, you can create one item in an unoccupied space within 5 ft of yourself. Choose the item from the Tinker's Magic List (find them in the next sheet). The items last until you finish a Long Rest", + resource: { + id: 'TinkerMagic', + name: "Tinker's Magic", + count: { + kind: 'fixed', + value: 2, + }, + refresh: { + kind: 'long-rest', + }, + }, + }, + { + name: 'Replicate Magic Item', + description: ` + You have learned arcane plans that you use to make magic items. + On finishing a Long Rest, you can create 1 or 2 different magic items from these plans. The items last until you die. You can only have 2 such created magic items at any given point, creating another one will make the oldest one vanish. + You know the following plans: +Archaeologist. You've made a lifelong study of the lost and fallen cultures of the past. Perhaps you have studied at Morgrave University or similar institutions. Or perhaps you have made it your mission to dig through the remains of the Last War
+ `, + equipment: [ + 'Backpack, Caltrops, Crowbar, Rope, Tinderbox', + "Navigator's Tools, Tinker's Tools, Woodcarver's Tools", + 'Studded Leather Armour', + 'Light Crossbow, Dagger', + 'Crossbow bolts = 20', + 'Oil flasks = 2', + 'Torches = 10', + ], +}; diff --git a/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts b/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts index 8be687f0..e7b6291a 100644 --- a/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts +++ b/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts @@ -7,7 +7,7 @@ import { SPELL, } from '@ageorgedev/dnd-character-sheet'; import { mapObjIndexed } from 'ramda'; -import { expertise } from '../common'; +import { expertise, SKILLED } from '../common'; export const SaoraData: Character = { name: 'Saora Embervale', @@ -166,13 +166,7 @@ export const SaoraData: Character = { 'You can take the Hide action when you are obscured by a creature larger than you.', }, ], - feats: [ - { - name: 'Skilled', - description: - 'You have additional proficiency in any combination of 3 skills or tools (already considered in this sheet)', - }, - ], + feats: [SKILLED], spellcasting: { ability: Ability.Charisma, slots: { diff --git a/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts b/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts index 037d1f52..8ea46562 100644 --- a/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts +++ b/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts @@ -3,7 +3,7 @@ import { type Character, Skill, } from '@ageorgedev/dnd-character-sheet'; -import { SAVAGE_ATTACKER, weaponMastery } from '../common'; +import { SAVAGE_ATTACKER, WarforgedTraits, weaponMastery } from '../common'; export const SplitterData: Character = { name: 'Splitter', @@ -130,33 +130,7 @@ export const SplitterData: Character = { 'If you do Reckless Attack while Rage is active, you do an extra 2d6 damage to the first target you hit on your turn with a Str. based attack.', }, ], - speciesTraits: [ - { - name: 'Construct resilience', - description: - 'You have Resistance to Poison damage. You also have Advantage on saving throws you make to avoid or end the Poisoned condition.', - }, - { - name: 'Integrated Protection', - description: - "You have +1 AC (already considered in this sheet). Your armour can't be removed against your will", - }, - { - name: "Sentry's Rest", - description: - "You don't need to sleep, and magic can't put you to sleep. You can finish a Long Rest in 6hrs if you spend that time inactive and motionless. You appear inert but are still conscious.", - }, - { - name: 'Specialised Design', - description: - 'You gain one skill proficiency and one tool proficiency of your choice (already considered in this sheet)', - }, - { - name: 'Tireless', - description: - "You don't need to consume water, food, and don't need to breathe", - }, - ], + speciesTraits: WarforgedTraits, feats: [SAVAGE_ATTACKER], appearance: 'Warforged are mechanical beings built as weapons to fight in the Last War. You comprise a blend of organic and inorganic materials. Rootlike chords infused with alchemical fluids serve as your muscles, wrapped around a framework of steel, darkwood or stone', diff --git a/apps/game-tools/src/routes/_public/dnd/characters/index.tsx b/apps/game-tools/src/routes/_public/dnd/characters/index.tsx index 6b02482f..455ddb9e 100644 --- a/apps/game-tools/src/routes/_public/dnd/characters/index.tsx +++ b/apps/game-tools/src/routes/_public/dnd/characters/index.tsx @@ -3,6 +3,7 @@ import { InterfaceLg, } from '@ageorgedev/design-system/typography/typography-components'; import { createFileRoute } from '@tanstack/react-router'; +import { Fragment } from 'react'; import { CharacterRosterCard } from '../../../../components/dnd/CharacterRosterCard'; import { AllMyCharactersInBrief } from '../../../../data/dnd-characters'; import { groupCharactersByLevel } from '../../../../lib/group-characters-by-level'; @@ -23,27 +24,28 @@ function RouteComponent() { D&D 5.5e Characters -