/** * Tests for mechanic override system from the design document * Tests special abilities that modify core battle mechanics */ import { describe, it, expect, beforeEach } from 'vitest'; import { BattleEngine } from './BattleEngine'; import { PicletDefinition, Move, SpecialAbility } from './types'; import { PicletType, AttackType } from './types'; const STANDARD_STATS = { hp: 100, attack: 80, defense: 70, speed: 60 }; describe('Mechanic Override System - TDD Implementation', () => { describe('Critical Hit Mechanics', () => { it('should handle Shell Armor - cannot be critically hit', () => { const shellArmor: SpecialAbility = { name: "Shell Armor", description: "Hard shell prevents critical hits", effects: [ { type: 'mechanicOverride', mechanic: 'criticalHits', condition: 'always', value: false } ] }; const shellPiclet: PicletDefinition = { name: "Shell Defender", description: "Protected by a hard shell", tier: 'medium', primaryType: PicletType.MINERAL, baseStats: STANDARD_STATS, nature: "Bold", specialAbility: shellArmor, movepool: [{ name: "Tackle", type: AttackType.NORMAL, power: 40, accuracy: 100, pp: 35, priority: 0, flags: [], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }] }] }; // Test would verify this Piclet cannot be critically hit expect(shellArmor.effects![0].mechanic).toBe('criticalHits'); expect(shellArmor.effects![0].value).toBe(false); }); it('should handle Super Luck - always critical hits', () => { const superLuck: SpecialAbility = { name: "Super Luck", description: "Extremely lucky, always lands critical hits", effects: [ { type: 'mechanicOverride', mechanic: 'criticalHits', condition: 'always', value: true } ] }; expect(superLuck.effects![0].value).toBe(true); }); it('should handle Scope Lens - double critical hit rate', () => { const scopeLens: SpecialAbility = { name: "Scope Lens", description: "Enhanced precision doubles critical hit rate", effects: [ { type: 'mechanicOverride', mechanic: 'criticalHits', condition: 'always', value: 'double' } ] }; expect(scopeLens.effects![0].value).toBe('double'); }); }); describe('Status Immunity', () => { it('should handle Insomnia - sleep immunity', () => { const insomnia: SpecialAbility = { name: "Insomnia", description: "Prevents sleep status", effects: [ { type: 'mechanicOverride', mechanic: 'statusImmunity', value: ['sleep'] } ] }; const insomniaPiclet: PicletDefinition = { name: "Sleepless Guardian", description: "Never sleeps", tier: 'medium', primaryType: PicletType.CULTURE, baseStats: STANDARD_STATS, nature: "Alert", specialAbility: insomnia, movepool: [{ name: "Tackle", type: AttackType.NORMAL, power: 40, accuracy: 100, pp: 35, priority: 0, flags: [], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }] }] }; expect(insomnia.effects![0].value).toContain('sleep'); }); it('should handle multi-status immunity', () => { const immunity: SpecialAbility = { name: "Pure Body", description: "Immune to poison and burn", effects: [ { type: 'mechanicOverride', mechanic: 'statusImmunity', value: ['poison', 'burn'] } ] }; expect(immunity.effects![0].value).toEqual(['poison', 'burn']); }); }); describe('Damage Reflection', () => { it('should handle Rough Skin - contact damage reflection', () => { const roughSkin: SpecialAbility = { name: "Rough Skin", description: "Rough skin damages attackers on contact", triggers: [ { event: 'onContactDamage', effects: [ { type: 'damage', target: 'attacker', formula: 'fixed', value: 12 } ] } ] }; const roughPiclet: PicletDefinition = { name: "Spike Beast", description: "Covered in rough spikes", tier: 'medium', primaryType: PicletType.MINERAL, baseStats: STANDARD_STATS, nature: "Hardy", specialAbility: roughSkin, movepool: [{ name: "Tackle", type: AttackType.NORMAL, power: 40, accuracy: 100, pp: 35, priority: 0, flags: [], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }] }] }; expect(roughSkin.triggers![0].event).toBe('onContactDamage'); expect(roughSkin.triggers![0].effects[0].target).toBe('attacker'); }); it('should handle damage reflection percentage', () => { const reflectArmor: SpecialAbility = { name: "Mirror Armor", description: "Reflects 50% of damage back", effects: [ { type: 'mechanicOverride', mechanic: 'damageReflection', value: 0.5 } ] }; expect(reflectArmor.effects![0].value).toBe(0.5); }); }); describe('Type Mechanics', () => { it('should handle Wonder Guard - only super-effective moves hit', () => { const wonderGuard: SpecialAbility = { name: "Wonder Guard", description: "Only super-effective moves deal damage", effects: [ { type: 'mechanicOverride', mechanic: 'damageCalculation', condition: 'ifNotSuperEffective', value: false } ] }; expect(wonderGuard.effects![0].mechanic).toBe('damageCalculation'); expect(wonderGuard.effects![0].condition).toBe('ifNotSuperEffective'); }); it('should handle Levitate - ground type immunity', () => { const levitate: SpecialAbility = { name: "Levitate", description: "Floating ability makes ground moves miss", effects: [ { type: 'mechanicOverride', mechanic: 'typeImmunity', value: ['ground'] } ] }; expect(levitate.effects![0].value).toContain('ground'); }); it('should handle Protean - type changes to match move', () => { const protean: SpecialAbility = { name: "Protean", description: "Changes type to match the move being used", triggers: [ { event: 'beforeMoveUse', effects: [ { type: 'mechanicOverride', mechanic: 'typeChange', value: 'matchMoveType' } ] } ] }; expect(protean.triggers![0].event).toBe('beforeMoveUse'); expect(protean.triggers![0].effects[0].value).toBe('matchMoveType'); }); }); describe('Healing Mechanics', () => { it('should handle Poison Heal - poison heals instead of damages', () => { const poisonHeal: SpecialAbility = { name: "Poison Heal", description: "Poison heals instead of damages", effects: [ { type: 'mechanicOverride', mechanic: 'healingInversion', value: 'invert' } ] }; expect(poisonHeal.effects![0].mechanic).toBe('healingInversion'); expect(poisonHeal.effects![0].value).toBe('invert'); }); it('should handle healing blocked', () => { const healBlock: SpecialAbility = { name: "Cursed Body", description: "Cannot be healed by any means", effects: [ { type: 'mechanicOverride', mechanic: 'healingBlocked', value: true } ] }; expect(healBlock.effects![0].value).toBe(true); }); }); describe('Damage Absorption', () => { it('should handle Photosynthesis - absorbs flora moves', () => { const photosynthesis: SpecialAbility = { name: "Photosynthesis", description: "Absorbs flora-type moves to restore HP", triggers: [ { event: 'onDamageTaken', condition: 'ifMoveType:flora', effects: [ { type: 'mechanicOverride', mechanic: 'damageAbsorption', value: 'absorb' }, { type: 'heal', target: 'self', formula: 'percentage', value: 25 } ] } ] }; expect(photosynthesis.triggers![0].condition).toBe('ifMoveType:flora'); expect(photosynthesis.triggers![0].effects[0].mechanic).toBe('damageAbsorption'); }); }); describe('Stat Modification Mechanics', () => { it('should handle Contrary - stat changes are reversed', () => { const contrary: SpecialAbility = { name: "Contrary", description: "Stat changes have the opposite effect", effects: [ { type: 'mechanicOverride', mechanic: 'statModification', value: 'invert' } ] }; expect(contrary.effects![0].value).toBe('invert'); }); }); describe('Flag-Based Immunities', () => { it('should handle Sky Dancer - immune to ground-flagged attacks', () => { const skyDancer: SpecialAbility = { name: "Sky Dancer", description: "Floating in air, immune to ground-based attacks", effects: [ { type: 'mechanicOverride', mechanic: 'flagImmunity', value: ['ground'] } ] }; expect(skyDancer.effects![0].value).toContain('ground'); }); it('should handle Sound Barrier - immune to sound attacks', () => { const soundBarrier: SpecialAbility = { name: "Sound Barrier", description: "Natural sound dampening prevents sound-based moves", effects: [ { type: 'mechanicOverride', mechanic: 'flagImmunity', value: ['sound'] } ] }; expect(soundBarrier.effects![0].value).toContain('sound'); }); it('should handle Soft Body - immune to explosive, weak to punch', () => { const softBody: SpecialAbility = { name: "Soft Body", description: "Gelatinous form absorbs explosions but vulnerable to direct hits", effects: [ { type: 'mechanicOverride', mechanic: 'flagImmunity', value: ['explosive'] }, { type: 'mechanicOverride', mechanic: 'flagWeakness', value: ['punch'] } ] }; expect(softBody.effects![0].value).toContain('explosive'); expect(softBody.effects![1].value).toContain('punch'); }); it('should handle flag resistance', () => { const thickHide: SpecialAbility = { name: "Thick Hide", description: "Tough skin reduces impact from physical contact", effects: [ { type: 'mechanicOverride', mechanic: 'flagResistance', value: ['contact'] } ] }; expect(thickHide.effects![0].value).toContain('contact'); }); }); describe('Priority Override', () => { it('should handle Prankster - status moves get priority', () => { const prankster: SpecialAbility = { name: "Prankster", description: "Status moves gain priority", effects: [ { type: 'mechanicOverride', mechanic: 'priorityOverride', condition: 'ifStatusMove', value: 1 } ] }; expect(prankster.effects![0].condition).toBe('ifStatusMove'); expect(prankster.effects![0].value).toBe(1); }); }); describe('Drain Inversion', () => { it('should handle Vampiric - drain moves damage the drainer', () => { const vampiric: SpecialAbility = { name: "Vampiric", description: "Cursed blood damages those who try to drain it", triggers: [ { event: 'onHPDrained', effects: [ { type: 'mechanicOverride', mechanic: 'drainInversion', value: true }, { type: 'damage', target: 'attacker', formula: 'fixed', value: 20 } ] } ] }; expect(vampiric.triggers![0].event).toBe('onHPDrained'); expect(vampiric.triggers![0].effects[0].mechanic).toBe('drainInversion'); }); }); describe('Target Redirection', () => { it('should handle Magic Bounce - reflects status moves', () => { const magicBounce: SpecialAbility = { name: "Magic Bounce", description: "Reflects status moves back at the user", triggers: [ { event: 'onStatusMoveTargeted', effects: [ { type: 'mechanicOverride', mechanic: 'targetRedirection', value: 'reflect' } ] } ] }; expect(magicBounce.triggers![0].event).toBe('onStatusMoveTargeted'); expect(magicBounce.triggers![0].effects[0].value).toBe('reflect'); }); }); describe('Status Replacement', () => { it('should handle Frost Walker - freeze becomes attack boost', () => { const frostWalker: SpecialAbility = { name: "Frost Walker", description: "Instead of being frozen, gains +50% attack", effects: [ { type: 'mechanicOverride', mechanic: 'statusReplacement', value: { status: 'freeze', replacement: { type: 'modifyStats', target: 'self', stats: { attack: 'greatly_increase' } } } } ] }; expect(frostWalker.effects![0].mechanic).toBe('statusReplacement'); expect(frostWalker.effects![0].value.status).toBe('freeze'); }); }); describe('Damage Multiplier', () => { it('should handle damage multiplication abilities', () => { const damageBoost: SpecialAbility = { name: "Rage Mode", description: "All damage dealt is doubled when at low HP", effects: [ { type: 'mechanicOverride', mechanic: 'damageMultiplier', condition: 'ifLowHp', value: 2.0 } ] }; expect(damageBoost.effects![0].value).toBe(2.0); expect(damageBoost.effects![0].condition).toBe('ifLowHp'); }); }); describe('Extra Turn Mechanics', () => { it('should handle extra turn abilities', () => { const extraTurn: SpecialAbility = { name: "Time Distortion", description: "Gets an extra turn when switching in", triggers: [ { event: 'onSwitchIn', effects: [ { type: 'mechanicOverride', mechanic: 'extraTurn', value: true, condition: 'nextTurn' } ] } ] }; expect(extraTurn.triggers![0].effects[0].mechanic).toBe('extraTurn'); expect(extraTurn.triggers![0].effects[0].condition).toBe('nextTurn'); }); }); });