-
Notifications
You must be signed in to change notification settings - Fork 27
fix(combat): participant ac field propagates through to attack resolver
#57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e7b452a
3262e07
d8c41b1
4fbc410
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -579,6 +579,8 @@ Example (use real UUID from context for player character!): | |
| isEnemy: z.boolean().optional().describe('Whether this is an enemy (auto-detected if not set)'), | ||
| hasLairActions: z.boolean().optional() | ||
| .describe('Adds a LAIR slot at initiative 20 to the turn order'), | ||
| ac: z.number().int().min(0).optional() | ||
| .describe('Armor Class (used by attack resolution; defaults to attacker-side derivation if omitted)'), | ||
| conditions: z.array(z.string()).default([]), | ||
| position: z.object({ x: z.number(), y: z.number(), z: z.number().optional() }).optional() | ||
| .describe('CRIT-003: Spatial position for movement (x, y coordinates)'), | ||
|
|
@@ -1102,7 +1104,10 @@ export async function handleCreateEncounter(args: unknown, ctx: SessionContext) | |
| resistances: p.resistances, | ||
| vulnerabilities: p.vulnerabilities, | ||
| immunities: p.immunities, | ||
| ...extraStats | ||
| ...extraStats, | ||
| // Caller-supplied AC wins over the preset's default so explicit | ||
| // overrides (e.g., a goblin in chain mail) take effect. | ||
| ...(p.ac !== undefined ? { ac: p.ac } : {}) | ||
|
Comment on lines
+1107
to
+1110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect every engine attack-resolution path and how target AC is chosen.
rg -n -C3 "targetAC|executeOpportunityAttack|autoResolveAttack|target\.ac" src/engine/combat/engine.tsRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 1705 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check the structure/type of target object to confirm ac property exists
echo "=== Looking for target object type definition and ac property ==="
rg -n "interface.*Target|type.*Target|\.ac\s*:" src/engine/combat/ --max-count=20
echo ""
echo "=== Search for all targetAC assignments in engine ==="
rg -n "targetAC\s*=" src/engine/combat/
echo ""
echo "=== Check for other auto-resolve or attack patterns ==="
rg -n "autoResolve|auto-resolve|Resolve.*[Aa]ttack" src/engine/combat/Repository: Mnehmos/mnehmos.rpg.mcp Length of output: 132 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Search for Participant type definition ==="
rg -n "interface Participant|type Participant|class Participant" --type ts
echo ""
echo "=== Get full executeOpportunityAttack function context ==="
sed -n '1322,1395p' src/engine/combat/engine.ts
echo ""
echo "=== Search for all targetAC or similar AC computation ==="
rg -n "targetAC|target\.ac" src/engine/combat/engine.ts
echo ""
echo "=== Search for other attack methods ==="
rg -n "executeAttack|resolveAttack|checkAttack" src/engine/combat/engine.tsRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 113 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== List repository structure ==="
ls -la src/ 2>/dev/null || echo "src/ not found"
ls -la src/engine/ 2>/dev/null || echo "src/engine/ not found"
ls -la src/engine/combat/ 2>/dev/null || echo "src/engine/combat/ not found"
echo ""
echo "=== Find all TypeScript files in the repo ==="
find . -name "*.ts" -type f | head -20
echo ""
echo "=== Check if combat-handlers.ts exists ==="
find . -name "combat-handlers.ts" -type f
echo ""
echo "=== Check if engine.ts exists ==="
find . -name "engine.ts" -type fRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 2495 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Get executeOpportunityAttack function and surrounding context ==="
sed -n '1319,1400p' src/engine/combat/engine.ts
echo ""
echo "=== Search for all mentions of targetAC in engine.ts ==="
grep -n "targetAC" src/engine/combat/engine.ts
echo ""
echo "=== Check if target object has an ac property being used elsewhere ==="
grep -n "target\.ac" src/engine/combat/engine.tsRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 3951 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Check combat-handlers.ts at lines 1104-1107 to verify ac assignment ==="
sed -n '1100,1110p' src/server/handlers/combat-handlers.ts
echo ""
echo "=== Search for Participant type/interface in entire codebase ==="
grep -rn "interface Participant\|type Participant\|class.*Participant" src/ --include="*.ts" | head -20
echo ""
echo "=== Search for where ac property is defined or used in any file ==="
grep -rn "\.ac\s*=" src/ --include="*.ts" | head -30
echo ""
echo "=== Check if there are other attack resolution methods ==="
grep -n "handle.*[Aa]ttack\|execute.*[Aa]ttack\|resolve.*[Aa]ttack" src/engine/combat/engine.tsRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 666 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Search for CombatParticipant type definition ==="
grep -rn "interface CombatParticipant\|type CombatParticipant\|class CombatParticipant" src/ --include="*.ts"
echo ""
echo "=== Search for where CombatParticipant is defined ==="
grep -rn "CombatParticipant" src/ --include="*.ts" -l
echo ""
echo "=== Check engine.ts imports and type definitions at top ==="
head -100 src/engine/combat/engine.ts | grep -n "interface\|type\|import\|CombatParticipant"
echo ""
echo "=== Check if other handler methods use target.ac ==="
grep -n "\.ac" src/server/handlers/combat-handlers.ts | head -20Repository: Mnehmos/mnehmos.rpg.mcp Length of output: 2304 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Get full CombatParticipant interface definition ==="
sed -n '22,80p' src/engine/combat/engine.ts
echo ""
echo "=== Get context around line 1315-1316 in combat-handlers.ts to see how normal attacks use target.ac ==="
sed -n '1310,1325p' src/server/handlers/combat-handlers.ts
echo ""
echo "=== Check if there are other attack methods besides executeOpportunityAttack ==="
grep -n "execute.*[Aa]ttack\|resolve.*[Aa]ttack" src/engine/combat/engine.tsRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 3933 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Get executeAttack method to see how it handles target.ac ==="
sed -n '550,650p' src/engine/combat/engine.tsRepository: Mnehmos/mnehmos.rpg.mcp Length of output: 4344 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== Search for all methods that compute AC/DC and might have the same issue ==="
grep -n "10 + \|targetAC\|derive.*ac\|ac.*=" src/engine/combat/engine.ts | head -30
echo ""
echo "=== Get full context of any other attack methods that might ignore target.ac ==="
grep -B5 -A15 "const.*AC.*=" src/engine/combat/engine.ts | head -80Repository: Mnehmos/mnehmos.rpg.mcp Length of output: 3171 Fix The opportunity attack path still derives -const targetAC = 10 + (target.initiativeBonus > 0 ? Math.floor(target.initiativeBonus / 2) : 0);
+const targetAC =
+ target.ac ??
+ (10 + (target.initiativeBonus > 0 ? Math.floor(target.initiativeBonus / 2) : 0));This ensures opportunity attacks use the same AC preference logic as regular attacks. 🤖 Prompt for AI Agents |
||
| } as CombatParticipant; | ||
|
|
||
| return participant; | ||
|
|
@@ -1125,7 +1130,10 @@ export async function handleCreateEncounter(args: unknown, ctx: SessionContext) | |
| const db = getDb(process.env.NODE_ENV === 'test' ? ':memory:' : 'rpg.db'); | ||
| const repo = new EncounterRepository(db); | ||
|
|
||
| // Create the encounter record first (with initiative and isEnemy) | ||
| // Create the encounter record first (with initiative and isEnemy). | ||
| // PR #57 follow-up: persist ac/attackDamage/attackBonus too — those drive | ||
| // attack resolution, and omitting them on initial create made loadState() | ||
| // (before any saveState) drop back to the default-AC-10 fallback. | ||
| repo.create({ | ||
| id: encounterId, | ||
| tokens: state.participants.map(p => ({ | ||
|
|
@@ -1139,6 +1147,14 @@ export async function handleCreateEncounter(args: unknown, ctx: SessionContext) | |
| maxHp: p.maxHp, | ||
| conditions: p.conditions, | ||
| abilityScores: p.abilityScores, | ||
| // Combat stats used by the attack resolver | ||
| ac: p.ac, | ||
| attackDamage: p.attackDamage, | ||
| attackBonus: p.attackBonus, | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| // Damage modifiers — drop them and post-load attacks lose half/2x/immune behavior | ||
| resistances: p.resistances, | ||
| vulnerabilities: p.vulnerabilities, | ||
| immunities: p.immunities, | ||
| // Spatial visualization data | ||
| position: p.position, | ||
| movementSpeed: p.movementSpeed ?? 30, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disallow
ac: 0at validation time.Line 43 currently accepts
0, but the combat resolver treatsdc === 0as “no AC provided”. That makesac: 0behave inconsistently with the rest of the pipeline. Please tighten this tomin(1)here and mirror the same bound in the matchingcreate_encounterand encounter token schemas.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents