import { describe, it, expect, beforeEach } from 'vitest'; import { BattleEngine } from './BattleEngine'; import type { PicletDefinition } from './types'; import { PicletType, AttackType } from './types'; describe('Remaining Trigger Events', () => { let basicPiclet: PicletDefinition; let triggerPiclet: PicletDefinition; beforeEach(() => { basicPiclet = { name: "Basic Fighter", description: "Standard test piclet", tier: 'medium', primaryType: PicletType.BEAST, baseStats: { hp: 80, attack: 60, defense: 60, speed: 60 }, nature: "Hardy", specialAbility: { name: "No Ability", description: "" }, movepool: [ { name: "Basic Attack", type: AttackType.BEAST, power: 50, accuracy: 100, pp: 20, priority: 0, flags: ['contact'], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }] } ] }; triggerPiclet = { name: "Trigger Test", description: "Tests all trigger events", tier: 'medium', primaryType: PicletType.CULTURE, baseStats: { hp: 100, attack: 70, defense: 70, speed: 50 }, nature: "Bold", specialAbility: { name: "Multi Trigger", description: "Triggers on various events", triggers: [] }, movepool: [ { name: "Drain Punch", type: AttackType.BEAST, power: 60, accuracy: 100, pp: 15, priority: 0, flags: ['contact'], effects: [ { type: 'damage', target: 'opponent', amount: 'normal', formula: 'drain', value: 0.5 } ] }, { name: "Status Move", type: AttackType.CULTURE, power: 0, accuracy: 100, pp: 20, priority: 0, flags: [], effects: [ { type: 'applyStatus', target: 'opponent', status: 'paralyze', chance: 100 } ] }, { name: "Stat Boost", type: AttackType.CULTURE, power: 0, accuracy: 100, pp: 20, priority: 0, flags: [], effects: [ { type: 'modifyStats', target: 'self', stats: { attack: 'increase' } } ] }, { name: "Heal Move", type: AttackType.CULTURE, power: 0, accuracy: 100, pp: 15, priority: 0, flags: [], effects: [ { type: 'heal', target: 'self', amount: 'large' } ] } ] }; }); describe('onStatusInflicted Trigger', () => { it('should trigger when status effect is applied', () => { const statusTriggerPiclet = { ...triggerPiclet, specialAbility: { name: "Status Aware", description: "Triggers when status is inflicted", triggers: [ { event: 'onStatusInflicted', condition: 'always', effects: [ { type: 'modifyStats', target: 'self', stats: { attack: 'increase' } } ] } ] } }; const engine = new BattleEngine(basicPiclet, statusTriggerPiclet); const initialAttack = engine.getState().opponentPiclet.attack; // Use status move to trigger the ability engine.executeActions( { type: 'move', piclet: 'player', moveIndex: 0 }, { type: 'move', piclet: 'opponent', moveIndex: 1 } // Status Move ); const finalAttack = engine.getState().opponentPiclet.attack; const log = engine.getLog(); console.log('Status inflicted test log:', log); console.log('Initial attack:', initialAttack, 'Final attack:', finalAttack); expect(finalAttack).toBeGreaterThan(initialAttack); expect(log.some(msg => msg.includes('Status Aware') && msg.includes('triggered'))).toBe(true); }); }); describe('onHPDrained Trigger', () => { it('should trigger when HP is drained from opponent', () => { const drainTriggerPiclet = { ...triggerPiclet, specialAbility: { name: "Life Stealer", description: "Triggers when draining HP", triggers: [ { event: 'onHPDrained', condition: 'always', effects: [ { type: 'modifyStats', target: 'self', stats: { speed: 'increase' } } ] } ] } }; const engine = new BattleEngine(basicPiclet, drainTriggerPiclet); const initialSpeed = engine.getState().opponentPiclet.speed; // Use drain move to trigger the ability engine.executeActions( { type: 'move', piclet: 'player', moveIndex: 0 }, { type: 'move', piclet: 'opponent', moveIndex: 0 } // Drain Punch ); const finalSpeed = engine.getState().opponentPiclet.speed; const log = engine.getLog(); expect(finalSpeed).toBeGreaterThan(initialSpeed); expect(log.some(msg => msg.includes('Life Stealer') && msg.includes('triggered'))).toBe(true); }); }); describe('beforeMoveUse/afterMoveUse Triggers', () => { it('should trigger before and after move use', () => { const moveTriggerPiclet = { ...triggerPiclet, specialAbility: { name: "Move Monitor", description: "Triggers before and after moves", triggers: [ { event: 'beforeMoveUse', condition: 'always', effects: [ { type: 'modifyStats', target: 'self', stats: { defense: 'increase' } } ] }, { event: 'afterMoveUse', condition: 'always', effects: [ { type: 'modifyStats', target: 'self', stats: { accuracy: 'increase' } } ] } ] } }; const engine = new BattleEngine(basicPiclet, moveTriggerPiclet); const initialDefense = engine.getState().opponentPiclet.defense; const initialAccuracy = engine.getState().opponentPiclet.accuracy; engine.executeActions( { type: 'move', piclet: 'player', moveIndex: 0 }, { type: 'move', piclet: 'opponent', moveIndex: 0 } ); const finalDefense = engine.getState().opponentPiclet.defense; const finalAccuracy = engine.getState().opponentPiclet.accuracy; const log = engine.getLog(); expect(finalDefense).toBeGreaterThan(initialDefense); expect(finalAccuracy).toBeGreaterThan(initialAccuracy); expect(log.some(msg => msg.includes('Move Monitor') && msg.includes('triggered'))).toBe(true); }); }); describe('onFullHP Trigger', () => { it('should trigger when HP reaches maximum', () => { const fullHpTriggerPiclet = { ...triggerPiclet, specialAbility: { name: "Full Power", description: "Triggers when at full HP", triggers: [ { event: 'onFullHP', condition: 'always', effects: [ { type: 'modifyStats', target: 'self', stats: { attack: 'greatly_increase' } } ] } ] } }; const engine = new BattleEngine(basicPiclet, fullHpTriggerPiclet); // Damage the piclet first engine['state'].opponentPiclet.currentHp = Math.floor(engine['state'].opponentPiclet.maxHp * 0.5); const initialAttack = engine.getState().opponentPiclet.attack; // Use heal move to reach full HP engine.executeActions( { type: 'move', piclet: 'player', moveIndex: 0 }, { type: 'move', piclet: 'opponent', moveIndex: 3 } // Heal Move ); const finalAttack = engine.getState().opponentPiclet.attack; const log = engine.getLog(); // Should trigger if healed to full HP if (engine.getState().opponentPiclet.currentHp === engine.getState().opponentPiclet.maxHp) { expect(finalAttack).toBeGreaterThan(initialAttack); expect(log.some(msg => msg.includes('Full Power') && msg.includes('triggered'))).toBe(true); } }); }); describe('onOpponentContactMove Trigger', () => { it('should trigger when opponent uses contact move', () => { const contactTriggerPiclet = { ...triggerPiclet, specialAbility: { name: "Rough Skin", description: "Triggers when hit by contact moves", triggers: [ { event: 'onOpponentContactMove', condition: 'always', effects: [ { type: 'damage', target: 'opponent', amount: 'weak' } ] } ] } }; const engine = new BattleEngine(basicPiclet, contactTriggerPiclet); const initialPlayerHp = engine.getState().playerPiclet.currentHp; // Player uses contact move engine.executeActions( { type: 'move', piclet: 'player', moveIndex: 0 }, // Basic Attack (contact) { type: 'move', piclet: 'opponent', moveIndex: 0 } ); const finalPlayerHp = engine.getState().playerPiclet.currentHp; const log = engine.getLog(); // Player should take damage from contact expect(finalPlayerHp).toBeLessThan(initialPlayerHp); expect(log.some(msg => msg.includes('Rough Skin') && msg.includes('triggered'))).toBe(true); }); }); describe('onStatChange Trigger', () => { it('should trigger when stats are modified', () => { const statTriggerPiclet = { ...triggerPiclet, specialAbility: { name: "Stat Monitor", description: "Triggers when stats change", triggers: [ { event: 'onStatChange', condition: 'always', effects: [ { type: 'heal', target: 'self', amount: 'small' } ] } ] } }; const engine = new BattleEngine(basicPiclet, statTriggerPiclet); // Damage the piclet first so healing is visible engine['state'].opponentPiclet.currentHp = Math.floor(engine['state'].opponentPiclet.maxHp * 0.8); const initialHp = engine.getState().opponentPiclet.currentHp; // Use stat boost move to trigger the ability engine.executeActions( { type: 'move', piclet: 'player', moveIndex: 0 }, { type: 'move', piclet: 'opponent', moveIndex: 2 } // Stat Boost ); const finalHp = engine.getState().opponentPiclet.currentHp; const log = engine.getLog(); expect(finalHp).toBeGreaterThan(initialHp); expect(log.some(msg => msg.includes('Stat Monitor') && msg.includes('triggered'))).toBe(true); }); }); });