manage_condition
Track and modify D&D 5e conditions like blinded or poisoned on characters during gameplay. Add, remove, query status, or advance condition durations to manage combat effects.
Instructions
Manage D&D 5e conditions on targets (add, remove, query, tick duration)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| targetId | No | ||
| encounterId | No | ||
| operation | No | ||
| condition | No | ||
| source | No | ||
| duration | No | ||
| saveDC | No | ||
| saveAbility | No | ||
| exhaustionLevels | No | ||
| roundNumber | No | ||
| batch | No |
Implementation Reference
- src/modules/combat.ts:376-397 (handler)Core handler function for the manage_condition tool. Dispatches single or batch operations (add, remove, query, tick) on character conditions.export function manageCondition(input: ManageConditionInput | { batch: ManageConditionInput[] }): string { // Check if this is a batch operation if ('batch' in input && input.batch) { return handleBatchConditions(input.batch); } const singleInput = input as ManageConditionInput; const { targetId, operation } = singleInput; switch (operation) { case 'add': return handleAddCondition(targetId, singleInput); case 'remove': return handleRemoveCondition(targetId, singleInput); case 'query': return handleQueryConditions(targetId); case 'tick': return handleTickDuration(targetId, singleInput); default: throw new Error(`Unknown operation: ${operation}`); } }
- src/modules/combat.ts:29-66 (schema)Zod input schema definition for manage_condition tool, supporting single condition operations or batch arrays.// Single condition operation const singleConditionSchema = z.object({ targetId: z.string(), encounterId: z.string().optional(), operation: z.enum(['add', 'remove', 'query', 'tick']), // For add/remove condition: ConditionSchema.optional(), source: z.string().optional(), duration: z.union([ z.number(), // Rounds z.literal('concentration'), z.literal('until_dispelled'), z.literal('until_rest'), z.literal('save_ends'), ]).optional(), saveDC: z.number().optional(), saveAbility: AbilitySchema.optional(), // For exhaustion exhaustionLevels: z.number().min(1).optional(), // For tick roundNumber: z.number().optional(), }); // Support both single and batch operations export const manageConditionSchema = z.union([ singleConditionSchema, z.object({ batch: z.array(singleConditionSchema).min(1).max(20), }), ]); export type ManageConditionInput = z.infer<typeof singleConditionSchema>;
- src/registry.ts:402-419 (registration)Static registration of manage_condition tool in the toolRegistry, converting Zod schema to JSON schema and providing async wrapper handler.manage_condition: { name: 'manage_condition', description: 'Manage D&D 5e conditions on targets (add, remove, query, tick duration)', inputSchema: toJsonSchema(manageConditionSchema), handler: async (args) => { try { const validated = manageConditionSchema.parse(args); const result = manageCondition(validated); return success(result); } catch (err) { if (err instanceof z.ZodError) { const messages = err.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', '); return error(`Validation failed: ${messages}`); } const message = err instanceof Error ? err.message : String(err); return error(message); } },
- src/modules/combat.ts:284-370 (helper)Supporting function to compute effective stats for characters under active conditions, used by character retrieval to show condition impacts.export function calculateEffectiveStats(characterId: string, baseStats: { maxHp: number; hp: number; speed: number; ac?: number; }): { maxHp: { base: number; effective: number; modified: boolean }; speed: { base: number; effective: number; modified: boolean }; ac?: { base: number; effective: number; modified: boolean }; conditionEffects: string[]; // Human-readable list of active effects } { const conditions = getActiveConditions(characterId); const result = { maxHp: { base: baseStats.maxHp, effective: baseStats.maxHp, modified: false }, speed: { base: baseStats.speed, effective: baseStats.speed, modified: false }, ac: baseStats.ac ? { base: baseStats.ac, effective: baseStats.ac, modified: false } : undefined, conditionEffects: [] as string[], }; // Apply each condition's mechanical effects for (const cond of conditions) { // Get mechanical effects (either custom or built-in) let effects = cond.mechanicalEffects; if (!effects && (cond.condition in CONDITION_EFFECTS || cond.condition === 'exhaustion')) { effects = getBuiltInMechanicalEffects(cond.condition, cond.exhaustionLevel); } if (!effects) continue; // Apply HP maximum modifier if (effects.maxHpMultiplier !== undefined) { result.maxHp.effective = Math.floor(result.maxHp.effective * effects.maxHpMultiplier); result.maxHp.modified = true; const condName = cond.condition === 'exhaustion' ? `Exhaustion ${cond.exhaustionLevel}` : String(cond.condition).charAt(0).toUpperCase() + String(cond.condition).slice(1); result.conditionEffects.push(`${condName}: HP max ×${effects.maxHpMultiplier}`); } if (effects.maxHpModifier !== undefined) { result.maxHp.effective += effects.maxHpModifier; result.maxHp.modified = true; result.conditionEffects.push(`HP max ${effects.maxHpModifier > 0 ? '+' : ''}${effects.maxHpModifier}`); } // Apply speed modifier if (effects.speedMultiplier !== undefined) { result.speed.effective = Math.floor(result.speed.effective * effects.speedMultiplier); result.speed.modified = true; const condName = cond.condition === 'exhaustion' ? `Exhaustion ${cond.exhaustionLevel}` : String(cond.condition).charAt(0).toUpperCase() + String(cond.condition).slice(1); result.conditionEffects.push(`${condName}: Speed ×${effects.speedMultiplier}`); } if (effects.speedModifier !== undefined) { result.speed.effective += effects.speedModifier; result.speed.modified = true; result.conditionEffects.push(`Speed ${effects.speedModifier > 0 ? '+' : ''}${effects.speedModifier}`); } if (effects.cannotMove) { result.speed.effective = 0; result.speed.modified = true; } // Apply AC modifier if (result.ac && effects.acModifier !== undefined) { result.ac.effective += effects.acModifier; result.ac.modified = true; result.conditionEffects.push(`AC ${effects.acModifier > 0 ? '+' : ''}${effects.acModifier}`); } // Track other effects (disadvantage, auto-fail, etc.) if (effects.disadvantageOn && effects.disadvantageOn.length > 0) { const condName = cond.condition === 'exhaustion' ? `Exhaustion ${cond.exhaustionLevel}` : String(cond.condition).charAt(0).toUpperCase() + String(cond.condition).slice(1); result.conditionEffects.push(`${condName}: Disadv. on ${effects.disadvantageOn.join(', ')}`); } if (effects.autoFailSaves && effects.autoFailSaves.length > 0) { result.conditionEffects.push(`Auto-fail ${effects.autoFailSaves.join(', ').toUpperCase()} saves`); } if (effects.incapacitated) { result.conditionEffects.push('Incapacitated'); } } return result; }