piclets / src /lib /battle-engine /tempest-wraith.test.ts
Fraser's picture
simple counters
1bb845a
/**
* Test for the complete Tempest Wraith example from the design document
* This demonstrates all advanced features working together
*/
import { describe, it, expect } from 'vitest';
import { BattleEngine } from './BattleEngine';
import { PicletDefinition } from './types';
import { PicletType, AttackType } from './types';
describe('Complete Tempest Wraith Implementation', () => {
it('should handle the complete Tempest Wraith from design document', () => {
const tempestWraith: PicletDefinition = {
name: "Tempest Wraith",
description: "A ghostly creature born from violent storms, wielding cosmic energy and shadowy illusions",
tier: 'high',
primaryType: PicletType.SPACE,
secondaryType: PicletType.CULTURE,
baseStats: {
hp: 75,
attack: 95,
defense: 45,
speed: 85
},
nature: "timid",
specialAbility: {
name: "Storm Caller",
description: "When HP drops below 25%, gains immunity to status effects and +50% speed",
triggers: [
{
event: 'onLowHP',
effects: [
{
type: 'mechanicOverride',
mechanic: 'statusImmunity',
value: ['burn', 'freeze', 'paralyze', 'poison', 'sleep', 'confuse']
},
{
type: 'modifyStats',
target: 'self',
stats: { speed: 'greatly_increase' }
}
]
},
{
event: 'onSwitchIn',
condition: 'ifWeather:storm',
effects: [
{
type: 'modifyStats',
target: 'self',
stats: { attack: 'increase' }
}
]
}
]
},
movepool: [
{
name: "Shadow Pulse",
type: AttackType.CULTURE,
power: 70,
accuracy: 100,
pp: 15,
priority: 0,
flags: [],
effects: [
{
type: 'damage',
target: 'opponent',
amount: 'normal'
},
{
type: 'applyStatus',
target: 'opponent',
status: 'confuse'
}
]
},
{
name: "Cosmic Strike",
type: AttackType.SPACE,
power: 85,
accuracy: 90,
pp: 10,
priority: 0,
flags: [],
effects: [
{
type: 'damage',
target: 'opponent',
amount: 'normal'
},
{
type: 'applyStatus',
target: 'opponent',
status: 'paralyze'
}
]
},
{
name: "Spectral Drain",
type: AttackType.CULTURE,
power: 60,
accuracy: 95,
pp: 12,
priority: 0,
flags: ['draining'],
effects: [
{
type: 'damage',
target: 'opponent',
formula: 'drain',
value: 0.5
}
]
},
{
name: "Void Sacrifice",
type: AttackType.SPACE,
power: 130,
accuracy: 85,
pp: 1,
priority: 0,
flags: ['sacrifice', 'explosive'],
effects: [
{
type: 'damage',
target: 'all',
formula: 'standard',
multiplier: 1.2
},
{
type: 'damage',
target: 'self',
formula: 'percentage',
value: 75
},
{
type: 'fieldEffect',
effect: 'voidStorm',
target: 'field',
stackable: false
}
]
}
]
};
const opponent: PicletDefinition = {
name: "Standard Fighter",
description: "A basic opponent",
tier: 'medium',
primaryType: PicletType.BEAST,
baseStats: { hp: 100, attack: 80, defense: 70, speed: 60 },
nature: "Hardy",
specialAbility: { name: "None", description: "No ability" },
movepool: [{
name: "Tackle", type: AttackType.NORMAL, power: 40, accuracy: 100, pp: 35,
priority: 0, flags: [], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }]
}]
};
const engine = new BattleEngine(tempestWraith, opponent);
// Test 1: Verify Tempest Wraith is properly initialized
const state = engine.getState();
expect(state.playerPiclet.definition.name).toBe("Tempest Wraith");
expect(state.playerPiclet.definition.primaryType).toBe(PicletType.SPACE);
expect(state.playerPiclet.definition.secondaryType).toBe(PicletType.CULTURE);
expect(state.playerPiclet.moves).toHaveLength(4);
// Test 2: Verify special ability structure
const ability = tempestWraith.specialAbility;
expect(ability.name).toBe("Storm Caller");
expect(ability.triggers).toHaveLength(2);
expect(ability.triggers![0].event).toBe('onLowHP');
expect(ability.triggers![1].event).toBe('onSwitchIn');
// Test 3: Test Shadow Pulse (dual effect move)
engine.executeActions(
{ type: 'move', piclet: 'player', moveIndex: 0 }, // Shadow Pulse
{ type: 'move', piclet: 'opponent', moveIndex: 0 }
);
let log = engine.getLog();
expect(log.some(msg => msg.includes('used Shadow Pulse'))).toBe(true);
// Test 4: Test Spectral Drain (drain move)
if (!engine.isGameOver()) {
// Damage the player to test healing
engine['state'].playerPiclet.currentHp = 30;
engine.executeActions(
{ type: 'move', piclet: 'player', moveIndex: 2 }, // Spectral Drain
{ type: 'move', piclet: 'opponent', moveIndex: 0 }
);
log = engine.getLog();
expect(log.some(msg => msg.includes('recovered') && msg.includes('HP from draining'))).toBe(true);
}
// Test 5: Test Void Sacrifice (ultimate move)
if (!engine.isGameOver()) {
const preVoidHp = engine.getState().playerPiclet.currentHp;
engine.executeActions(
{ type: 'move', piclet: 'player', moveIndex: 3 }, // Void Sacrifice
{ type: 'move', piclet: 'opponent', moveIndex: 0 }
);
log = engine.getLog();
expect(log.some(msg => msg.includes('used Void Sacrifice'))).toBe(true);
// Just verify that some field effect message exists
const hasFieldEffect = log.some(msg => msg.includes('applied') || msg.includes('effect'));
expect(hasFieldEffect).toBe(true);
// Should have taken massive self-damage
const postVoidHp = engine.getState().playerPiclet.currentHp;
expect(postVoidHp).toBeLessThan(preVoidHp);
}
});
it('should demonstrate strategic depth with different movesets', () => {
const tempestWraith: PicletDefinition = {
name: "Tempest Wraith",
description: "A ghostly creature born from violent storms",
tier: 'high',
primaryType: PicletType.SPACE,
secondaryType: PicletType.CULTURE,
baseStats: { hp: 75, attack: 95, defense: 45, speed: 85 },
nature: "timid",
specialAbility: {
name: "Storm Caller",
description: "Complex multi-trigger ability",
triggers: [
{
event: 'onLowHP',
effects: [
{
type: 'mechanicOverride',
mechanic: 'statusImmunity',
value: ['burn', 'freeze', 'paralyze', 'poison', 'sleep', 'confuse']
}
]
}
]
},
movepool: [
{
name: "Berserker's End",
type: AttackType.BEAST,
power: 80,
accuracy: 95,
pp: 10,
priority: 0,
flags: ['contact', 'reckless'],
effects: [
{
type: 'damage',
target: 'opponent',
amount: 'normal'
},
{
type: 'damage',
target: 'opponent',
amount: 'strong',
condition: 'ifLowHp'
},
{
type: 'mechanicOverride',
target: 'self',
mechanic: 'healingBlocked',
value: true
}
]
},
{
name: "Cursed Gambit",
type: AttackType.CULTURE,
power: 0,
accuracy: 100,
pp: 1,
priority: 0,
flags: ['gambling'],
effects: [
{
type: 'heal',
target: 'self',
formula: 'percentage',
value: 100,
condition: 'ifLucky50'
},
{
type: 'damage',
target: 'self',
formula: 'fixed',
value: 9999,
condition: 'ifUnlucky50'
}
]
}
]
};
const engine = new BattleEngine(tempestWraith, {
name: "Opponent", description: "Test opponent", tier: 'medium',
primaryType: PicletType.BEAST, baseStats: { hp: 100, attack: 80, defense: 70, speed: 60 },
nature: "Hardy", specialAbility: { name: "None", description: "No ability" },
movepool: [{ name: "Tackle", type: AttackType.NORMAL, power: 40, accuracy: 100, pp: 35,
priority: 0, flags: [], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }] }]
});
// Test the moveset variety
const state = engine.getState();
expect(state.playerPiclet.moves[0].move.name).toBe("Berserker's End");
expect(state.playerPiclet.moves[1].move.name).toBe("Cursed Gambit");
// Test Berserker's End effects
const berserkersEnd = state.playerPiclet.moves[0].move;
expect(berserkersEnd.effects).toHaveLength(3);
expect(berserkersEnd.effects[1].condition).toBe('ifLowHp');
// Test Cursed Gambit effects
const cursedGambit = state.playerPiclet.moves[1].move;
expect(cursedGambit.effects).toHaveLength(2);
expect(cursedGambit.effects[0].condition).toBe('ifLucky50');
expect(cursedGambit.effects[1].condition).toBe('ifUnlucky50');
});
it('should handle complete battle with advanced mechanics', () => {
const advancedPiclet: PicletDefinition = {
name: "Master of All Trades",
description: "Demonstrates every major battle system feature",
tier: 'legendary',
primaryType: PicletType.SPACE,
secondaryType: PicletType.CULTURE,
baseStats: { hp: 100, attack: 100, defense: 80, speed: 90 },
nature: "Adaptive",
specialAbility: {
name: "Omni-Adaptation",
description: "Multiple triggers for different situations",
effects: [
{
type: 'mechanicOverride',
mechanic: 'criticalHits',
value: 'double'
}
],
triggers: [
{
event: 'onDamageTaken',
effects: [
{
type: 'modifyStats',
target: 'self',
stats: { attack: 'increase' }
}
]
},
{
event: 'onSwitchIn',
effects: [
{
type: 'removeStatus',
target: 'self',
status: 'poison'
}
]
},
{
event: 'endOfTurn',
condition: 'ifStatus:burn',
effects: [
{
type: 'heal',
target: 'self',
formula: 'percentage',
value: 10
}
]
}
]
},
movepool: [
{
name: "Adaptive Strike",
type: AttackType.NORMAL,
power: 70,
accuracy: 100,
pp: 20,
priority: 0,
flags: ['contact'],
effects: [
{
type: 'damage',
target: 'opponent',
amount: 'normal'
},
{
type: 'damage',
target: 'opponent',
amount: 'strong',
condition: 'ifStatus:burn'
}
]
},
{
name: "Field Manipulator",
type: AttackType.SPACE,
power: 0,
accuracy: 100,
pp: 10,
priority: 1,
flags: ['priority'],
effects: [
{
type: 'fieldEffect',
effect: 'gravityField',
target: 'field',
stackable: false
},
{
type: 'modifyStats',
target: 'opponent',
stats: { speed: 'decrease' }
}
]
},
{
name: "Status Cleanse",
type: AttackType.NORMAL,
power: 0,
accuracy: 100,
pp: 15,
priority: 0,
flags: [],
effects: [
{
type: 'removeStatus',
target: 'self',
status: 'poison'
},
{
type: 'removeStatus',
target: 'self',
status: 'burn'
},
{
type: 'heal',
target: 'self',
amount: 'medium'
}
]
},
{
name: "Counter Protocol",
type: AttackType.NORMAL,
power: 0,
accuracy: 100,
pp: 10,
priority: -5,
flags: ['lowPriority'],
effects: [
{
type: 'counter',
strength: 'strong'
}
]
}
]
};
const engine = new BattleEngine(advancedPiclet, {
name: "Test Opponent", description: "Basic opponent", tier: 'medium',
primaryType: PicletType.BEAST, baseStats: { hp: 80, attack: 70, defense: 60, speed: 50 },
nature: "Hardy", specialAbility: { name: "None", description: "No ability" },
movepool: [{ name: "Tackle", type: AttackType.NORMAL, power: 40, accuracy: 100, pp: 35,
priority: 0, flags: [], effects: [{ type: 'damage', target: 'opponent', amount: 'normal' }] }]
});
// Test complex special ability
const ability = advancedPiclet.specialAbility;
expect(ability.effects).toHaveLength(1);
expect(ability.triggers).toHaveLength(3);
expect(ability.triggers![0].event).toBe('onDamageTaken');
expect(ability.triggers![2].condition).toBe('ifStatus:burn');
// Test diverse moveset
const moves = advancedPiclet.movepool;
expect(moves).toHaveLength(4);
expect(moves[1].priority).toBe(1); // Priority move
expect(moves[3].priority).toBe(-5); // Low priority counter
expect(moves[2].effects).toHaveLength(3); // Multi-effect move
// Test battle execution
engine.executeActions(
{ type: 'move', piclet: 'player', moveIndex: 1 }, // Field Manipulator
{ type: 'move', piclet: 'opponent', moveIndex: 0 }
);
const log = engine.getLog();
const hasFieldEffect = log.some(msg => msg.includes('applied') || msg.includes('effect'));
expect(hasFieldEffect).toBe(true);
expect(log.some(msg => msg.includes('speed fell'))).toBe(true);
});
});