@robsonbittencourt/calc 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +2 -0
- package/.eslintrc +12 -0
- package/bundle +107 -0
- package/dist/adaptable.d.ts +6 -0
- package/dist/adaptable.js +28 -0
- package/dist/adaptable.js.map +1 -0
- package/dist/calc.d.ts +6 -0
- package/dist/calc.js +26 -0
- package/dist/calc.js.map +1 -0
- package/dist/data/abilities.d.ts +15 -0
- package/dist/data/abilities.js +448 -0
- package/dist/data/abilities.js.map +1 -0
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.js +30 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/interface.d.ts +150 -0
- package/dist/data/interface.js +3 -0
- package/dist/data/interface.js.map +1 -0
- package/dist/data/items.d.ts +24 -0
- package/dist/data/items.js +708 -0
- package/dist/data/items.js.map +1 -0
- package/dist/data/moves.d.ts +86 -0
- package/dist/data/moves.js +5014 -0
- package/dist/data/moves.js.map +1 -0
- package/dist/data/natures.d.ts +17 -0
- package/dist/data/natures.js +127 -0
- package/dist/data/natures.js.map +1 -0
- package/dist/data/production.min.js +1 -0
- package/dist/data/species.d.ts +48 -0
- package/dist/data/species.js +10126 -0
- package/dist/data/species.js.map +1 -0
- package/dist/data/types.d.ts +23 -0
- package/dist/data/types.js +538 -0
- package/dist/data/types.js.map +1 -0
- package/dist/desc.d.ts +65 -0
- package/dist/desc.js +866 -0
- package/dist/desc.js.map +1 -0
- package/dist/field.d.ts +49 -0
- package/dist/field.js +111 -0
- package/dist/field.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/items.d.ts +13 -0
- package/dist/items.js +434 -0
- package/dist/items.js.map +1 -0
- package/dist/mechanics/gen12.d.ts +6 -0
- package/dist/mechanics/gen12.js +271 -0
- package/dist/mechanics/gen12.js.map +1 -0
- package/dist/mechanics/gen3.d.ts +11 -0
- package/dist/mechanics/gen3.js +371 -0
- package/dist/mechanics/gen3.js.map +1 -0
- package/dist/mechanics/gen4.d.ts +11 -0
- package/dist/mechanics/gen4.js +596 -0
- package/dist/mechanics/gen4.js.map +1 -0
- package/dist/mechanics/gen56.d.ts +13 -0
- package/dist/mechanics/gen56.js +836 -0
- package/dist/mechanics/gen56.js.map +1 -0
- package/dist/mechanics/gen789.d.ts +14 -0
- package/dist/mechanics/gen789.js +1325 -0
- package/dist/mechanics/gen789.js.map +1 -0
- package/dist/mechanics/util.d.ts +39 -0
- package/dist/mechanics/util.js +675 -0
- package/dist/mechanics/util.js.map +1 -0
- package/dist/move.d.ts +50 -0
- package/dist/move.js +324 -0
- package/dist/move.js.map +1 -0
- package/dist/pokemon.d.ts +55 -0
- package/dist/pokemon.js +240 -0
- package/dist/pokemon.js.map +1 -0
- package/dist/production.min.js +1 -0
- package/dist/result.d.ts +34 -0
- package/dist/result.js +94 -0
- package/dist/result.js.map +1 -0
- package/dist/state.d.ts +77 -0
- package/dist/state.js +3 -0
- package/dist/state.js.map +1 -0
- package/dist/stats.d.ts +26 -0
- package/dist/stats.js +183 -0
- package/dist/stats.js.map +1 -0
- package/dist/test/calc.test.d.ts +1 -0
- package/dist/test/calc.test.js +1297 -0
- package/dist/test/calc.test.js.map +1 -0
- package/dist/test/data.test.d.ts +1 -0
- package/dist/test/data.test.js +368 -0
- package/dist/test/data.test.js.map +1 -0
- package/dist/test/gen.d.ts +135 -0
- package/dist/test/gen.js +636 -0
- package/dist/test/gen.js.map +1 -0
- package/dist/test/helper.d.ts +55 -0
- package/dist/test/helper.js +174 -0
- package/dist/test/helper.js.map +1 -0
- package/dist/test/move.test.d.ts +1 -0
- package/dist/test/move.test.js +14 -0
- package/dist/test/move.test.js.map +1 -0
- package/dist/test/pokemon.test.d.ts +1 -0
- package/dist/test/pokemon.test.js +102 -0
- package/dist/test/pokemon.test.js.map +1 -0
- package/dist/test/stats.test.d.ts +1 -0
- package/dist/test/stats.test.js +64 -0
- package/dist/test/stats.test.js.map +1 -0
- package/dist/test/utils.test.d.ts +1 -0
- package/dist/test/utils.test.js +19 -0
- package/dist/test/utils.test.js.map +1 -0
- package/dist/util.d.ts +17 -0
- package/dist/util.js +115 -0
- package/dist/util.js.map +1 -0
- package/jest.config.js +11 -0
- package/package.json +40 -0
- package/src/adaptable.ts +12 -0
- package/src/calc.ts +40 -0
- package/src/data/abilities.ts +383 -0
- package/src/data/index.ts +36 -0
- package/src/data/interface.ts +176 -0
- package/src/data/items.ts +632 -0
- package/src/data/moves.ts +5028 -0
- package/src/data/natures.ts +65 -0
- package/src/data/species.ts +10098 -0
- package/src/data/types.ts +478 -0
- package/src/desc.ts +1063 -0
- package/src/field.ts +124 -0
- package/src/index.ts +156 -0
- package/src/items.ts +423 -0
- package/src/mechanics/gen12.ts +297 -0
- package/src/mechanics/gen3.ts +444 -0
- package/src/mechanics/gen4.ts +702 -0
- package/src/mechanics/gen56.ts +1134 -0
- package/src/mechanics/gen789.ts +1788 -0
- package/src/mechanics/util.ts +676 -0
- package/src/move.ts +337 -0
- package/src/pokemon.ts +244 -0
- package/src/result.ts +106 -0
- package/src/state.ts +81 -0
- package/src/stats.ts +213 -0
- package/src/test/calc.test.ts +1588 -0
- package/src/test/data.test.ts +129 -0
- package/src/test/gen.ts +514 -0
- package/src/test/helper.ts +185 -0
- package/src/test/move.test.ts +13 -0
- package/src/test/pokemon.test.ts +121 -0
- package/src/test/stats.test.ts +84 -0
- package/src/test/utils.test.ts +18 -0
- package/src/util.ts +153 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/* eslint-env jest */
|
|
2
|
+
|
|
3
|
+
import type * as I from '../data/interface';
|
|
4
|
+
import {type Result, calculate, Pokemon, Move} from '../index';
|
|
5
|
+
import type {State} from '../state';
|
|
6
|
+
import {Field, Side} from '../field';
|
|
7
|
+
|
|
8
|
+
const calc = (gen: I.GenerationNum) => (
|
|
9
|
+
attacker: Pokemon,
|
|
10
|
+
defender: Pokemon,
|
|
11
|
+
move: Move,
|
|
12
|
+
field?: Field
|
|
13
|
+
) => calculate(gen, attacker, defender, move, field);
|
|
14
|
+
|
|
15
|
+
const move = (gen: I.GenerationNum) => (
|
|
16
|
+
name: string,
|
|
17
|
+
options: Partial<Omit<State.Move, 'ability' | 'item' | 'species'>> & {
|
|
18
|
+
ability?: string;
|
|
19
|
+
item?: string;
|
|
20
|
+
species?: string;
|
|
21
|
+
} = {}
|
|
22
|
+
) => new Move(gen, name, options as any);
|
|
23
|
+
|
|
24
|
+
const pokemon = (gen: I.GenerationNum) => (
|
|
25
|
+
name: string,
|
|
26
|
+
options: Partial<Omit<State.Pokemon, 'ability' | 'item' | 'nature' | 'moves'>> & {
|
|
27
|
+
ability?: string;
|
|
28
|
+
item?: string;
|
|
29
|
+
nature?: string;
|
|
30
|
+
moves?: string[];
|
|
31
|
+
curHP?: number;
|
|
32
|
+
ivs?: Partial<I.StatsTable> & {spc?: number};
|
|
33
|
+
evs?: Partial<I.StatsTable> & {spc?: number};
|
|
34
|
+
boosts?: Partial<I.StatsTable> & {spc?: number};
|
|
35
|
+
} = {}
|
|
36
|
+
) => new Pokemon(gen, name, options as any);
|
|
37
|
+
|
|
38
|
+
const field = (field: Partial<State.Field> = {}) => new Field(field);
|
|
39
|
+
|
|
40
|
+
const side = (side: State.Side = {}) => new Side(side);
|
|
41
|
+
|
|
42
|
+
interface Gen {
|
|
43
|
+
gen: I.GenerationNum;
|
|
44
|
+
calculate: ReturnType<typeof calc>;
|
|
45
|
+
Pokemon: ReturnType<typeof pokemon>;
|
|
46
|
+
Move: ReturnType<typeof move>;
|
|
47
|
+
Field: typeof field;
|
|
48
|
+
Side: typeof side;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function inGen(gen: I.GenerationNum, fn: (gen: Gen) => void) {
|
|
52
|
+
fn({
|
|
53
|
+
gen,
|
|
54
|
+
calculate: calc(gen),
|
|
55
|
+
Move: move(gen),
|
|
56
|
+
Pokemon: pokemon(gen),
|
|
57
|
+
Field: field,
|
|
58
|
+
Side: side,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function inGens(from: I.GenerationNum, to: I.GenerationNum, fn: (gen: Gen) => void) {
|
|
63
|
+
for (let gen = from; gen <= to; gen++) {
|
|
64
|
+
inGen(gen, fn);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function tests(name: string, fn: (gen: Gen) => void, type?: 'skip' | 'only'): void;
|
|
69
|
+
export function tests(
|
|
70
|
+
name: string,
|
|
71
|
+
from: I.GenerationNum,
|
|
72
|
+
fn: (gen: Gen) => void,
|
|
73
|
+
type?: 'skip' | 'only'
|
|
74
|
+
): void;
|
|
75
|
+
export function tests(
|
|
76
|
+
name: string,
|
|
77
|
+
from: I.GenerationNum,
|
|
78
|
+
to: I.GenerationNum,
|
|
79
|
+
fn: (gen: Gen) => void,
|
|
80
|
+
type?: 'skip' | 'only'
|
|
81
|
+
): void;
|
|
82
|
+
export function tests(...args: any[]) {
|
|
83
|
+
const name = args[0];
|
|
84
|
+
let from: I.GenerationNum;
|
|
85
|
+
let to: I.GenerationNum;
|
|
86
|
+
let fn: (gen: Gen) => void;
|
|
87
|
+
let type: 'skip' | 'only' | undefined = undefined;
|
|
88
|
+
if (typeof args[1] !== 'number') {
|
|
89
|
+
from = 1;
|
|
90
|
+
to = 9;
|
|
91
|
+
fn = args[1];
|
|
92
|
+
type = args[2];
|
|
93
|
+
} else if (typeof args[2] !== 'number') {
|
|
94
|
+
from = args[1] as I.GenerationNum ?? 1;
|
|
95
|
+
to = 9;
|
|
96
|
+
fn = args[2];
|
|
97
|
+
type = args[3];
|
|
98
|
+
} else {
|
|
99
|
+
from = args[1] as I.GenerationNum ?? 1;
|
|
100
|
+
to = args[2] as I.GenerationNum ?? 8;
|
|
101
|
+
fn = args[3];
|
|
102
|
+
type = args[4];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
inGens(from, to, gen => {
|
|
106
|
+
const n = `${name} (gen ${gen.gen})`;
|
|
107
|
+
if (type === 'skip') {
|
|
108
|
+
test.skip(n, () => fn!(gen));
|
|
109
|
+
} else if (type === 'only') {
|
|
110
|
+
test.only(n, () => fn!(gen));
|
|
111
|
+
} else {
|
|
112
|
+
test(n, () => fn!(gen));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
declare global {
|
|
118
|
+
namespace jest {
|
|
119
|
+
interface Matchers<R, T> {
|
|
120
|
+
toMatch(gen: I.GenerationNum, notation?: '%' | 'px' | ResultDiff, diff?: ResultDiff): R;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type ResultDiff = Partial<Record<I.GenerationNum, Partial<ResultBreakdown>>>;
|
|
126
|
+
interface ResultBreakdown {
|
|
127
|
+
range: [number, number];
|
|
128
|
+
desc: string;
|
|
129
|
+
result: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
expect.extend({
|
|
133
|
+
toMatch(
|
|
134
|
+
received: Result,
|
|
135
|
+
gen: I.GenerationNum,
|
|
136
|
+
notation?: '%' | 'px' | ResultDiff,
|
|
137
|
+
diff?: ResultDiff
|
|
138
|
+
) {
|
|
139
|
+
if (typeof notation !== 'string') {
|
|
140
|
+
diff = notation;
|
|
141
|
+
notation = '%';
|
|
142
|
+
}
|
|
143
|
+
if (!diff) throw new Error('toMatch called with no diff!');
|
|
144
|
+
|
|
145
|
+
const breakdowns = Object.entries(diff).sort() as [string, ResultBreakdown][];
|
|
146
|
+
const expected = {range: undefined! as [number, number], desc: '', result: ''};
|
|
147
|
+
for (const [g, {range, desc, result}] of breakdowns) {
|
|
148
|
+
if (Number(g) > gen) break;
|
|
149
|
+
if (range) expected.range = range;
|
|
150
|
+
if (desc) expected.desc = desc;
|
|
151
|
+
if (result) expected.result = result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!(expected.range || expected.desc || expected.result)) {
|
|
155
|
+
throw new Error(`toMatch called with empty diff: ${diff}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (expected.range) {
|
|
159
|
+
if (this.isNot) {
|
|
160
|
+
expect(received.range()).not.toEqual(expected.range);
|
|
161
|
+
} else {
|
|
162
|
+
expect(received.range()).toEqual(expected.range);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (expected.desc) {
|
|
166
|
+
const r = received.fullDesc(notation).split(': ')[0];
|
|
167
|
+
if (this.isNot) {
|
|
168
|
+
expect(r).not.toEqual(expected.desc);
|
|
169
|
+
} else {
|
|
170
|
+
expect(r).toEqual(expected.desc);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (expected.result) {
|
|
174
|
+
const post = received.fullDesc(notation).split(': ')[1];
|
|
175
|
+
const r = `(${post.split('(')[1]}`;
|
|
176
|
+
if (this.isNot) {
|
|
177
|
+
expect(r).not.toEqual(expected.result);
|
|
178
|
+
} else {
|
|
179
|
+
expect(r).toEqual(expected.result);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {pass: !this.isNot, message: () => ''};
|
|
184
|
+
},
|
|
185
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {Move} from '../index';
|
|
2
|
+
|
|
3
|
+
describe('Move', () => {
|
|
4
|
+
test('clone', () => {
|
|
5
|
+
const m = new Move(7, 'Blizzard', {useZ: true});
|
|
6
|
+
expect(m.name).toBe('Subzero Slammer');
|
|
7
|
+
expect(m.bp).toBe(185);
|
|
8
|
+
|
|
9
|
+
const clone = m.clone();
|
|
10
|
+
expect(clone.name).toBe('Subzero Slammer');
|
|
11
|
+
expect(clone.bp).toBe(185);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {Pokemon} from '../index';
|
|
2
|
+
import type {StatID, MoveName} from '../data/interface';
|
|
3
|
+
|
|
4
|
+
describe('Pokemon', () => {
|
|
5
|
+
test('defaults', () => {
|
|
6
|
+
const p = new Pokemon(7, 'Gengar');
|
|
7
|
+
|
|
8
|
+
expect(p.name).toBe('Gengar');
|
|
9
|
+
expect(p.types).toEqual(['Ghost', 'Poison']);
|
|
10
|
+
expect(p.weightkg).toBe(40.5);
|
|
11
|
+
expect(p.level).toBe(100);
|
|
12
|
+
expect(p.gender).toBe('M');
|
|
13
|
+
expect(p.item).toBeUndefined();
|
|
14
|
+
expect(p.ability).toBe('Cursed Body');
|
|
15
|
+
expect(p.nature).toBe('Serious');
|
|
16
|
+
expect(p.status).toBe('');
|
|
17
|
+
expect(p.hasStatus()).toBe(false);
|
|
18
|
+
expect(p.toxicCounter).toBe(0);
|
|
19
|
+
expect(p.curHP()).toBe(p.stats.hp);
|
|
20
|
+
|
|
21
|
+
let stat: StatID;
|
|
22
|
+
// tslint:disable-next-line:forin
|
|
23
|
+
for (stat in p.ivs) {
|
|
24
|
+
expect(p.ivs[stat]).toBe(31);
|
|
25
|
+
expect(p.evs[stat]).toBe(0);
|
|
26
|
+
expect(p.boosts[stat]).toBe(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
expect(p.stats).toEqual({hp: 261, atk: 166, def: 156, spa: 296, spd: 186, spe: 256});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Pokemon full', () => {
|
|
33
|
+
const p = new Pokemon(7, 'Suicune', {
|
|
34
|
+
level: 50,
|
|
35
|
+
ability: 'Inner Focus',
|
|
36
|
+
item: 'Leftovers',
|
|
37
|
+
nature: 'Bold',
|
|
38
|
+
ivs: {spa: 30},
|
|
39
|
+
evs: {spd: 4, def: 252, hp: 252},
|
|
40
|
+
boosts: {atk: -1, spa: 2, spd: 1},
|
|
41
|
+
curHP: 60,
|
|
42
|
+
status: 'tox',
|
|
43
|
+
toxicCounter: 2,
|
|
44
|
+
moves: ['Surf', 'Rest', 'Curse', 'Sleep Talk'],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(p.name).toBe('Suicune');
|
|
48
|
+
expect(p.types).toEqual(['Water']);
|
|
49
|
+
expect(p.weightkg).toBe(187.0);
|
|
50
|
+
expect(p.level).toBe(50);
|
|
51
|
+
expect(p.gender).toBe('N');
|
|
52
|
+
expect(p.item).toBe('Leftovers');
|
|
53
|
+
expect(p.ability).toBe('Inner Focus');
|
|
54
|
+
expect(p.nature).toBe('Bold');
|
|
55
|
+
expect(p.status).toBe('tox');
|
|
56
|
+
expect(p.toxicCounter).toBe(2);
|
|
57
|
+
expect(p.curHP()).toBe(60);
|
|
58
|
+
|
|
59
|
+
expect(p.ivs).toEqual({hp: 31, atk: 31, def: 31, spa: 30, spd: 31, spe: 31});
|
|
60
|
+
expect(p.evs).toEqual({hp: 252, atk: 0, def: 252, spa: 0, spd: 4, spe: 0});
|
|
61
|
+
expect(p.boosts).toEqual({hp: 0, atk: -1, def: 0, spa: 2, spd: 1, spe: 0});
|
|
62
|
+
expect(p.stats).toEqual({hp: 207, atk: 85, def: 183, spa: 110, spd: 136, spe: 105});
|
|
63
|
+
|
|
64
|
+
expect(p.moves).toEqual(['Surf', 'Rest', 'Curse', 'Sleep Talk'] as MoveName[]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('gen1', () => {
|
|
68
|
+
const p = new Pokemon(1, 'Tauros', {
|
|
69
|
+
level: 100,
|
|
70
|
+
ivs: {spc: 20, def: 16},
|
|
71
|
+
evs: {atk: 200},
|
|
72
|
+
curHP: 500,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// HP DV should be derived
|
|
76
|
+
expect(p.ivs).toEqual({hp: 20, atk: 31, def: 16, spa: 20, spd: 20, spe: 31});
|
|
77
|
+
expect(p.evs).toEqual({hp: 252, atk: 200, def: 252, spa: 252, spd: 252, spe: 252});
|
|
78
|
+
expect(p.stats).toEqual({hp: 343, atk: 298, def: 274, spa: 228, spd: 228, spe: 318});
|
|
79
|
+
|
|
80
|
+
// curHP() capped to maxHP
|
|
81
|
+
expect(p.curHP()).toBe(p.maxHP());
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('getForme', () => {
|
|
85
|
+
expect(Pokemon.getForme(1, 'Gengar')).toBe('Gengar');
|
|
86
|
+
|
|
87
|
+
expect(Pokemon.getForme(7, 'Gengar', 'Black Sludge', 'Hypnosis')).toBe('Gengar');
|
|
88
|
+
expect(Pokemon.getForme(7, 'Gengar', 'Gengarite', 'Hypnosis')).toBe('Gengar-Mega');
|
|
89
|
+
|
|
90
|
+
expect(Pokemon.getForme(7, 'Charizard')).toBe('Charizard');
|
|
91
|
+
expect(Pokemon.getForme(7, 'Charizard', 'Charizardite X')).toBe('Charizard-Mega-X');
|
|
92
|
+
expect(Pokemon.getForme(7, 'Charizard', 'Charizardite Y')).toBe('Charizard-Mega-Y');
|
|
93
|
+
expect(Pokemon.getForme(7, 'Mewtwo', 'Choice Specs', 'Psystrike')).toBe('Mewtwo');
|
|
94
|
+
expect(Pokemon.getForme(7, 'Mewtwo', 'Mewtwonite X', 'Psystrike')).toBe('Mewtwo-Mega-X');
|
|
95
|
+
expect(Pokemon.getForme(7, 'Mewtwo', 'Mewtwonite Y', 'Psystrike')).toBe('Mewtwo-Mega-Y');
|
|
96
|
+
|
|
97
|
+
expect(Pokemon.getForme(7, 'Groudon', 'Choice Band', 'Earthquake')).toBe('Groudon');
|
|
98
|
+
expect(Pokemon.getForme(7, 'Groudon', 'Red Orb', 'Earthquake')).toBe('Groudon-Primal');
|
|
99
|
+
expect(Pokemon.getForme(7, 'Kyogre', 'Choice Specs', 'Surf')).toBe('Kyogre');
|
|
100
|
+
expect(Pokemon.getForme(7, 'Kyogre', 'Blue Orb', 'Surf')).toBe('Kyogre-Primal');
|
|
101
|
+
|
|
102
|
+
expect(Pokemon.getForme(7, 'Meloetta', 'Leftovers', 'Psychic')).toBe('Meloetta');
|
|
103
|
+
expect(Pokemon.getForme(7, 'Meloetta', 'Leftovers', 'Relic Song')).toBe('Meloetta-Pirouette');
|
|
104
|
+
expect(Pokemon.getForme(7, 'Rayquaza', undefined, 'Earthquake')).toBe('Rayquaza');
|
|
105
|
+
expect(Pokemon.getForme(7, 'Rayquaza', undefined, 'Dragon Ascent')).toBe('Rayquaza-Mega');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('hasType', () => {
|
|
109
|
+
const p = new Pokemon(7, 'Gengar');
|
|
110
|
+
expect(p.hasType('Ghost')).toBe(true);
|
|
111
|
+
expect(p.hasType('Poison')).toBe(true);
|
|
112
|
+
expect(p.hasType('Fire')).toBe(false);
|
|
113
|
+
expect(p.hasType('Ice')).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('Gigantamx weights', () => {
|
|
117
|
+
expect(new Pokemon(8, 'Venusaur-Gmax').weightkg).toBe(100);
|
|
118
|
+
expect(new Pokemon(8, 'Venusaur-Gmax', {isDynamaxed: true}).weightkg).toBe(0);
|
|
119
|
+
expect(new Pokemon(8, 'Venusaur-Gmax', {overrides: {weightkg: 50}}).weightkg).toBe(50);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {Generations} from '../data';
|
|
2
|
+
import type {GenerationNum, StatsTable, StatID} from '../data/interface';
|
|
3
|
+
import {Stats} from '../stats';
|
|
4
|
+
import {getModifiedStat} from '../mechanics/util';
|
|
5
|
+
|
|
6
|
+
describe('Stats', () => {
|
|
7
|
+
test('displayStat', () => {
|
|
8
|
+
expect(Stats.displayStat('hp')).toBe('HP');
|
|
9
|
+
expect(Stats.displayStat('atk')).toBe('Atk');
|
|
10
|
+
expect(Stats.displayStat('def')).toBe('Def');
|
|
11
|
+
expect(Stats.displayStat('spa')).toBe('SpA');
|
|
12
|
+
expect(Stats.displayStat('spd')).toBe('SpD');
|
|
13
|
+
expect(Stats.displayStat('spe')).toBe('Spe');
|
|
14
|
+
expect(Stats.displayStat('spc')).toBe('Spc');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('calcStat', () => {
|
|
18
|
+
const RBY: StatsTable = {
|
|
19
|
+
hp: 403,
|
|
20
|
+
atk: 298,
|
|
21
|
+
def: 298,
|
|
22
|
+
spa: 298,
|
|
23
|
+
spd: 298,
|
|
24
|
+
spe: 298,
|
|
25
|
+
};
|
|
26
|
+
const ADV: StatsTable = {hp: 404, atk: 328, def: 299, spa: 269, spd: 299, spe: 299};
|
|
27
|
+
for (let gen = 1; gen <= 9; gen++) {
|
|
28
|
+
for (const s in ADV) {
|
|
29
|
+
const stat = s as StatID;
|
|
30
|
+
const val = Stats.calcStat(
|
|
31
|
+
Generations.get(gen as GenerationNum),
|
|
32
|
+
stat,
|
|
33
|
+
100,
|
|
34
|
+
31,
|
|
35
|
+
252,
|
|
36
|
+
100,
|
|
37
|
+
'Adamant'
|
|
38
|
+
);
|
|
39
|
+
expect(val).toBe(gen < 3 ? RBY[stat] : ADV[stat]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Shedinja
|
|
44
|
+
expect(Stats.calcStat(Generations.get(8), 'hp', 1, 31, 252, 100, 'Jolly')).toBe(1);
|
|
45
|
+
// no nature
|
|
46
|
+
expect(Stats.calcStat(Generations.get(8), 'atk', 100, 31, 252, 100)).toBe(299);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('dvs', () => {
|
|
50
|
+
for (let dv = 0; dv <= 15; dv++) {
|
|
51
|
+
expect(Stats.IVToDV(Stats.DVToIV(dv))).toBe(dv);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expect(
|
|
55
|
+
Stats.getHPDV({
|
|
56
|
+
atk: Stats.DVToIV(15),
|
|
57
|
+
def: Stats.DVToIV(15),
|
|
58
|
+
spc: Stats.DVToIV(15),
|
|
59
|
+
spe: Stats.DVToIV(15),
|
|
60
|
+
})
|
|
61
|
+
).toBe(15);
|
|
62
|
+
expect(
|
|
63
|
+
Stats.getHPDV({
|
|
64
|
+
atk: Stats.DVToIV(5),
|
|
65
|
+
def: Stats.DVToIV(15),
|
|
66
|
+
spc: Stats.DVToIV(13),
|
|
67
|
+
spe: Stats.DVToIV(13),
|
|
68
|
+
})
|
|
69
|
+
).toBe(15);
|
|
70
|
+
expect(
|
|
71
|
+
Stats.getHPDV({
|
|
72
|
+
atk: Stats.DVToIV(15),
|
|
73
|
+
def: Stats.DVToIV(3),
|
|
74
|
+
spc: Stats.DVToIV(11),
|
|
75
|
+
spe: Stats.DVToIV(10),
|
|
76
|
+
})
|
|
77
|
+
).toBe(13);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('gen 2 modifications', () => {
|
|
81
|
+
expect(getModifiedStat(158, -1, Generations.get(2))).toBe(104); // Snorlax after Curse
|
|
82
|
+
expect(getModifiedStat(238, -1, Generations.get(2))).toBe(157); // Skarmory after Curse
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {extend} from '../util';
|
|
2
|
+
|
|
3
|
+
describe('util', () => {
|
|
4
|
+
test('extend', () => {
|
|
5
|
+
const obj1 = {a: 1, b: {c: 2}, d: {e: 3}, f: 4};
|
|
6
|
+
const obj2 = {a: 2, b: {c: 3}, d: 4, e: {f: 5}};
|
|
7
|
+
|
|
8
|
+
expect(extend(true, {}, obj1)).toEqual(obj1);
|
|
9
|
+
expect(extend(true, {}, obj1, obj2)).toEqual({a: 2, b: {c: 3}, d: 4, e: {f: 5}, f: 4});
|
|
10
|
+
expect(extend(true, {}, obj2, obj1)).toEqual({
|
|
11
|
+
a: 1,
|
|
12
|
+
b: {c: 2},
|
|
13
|
+
d: {e: 3},
|
|
14
|
+
e: {f: 5},
|
|
15
|
+
f: 4,
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* eslint-disable eqeqeq, @typescript-eslint/unbound-method, @typescript-eslint/ban-types */
|
|
2
|
+
import type {ID} from './data/interface';
|
|
3
|
+
|
|
4
|
+
export function toID(text: any): ID {
|
|
5
|
+
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '') as ID;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function error(err: boolean, msg: string) {
|
|
9
|
+
if (err) {
|
|
10
|
+
throw new Error(msg);
|
|
11
|
+
} else {
|
|
12
|
+
console.log(msg);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function assignWithout(
|
|
17
|
+
a: {[key: string]: any}, b: {[key: string]: any}, exclude: Set<string>
|
|
18
|
+
) {
|
|
19
|
+
for (const key in b) {
|
|
20
|
+
if (Object.prototype.hasOwnProperty.call(b, key) && !exclude.has(key)) {
|
|
21
|
+
a[key] = b[key];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// https://github.com/krzkaczor/ts-essentials v6.0.5
|
|
27
|
+
// MIT License Copyright 2018-2019 Chris Kaczor
|
|
28
|
+
export type Primitive = string | number | boolean | bigint | symbol | undefined | null;
|
|
29
|
+
export type Builtin = Primitive | Function | Date | Error | RegExp;
|
|
30
|
+
|
|
31
|
+
export type IsTuple<T> =
|
|
32
|
+
T extends [infer A] ? T
|
|
33
|
+
: T extends [infer A, infer B] ? T
|
|
34
|
+
: T extends [infer A, infer B, infer C] ? T
|
|
35
|
+
: T extends [infer A, infer B, infer C, infer D] ? T
|
|
36
|
+
: T extends [infer A, infer B, infer C, infer D, infer E] ? T
|
|
37
|
+
: never;
|
|
38
|
+
|
|
39
|
+
export type DeepPartial<T> =
|
|
40
|
+
T extends Builtin ? T
|
|
41
|
+
: T extends Map<infer K, infer V> ? Map<DeepPartial<K>, DeepPartial<V>>
|
|
42
|
+
: T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<DeepPartial<K>, DeepPartial<V>>
|
|
43
|
+
: T extends Set<infer U> ? Set<DeepPartial<U>>
|
|
44
|
+
: T extends ReadonlySet<infer U> ? ReadonlySet<DeepPartial<U>>
|
|
45
|
+
: T extends Array<infer U> ? T extends IsTuple<T>
|
|
46
|
+
? { [K in keyof T]?: DeepPartial<T[K]> }
|
|
47
|
+
: Array<DeepPartial<U>>
|
|
48
|
+
: T extends Promise<infer U> ? Promise<DeepPartial<U>>
|
|
49
|
+
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
|
50
|
+
: Partial<T>;
|
|
51
|
+
|
|
52
|
+
// jQuery JavaScript Library v2.0.3
|
|
53
|
+
// Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
|
|
54
|
+
const class2Type: {[c: string]: string} = {
|
|
55
|
+
'[object Boolean]': 'boolean',
|
|
56
|
+
'[object Number]': 'number',
|
|
57
|
+
'[object String]': 'string',
|
|
58
|
+
'[object Function]': 'function',
|
|
59
|
+
'[object Array]': 'array',
|
|
60
|
+
'[object Date]': 'date',
|
|
61
|
+
'[object RegExp]': 'regexp',
|
|
62
|
+
'[object Object]': 'object',
|
|
63
|
+
'[object Error]': 'error',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const coreToString = class2Type.toString;
|
|
67
|
+
const coreHasOwn = class2Type.hasOwnProperty;
|
|
68
|
+
|
|
69
|
+
function isFunction(obj: any) {
|
|
70
|
+
return getType(obj) === 'function';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isWindow(obj: any) {
|
|
74
|
+
return obj != null && obj === obj.window;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getType(obj: any) {
|
|
78
|
+
if (obj == null) {
|
|
79
|
+
return String(obj);
|
|
80
|
+
}
|
|
81
|
+
return typeof obj === 'object' || typeof obj === 'function'
|
|
82
|
+
? class2Type[coreToString.call(obj)] || 'object'
|
|
83
|
+
: typeof obj;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isPlainObject(obj: any) {
|
|
87
|
+
if (getType(obj) !== 'object' || obj.nodeType || isWindow(obj)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
if (obj.constructor && !coreHasOwn.call(obj.constructor.prototype, 'isPrototypeOf')) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function extend(this: any, ...args: any[]) {
|
|
103
|
+
let options, name, src, copy, copyIsArray, clone;
|
|
104
|
+
let target = args[0] || {};
|
|
105
|
+
let i = 1;
|
|
106
|
+
let deep = false;
|
|
107
|
+
const length = args.length;
|
|
108
|
+
|
|
109
|
+
if (typeof target === 'boolean') {
|
|
110
|
+
deep = target;
|
|
111
|
+
target = args[1] || {};
|
|
112
|
+
i = 2;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof target !== 'object' && !isFunction(target)) {
|
|
116
|
+
target = {};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (length === i) {
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
121
|
+
target = this;
|
|
122
|
+
--i;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (; i < length; i++) {
|
|
126
|
+
if ((options = args[i]) != null) {
|
|
127
|
+
// tslint:disable-next-line: forin
|
|
128
|
+
for (name in options) {
|
|
129
|
+
src = target[name];
|
|
130
|
+
copy = options[name];
|
|
131
|
+
|
|
132
|
+
if (target === copy) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
|
|
137
|
+
if (copyIsArray) {
|
|
138
|
+
copyIsArray = false;
|
|
139
|
+
clone = src && Array.isArray(src) ? src : [];
|
|
140
|
+
} else {
|
|
141
|
+
clone = src && isPlainObject(src) ? src : {};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
target[name] = extend(deep, clone, copy);
|
|
145
|
+
} else if (copy !== undefined) {
|
|
146
|
+
target[name] = copy;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return target;
|
|
153
|
+
}
|