piclets / src /lib /battle-engine /mechanic-overrides.test.ts
Fraser's picture
add battle engine
1ecc382
/**
* 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');
});
});
});