Skip to content
Merged

Work #593

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"Bash(done)",
"Bash(gh issue view:*)",
"Bash(npm update:*)",
"WebFetch(domain:eslint.org)"
"WebFetch(domain:eslint.org)",
"Bash(npm test:*)"
],
"deny": [
"Bash(npm run dev)"
Expand Down
124 changes: 123 additions & 1 deletion src/__tests__/utils/character.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("Character Utilities", () => {
);
});

it("calculates AC with worn armor", () => {
it("calculates AC with worn armor (no dex modifier)", () => {
const character = {
equipment: [
{
Expand All @@ -55,6 +55,14 @@ describe("Character Utilities", () => {
category: "armor",
} as Equipment,
],
abilities: {
strength: { value: 10, modifier: 0 },
dexterity: { value: 10, modifier: 0 },
constitution: { value: 10, modifier: 0 },
intelligence: { value: 10, modifier: 0 },
wisdom: { value: 10, modifier: 0 },
charisma: { value: 10, modifier: 0 },
},
};
expect(calculateArmorClass(character)).toBe(15);
});
Expand All @@ -75,6 +83,14 @@ describe("Character Utilities", () => {
category: "shield",
} as Equipment,
],
abilities: {
strength: { value: 10, modifier: 0 },
dexterity: { value: 10, modifier: 0 },
constitution: { value: 10, modifier: 0 },
intelligence: { value: 10, modifier: 0 },
wisdom: { value: 10, modifier: 0 },
charisma: { value: 10, modifier: 0 },
},
};
expect(calculateArmorClass(character)).toBe(13);
});
Expand All @@ -94,6 +110,112 @@ describe("Character Utilities", () => {
GAME_MECHANICS.DEFAULT_UNARMORED_AC
);
});

it("applies Dexterity modifier to AC", () => {
const character = {
equipment: [
{
name: "Leather Armor",
AC: 13,
wearing: true,
category: "armor",
} as Equipment,
],
abilities: {
strength: { value: 10, modifier: 0 },
dexterity: { value: 16, modifier: 2 },
constitution: { value: 10, modifier: 0 },
intelligence: { value: 10, modifier: 0 },
wisdom: { value: 10, modifier: 0 },
charisma: { value: 10, modifier: 0 },
},
};
// Leather armor (13) + Dex modifier (+2) = 15
expect(calculateArmorClass(character)).toBe(15);
});

it("applies negative Dexterity modifier to AC", () => {
const character = {
equipment: [
{
name: "Chain Mail",
AC: 15,
wearing: true,
category: "armor",
} as Equipment,
],
abilities: {
strength: { value: 10, modifier: 0 },
dexterity: { value: 8, modifier: -1 },
constitution: { value: 10, modifier: 0 },
intelligence: { value: 10, modifier: 0 },
wisdom: { value: 10, modifier: 0 },
charisma: { value: 10, modifier: 0 },
},
};
// Chain mail (15) + Dex modifier (-1) = 14
expect(calculateArmorClass(character)).toBe(14);
});

it("applies Dexterity modifier to unarmored AC", () => {
const character = {
equipment: [],
abilities: {
strength: { value: 10, modifier: 0 },
dexterity: { value: 16, modifier: 2 },
constitution: { value: 10, modifier: 0 },
intelligence: { value: 10, modifier: 0 },
wisdom: { value: 10, modifier: 0 },
charisma: { value: 10, modifier: 0 },
},
};
// Default AC (11) + Dex modifier (+2) = 13
expect(calculateArmorClass(character)).toBe(13);
});

it("applies Dexterity modifier with armor and shield", () => {
const character = {
equipment: [
{
name: "Leather Armor",
AC: 13,
wearing: true,
category: "armor",
} as Equipment,
{
name: "Shield",
AC: "+1",
wearing: true,
category: "shield",
} as Equipment,
],
abilities: {
strength: { value: 10, modifier: 0 },
dexterity: { value: 16, modifier: 2 },
constitution: { value: 10, modifier: 0 },
intelligence: { value: 10, modifier: 0 },
wisdom: { value: 10, modifier: 0 },
charisma: { value: 10, modifier: 0 },
},
};
// Leather armor (13) + Shield (+1) + Dex modifier (+2) = 16
expect(calculateArmorClass(character)).toBe(16);
});

it("handles characters without abilities property gracefully", () => {
const character = {
equipment: [
{
name: "Chain Mail",
AC: 15,
wearing: true,
category: "armor",
} as Equipment,
],
};
// Chain mail (15) + no abilities = 15 (defaults to 0 modifier)
expect(calculateArmorClass(character)).toBe(15);
});
});

describe("Movement Rate Calculations", () => {
Expand Down
5 changes: 3 additions & 2 deletions src/components/features/character/creation/EquipmentStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { Button, Icon } from "@/components/ui";
import { Card, Typography, Badge } from "@/components/ui/core/display";
import { InfoCardHeader, StatGrid } from "@/components/ui/composite";
import { ErrorDisplay } from "@/components/ui/core/feedback";
import type { BaseStepProps } from "@/types";
import type { EquipmentPack } from "@/types";
import type { BaseStepProps, EquipmentPack } from "@/types";
import { EquipmentSelector } from "@/components/domain/equipment";
import { useEquipmentManagement } from "@/hooks";
import { useCharacterMutations } from "@/hooks/mutations/useEnhancedMutations";
Expand All @@ -33,6 +32,7 @@ function EquipmentStep({ character, onCharacterChange }: EquipmentStepProps) {
totalValue,
cleanedEquipment,
getStatusMessage,
setStartingGold,
} = useEquipmentManagement(character, onCharacterChange);

// Equipment pack state
Expand All @@ -55,6 +55,7 @@ function EquipmentStep({ character, onCharacterChange }: EquipmentStepProps) {
pack,
});
onCharacterChange(updatedCharacter);
setStartingGold(updatedCharacter.currency.gold);
setSelectedPack(pack);
} catch {
// Error is handled by the mutation hook
Expand Down
1 change: 1 addition & 0 deletions src/hooks/equipment/useEquipmentManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export function useEquipmentManagement(

return {
startingGold,
setStartingGold,
handleGoldRoll,
handleEquipmentAdd,
handleEquipmentRemove,
Expand Down
4 changes: 2 additions & 2 deletions src/types/character.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ export interface Class {

// Base interface for character creation step props
export interface BaseStepProps {
character: Character;
onCharacterChange: (character: Character) => void;
readonly character: Character;
readonly onCharacterChange: (character: Character) => void;
}

// Game rules type exports - derived from gameRules constants
Expand Down
Loading