@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.
Files changed (144) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc +12 -0
  3. package/bundle +107 -0
  4. package/dist/adaptable.d.ts +6 -0
  5. package/dist/adaptable.js +28 -0
  6. package/dist/adaptable.js.map +1 -0
  7. package/dist/calc.d.ts +6 -0
  8. package/dist/calc.js +26 -0
  9. package/dist/calc.js.map +1 -0
  10. package/dist/data/abilities.d.ts +15 -0
  11. package/dist/data/abilities.js +448 -0
  12. package/dist/data/abilities.js.map +1 -0
  13. package/dist/data/index.d.ts +2 -0
  14. package/dist/data/index.js +30 -0
  15. package/dist/data/index.js.map +1 -0
  16. package/dist/data/interface.d.ts +150 -0
  17. package/dist/data/interface.js +3 -0
  18. package/dist/data/interface.js.map +1 -0
  19. package/dist/data/items.d.ts +24 -0
  20. package/dist/data/items.js +708 -0
  21. package/dist/data/items.js.map +1 -0
  22. package/dist/data/moves.d.ts +86 -0
  23. package/dist/data/moves.js +5014 -0
  24. package/dist/data/moves.js.map +1 -0
  25. package/dist/data/natures.d.ts +17 -0
  26. package/dist/data/natures.js +127 -0
  27. package/dist/data/natures.js.map +1 -0
  28. package/dist/data/production.min.js +1 -0
  29. package/dist/data/species.d.ts +48 -0
  30. package/dist/data/species.js +10126 -0
  31. package/dist/data/species.js.map +1 -0
  32. package/dist/data/types.d.ts +23 -0
  33. package/dist/data/types.js +538 -0
  34. package/dist/data/types.js.map +1 -0
  35. package/dist/desc.d.ts +65 -0
  36. package/dist/desc.js +866 -0
  37. package/dist/desc.js.map +1 -0
  38. package/dist/field.d.ts +49 -0
  39. package/dist/field.js +111 -0
  40. package/dist/field.js.map +1 -0
  41. package/dist/index.d.ts +44 -0
  42. package/dist/index.js +99 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/items.d.ts +13 -0
  45. package/dist/items.js +434 -0
  46. package/dist/items.js.map +1 -0
  47. package/dist/mechanics/gen12.d.ts +6 -0
  48. package/dist/mechanics/gen12.js +271 -0
  49. package/dist/mechanics/gen12.js.map +1 -0
  50. package/dist/mechanics/gen3.d.ts +11 -0
  51. package/dist/mechanics/gen3.js +371 -0
  52. package/dist/mechanics/gen3.js.map +1 -0
  53. package/dist/mechanics/gen4.d.ts +11 -0
  54. package/dist/mechanics/gen4.js +596 -0
  55. package/dist/mechanics/gen4.js.map +1 -0
  56. package/dist/mechanics/gen56.d.ts +13 -0
  57. package/dist/mechanics/gen56.js +836 -0
  58. package/dist/mechanics/gen56.js.map +1 -0
  59. package/dist/mechanics/gen789.d.ts +14 -0
  60. package/dist/mechanics/gen789.js +1325 -0
  61. package/dist/mechanics/gen789.js.map +1 -0
  62. package/dist/mechanics/util.d.ts +39 -0
  63. package/dist/mechanics/util.js +675 -0
  64. package/dist/mechanics/util.js.map +1 -0
  65. package/dist/move.d.ts +50 -0
  66. package/dist/move.js +324 -0
  67. package/dist/move.js.map +1 -0
  68. package/dist/pokemon.d.ts +55 -0
  69. package/dist/pokemon.js +240 -0
  70. package/dist/pokemon.js.map +1 -0
  71. package/dist/production.min.js +1 -0
  72. package/dist/result.d.ts +34 -0
  73. package/dist/result.js +94 -0
  74. package/dist/result.js.map +1 -0
  75. package/dist/state.d.ts +77 -0
  76. package/dist/state.js +3 -0
  77. package/dist/state.js.map +1 -0
  78. package/dist/stats.d.ts +26 -0
  79. package/dist/stats.js +183 -0
  80. package/dist/stats.js.map +1 -0
  81. package/dist/test/calc.test.d.ts +1 -0
  82. package/dist/test/calc.test.js +1297 -0
  83. package/dist/test/calc.test.js.map +1 -0
  84. package/dist/test/data.test.d.ts +1 -0
  85. package/dist/test/data.test.js +368 -0
  86. package/dist/test/data.test.js.map +1 -0
  87. package/dist/test/gen.d.ts +135 -0
  88. package/dist/test/gen.js +636 -0
  89. package/dist/test/gen.js.map +1 -0
  90. package/dist/test/helper.d.ts +55 -0
  91. package/dist/test/helper.js +174 -0
  92. package/dist/test/helper.js.map +1 -0
  93. package/dist/test/move.test.d.ts +1 -0
  94. package/dist/test/move.test.js +14 -0
  95. package/dist/test/move.test.js.map +1 -0
  96. package/dist/test/pokemon.test.d.ts +1 -0
  97. package/dist/test/pokemon.test.js +102 -0
  98. package/dist/test/pokemon.test.js.map +1 -0
  99. package/dist/test/stats.test.d.ts +1 -0
  100. package/dist/test/stats.test.js +64 -0
  101. package/dist/test/stats.test.js.map +1 -0
  102. package/dist/test/utils.test.d.ts +1 -0
  103. package/dist/test/utils.test.js +19 -0
  104. package/dist/test/utils.test.js.map +1 -0
  105. package/dist/util.d.ts +17 -0
  106. package/dist/util.js +115 -0
  107. package/dist/util.js.map +1 -0
  108. package/jest.config.js +11 -0
  109. package/package.json +40 -0
  110. package/src/adaptable.ts +12 -0
  111. package/src/calc.ts +40 -0
  112. package/src/data/abilities.ts +383 -0
  113. package/src/data/index.ts +36 -0
  114. package/src/data/interface.ts +176 -0
  115. package/src/data/items.ts +632 -0
  116. package/src/data/moves.ts +5028 -0
  117. package/src/data/natures.ts +65 -0
  118. package/src/data/species.ts +10098 -0
  119. package/src/data/types.ts +478 -0
  120. package/src/desc.ts +1063 -0
  121. package/src/field.ts +124 -0
  122. package/src/index.ts +156 -0
  123. package/src/items.ts +423 -0
  124. package/src/mechanics/gen12.ts +297 -0
  125. package/src/mechanics/gen3.ts +444 -0
  126. package/src/mechanics/gen4.ts +702 -0
  127. package/src/mechanics/gen56.ts +1134 -0
  128. package/src/mechanics/gen789.ts +1788 -0
  129. package/src/mechanics/util.ts +676 -0
  130. package/src/move.ts +337 -0
  131. package/src/pokemon.ts +244 -0
  132. package/src/result.ts +106 -0
  133. package/src/state.ts +81 -0
  134. package/src/stats.ts +213 -0
  135. package/src/test/calc.test.ts +1588 -0
  136. package/src/test/data.test.ts +129 -0
  137. package/src/test/gen.ts +514 -0
  138. package/src/test/helper.ts +185 -0
  139. package/src/test/move.test.ts +13 -0
  140. package/src/test/pokemon.test.ts +121 -0
  141. package/src/test/stats.test.ts +84 -0
  142. package/src/test/utils.test.ts +18 -0
  143. package/src/util.ts +153 -0
  144. package/tsconfig.json +10 -0
@@ -0,0 +1,1297 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __values = (this && this.__values) || function(o) {
14
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
15
+ if (m) return m.call(o);
16
+ if (o && typeof o.length === "number") return {
17
+ next: function () {
18
+ if (o && i >= o.length) o = void 0;
19
+ return { value: o && o[i++], done: !o };
20
+ }
21
+ };
22
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
23
+ };
24
+ var __read = (this && this.__read) || function (o, n) {
25
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
26
+ if (!m) return o;
27
+ var i = m.call(o), r, ar = [], e;
28
+ try {
29
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
30
+ }
31
+ catch (error) { e = { error: error }; }
32
+ finally {
33
+ try {
34
+ if (r && !r.done && (m = i["return"])) m.call(i);
35
+ }
36
+ finally { if (e) throw e.error; }
37
+ }
38
+ return ar;
39
+ };
40
+ exports.__esModule = true;
41
+ var helper_1 = require("./helper");
42
+ describe('calc', function () {
43
+ describe('Multi-Gen', function () {
44
+ (0, helper_1.inGens)(4, 7, function (_a) {
45
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
46
+ test("Grass Knot (gen ".concat(gen, ")"), function () {
47
+ var result = calculate(Pokemon('Groudon'), Pokemon('Groudon'), Move('Grass Knot'));
48
+ expect(result.range()).toEqual([190, 224]);
49
+ });
50
+ });
51
+ (0, helper_1.inGens)(4, 7, function (_a) {
52
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
53
+ test("Arceus Plate (gen ".concat(gen, ")"), function () {
54
+ var result = calculate(Pokemon('Arceus', { item: 'Meadow Plate' }), Pokemon('Blastoise'), Move('Judgment'));
55
+ expect(result.range()).toEqual([194, 230]);
56
+ expect(result.desc()).toBe('0 SpA Meadow Plate Arceus Judgment vs. 0 HP / 0 SpD Blastoise: 194-230 (64.8 - 76.9%) -- guaranteed 2HKO');
57
+ });
58
+ });
59
+ (0, helper_1.inGens)(1, 9, function (_a) {
60
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
61
+ test("Night Shade / Seismic Toss (gen ".concat(gen, ")"), function () {
62
+ var e_1, _a;
63
+ var mew = Pokemon('Mew', { level: 50 });
64
+ var vulpix = Pokemon('Vulpix');
65
+ try {
66
+ for (var _b = __values([Move('Seismic Toss'), Move('Night Shade')]), _c = _b.next(); !_c.done; _c = _b.next()) {
67
+ var move = _c.value;
68
+ var result = calculate(mew, vulpix, move);
69
+ expect(result.damage).toBe(50);
70
+ expect(result.desc()).toBe(gen < 3
71
+ ? "Lvl 50 Mew ".concat(move.name, " vs. Vulpix: 50-50 (17.9 - 17.9%) -- guaranteed 6HKO")
72
+ : "Lvl 50 Mew ".concat(move.name, " vs. 0 HP Vulpix: 50-50 (23 - 23%) -- guaranteed 5HKO"));
73
+ }
74
+ }
75
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
76
+ finally {
77
+ try {
78
+ if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
79
+ }
80
+ finally { if (e_1) throw e_1.error; }
81
+ }
82
+ });
83
+ });
84
+ (0, helper_1.tests)('Comet Punch', function (_a) {
85
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
86
+ expect(calculate(Pokemon('Snorlax'), Pokemon('Vulpix'), Move('Comet Punch'))).toMatch(gen, {
87
+ 1: { range: [108, 129], desc: 'Snorlax Comet Punch (3 hits) vs. Vulpix', result: '(38.7 - 46.2%) -- approx. 3HKO' },
88
+ 3: { range: [132, 156], desc: '0 Atk Snorlax Comet Punch (3 hits) vs. 0 HP / 0 Def Vulpix', result: '(60.8 - 71.8%) -- approx. 2HKO' },
89
+ 4: { range: [129, 156], result: '(59.4 - 71.8%) -- approx. 2HKO' }
90
+ });
91
+ });
92
+ (0, helper_1.inGens)(1, 9, function (_a) {
93
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
94
+ test("Immunity (gen ".concat(gen, ")"), function () {
95
+ expect(calculate(Pokemon('Snorlax'), Pokemon('Gengar'), Move('Hyper Beam')).damage).toBe(0);
96
+ });
97
+ });
98
+ (0, helper_1.inGens)(1, 9, function (_a) {
99
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
100
+ test("Non-damaging (gen ".concat(gen, ")"), function () {
101
+ var result = calculate(Pokemon('Snorlax'), Pokemon('Vulpix'), Move('Barrier'));
102
+ expect(result.damage).toBe(0);
103
+ expect(result.desc()).toBe('Snorlax Barrier vs. Vulpix: 0-0 (0 - 0%)');
104
+ });
105
+ });
106
+ (0, helper_1.inGens)(1, 9, function (_a) {
107
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
108
+ test("Protect (gen ".concat(gen, ")"), function () {
109
+ var field = Field({ defenderSide: { isProtected: true } });
110
+ var snorlax = Pokemon('Snorlax');
111
+ var chansey = Pokemon('Chansey');
112
+ expect(calculate(snorlax, chansey, Move('Hyper Beam'), field).damage).toBe(0);
113
+ });
114
+ });
115
+ (0, helper_1.inGens)(1, 9, function (_a) {
116
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
117
+ test("Critical hits ignore attack decreases (gen ".concat(gen, ")"), function () {
118
+ var field = Field({ defenderSide: { isReflect: true } });
119
+ var mew = Pokemon('Mew', { status: 'brn' });
120
+ var vulpix = Pokemon('Vulpix');
121
+ var explosion = Move('Explosion', { isCrit: true });
122
+ var result = calculate(mew, vulpix, explosion, field);
123
+ mew.boosts.atk = 2;
124
+ vulpix.boosts.def = 2;
125
+ if (gen < 2) {
126
+ expect(result.range()).toEqual([799, 939]);
127
+ expect(result.desc()).toBe('Mew Explosion vs. Vulpix on a critical hit: 799-939 (286.3 - 336.5%) -- guaranteed OHKO');
128
+ }
129
+ else if (gen < 5 && gen > 2) {
130
+ expect(result.range()).toEqual([729, 858]);
131
+ expect(result.desc()).toBe('0 Atk burned Mew Explosion vs. 0 HP / 0 Def Vulpix on a critical hit: 729-858 (335.9 - 395.3%) -- guaranteed OHKO');
132
+ }
133
+ else if (gen === 5) {
134
+ expect(result.range()).toEqual([364, 429]);
135
+ expect(result.desc()).toBe('0 Atk burned Mew Explosion vs. 0 HP / 0 Def Vulpix on a critical hit: 364-429 (167.7 - 197.6%) -- guaranteed OHKO');
136
+ }
137
+ else if (gen >= 6) {
138
+ expect(result.range()).toEqual([273, 321]);
139
+ expect(result.desc()).toBe('0 Atk burned Mew Explosion vs. 0 HP / 0 Def Vulpix on a critical hit: 273-321 (125.8 - 147.9%) -- guaranteed OHKO');
140
+ }
141
+ explosion.isCrit = false;
142
+ result = calculate(mew, vulpix, explosion, field);
143
+ if (gen === 1) {
144
+ expect(result.range()).toEqual([102, 120]);
145
+ }
146
+ else if (gen === 2) {
147
+ expect(result.range()).toEqual([149, 176]);
148
+ }
149
+ else if (gen > 2 && gen < 5) {
150
+ expect(result.range()).toEqual([182, 215]);
151
+ }
152
+ else {
153
+ expect(result.range()).toEqual([91, 107]);
154
+ }
155
+ });
156
+ });
157
+ (0, helper_1.inGens)(1, 9, function (_a) {
158
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
159
+ test("Struggle vs. Ghost (gen ".concat(gen, ")"), function () {
160
+ var result = calculate(Pokemon('Mew'), Pokemon('Gengar'), Move('Struggle'));
161
+ if (gen < 2) {
162
+ expect(result.range()[1]).toBe(0);
163
+ }
164
+ else {
165
+ expect(result.range()[1]).toBeGreaterThan(0);
166
+ }
167
+ });
168
+ });
169
+ (0, helper_1.inGens)(3, 9, function (_a) {
170
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
171
+ test("Weather Ball should change type depending on the weather (gen ".concat(gen, ")"), function () {
172
+ var e_2, _a;
173
+ var weathers = [
174
+ {
175
+ weather: 'Sun', type: 'Fire', damage: {
176
+ adv: { range: [346, 408], desc: '(149.7 - 176.6%) -- guaranteed OHKO' },
177
+ dpp: { range: [342, 404], desc: '(148 - 174.8%) -- guaranteed OHKO' },
178
+ modern: { range: [344, 408], desc: '(148.9 - 176.6%) -- guaranteed OHKO' }
179
+ }
180
+ },
181
+ {
182
+ weather: 'Rain', type: 'Water', damage: {
183
+ adv: { range: [86, 102], desc: '(37.2 - 44.1%) -- guaranteed 3HKO' },
184
+ dpp: { range: [85, 101], desc: '(36.7 - 43.7%) -- guaranteed 3HKO' },
185
+ modern: { range: [86, 102], desc: '(37.2 - 44.1%) -- guaranteed 3HKO' }
186
+ }
187
+ },
188
+ {
189
+ weather: 'Sand', type: 'Rock', damage: {
190
+ adv: {
191
+ range: [96, 114],
192
+ desc: '(41.5 - 49.3%) -- 82.4% chance to 2HKO after sandstorm damage'
193
+ },
194
+ dpp: {
195
+ range: [77, 91],
196
+ desc: '(33.3 - 39.3%) -- guaranteed 3HKO after sandstorm damage'
197
+ },
198
+ modern: {
199
+ range: [77, 91],
200
+ desc: '(33.3 - 39.3%) -- guaranteed 3HKO after sandstorm damage'
201
+ }
202
+ }
203
+ },
204
+ {
205
+ weather: 'Hail', type: 'Ice', damage: {
206
+ adv: {
207
+ range: [234, 276],
208
+ desc: '(101.2 - 119.4%) -- guaranteed OHKO'
209
+ },
210
+ dpp: {
211
+ range: [230, 272],
212
+ desc: '(99.5 - 117.7%) -- 93.8% chance to OHKO (guaranteed OHKO after hail damage)'
213
+ },
214
+ modern: {
215
+ range: [230, 272],
216
+ desc: '(99.5 - 117.7%) -- 93.8% chance to OHKO (guaranteed OHKO after hail damage)'
217
+ }
218
+ }
219
+ },
220
+ ];
221
+ try {
222
+ for (var weathers_1 = __values(weathers), weathers_1_1 = weathers_1.next(); !weathers_1_1.done; weathers_1_1 = weathers_1.next()) {
223
+ var _b = weathers_1_1.value, weather = _b.weather, type = _b.type, damage = _b.damage;
224
+ var dmg = gen === 3 ? damage.adv : gen === 4 ? damage.dpp : damage.modern;
225
+ var _c = __read(gen === 3 && type === 'Rock' ? ['Atk', 'Def'] : ['SpA', 'SpD'], 2), atk = _c[0], def = _c[1];
226
+ var result = calculate(Pokemon('Castform'), Pokemon('Bulbasaur'), Move('Weather Ball'), Field({ weather: weather }));
227
+ expect(result.range()).toEqual(dmg.range);
228
+ expect(result.desc()).toBe("0 ".concat(atk, " Castform Weather Ball (100 BP ").concat(type, ") vs. 0 HP / 0 ").concat(def, " Bulbasaur in ").concat(weather, ": ").concat(dmg.range[0], "-").concat(dmg.range[1], " ").concat(dmg.desc));
229
+ }
230
+ }
231
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
232
+ finally {
233
+ try {
234
+ if (weathers_1_1 && !weathers_1_1.done && (_a = weathers_1["return"])) _a.call(weathers_1);
235
+ }
236
+ finally { if (e_2) throw e_2.error; }
237
+ }
238
+ });
239
+ });
240
+ (0, helper_1.inGens)(6, 9, function (_a) {
241
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
242
+ test("Flying Press (gen ".concat(gen, ")"), function () {
243
+ var attacker = Pokemon('Hawlucha');
244
+ var flyingPress = Move('Flying Press');
245
+ var result = calculate(attacker, Pokemon('Cacturne'), flyingPress);
246
+ if (gen === 6) {
247
+ expect(result.range()).toEqual([484, 576]);
248
+ expect(result.desc()).toBe('0 Atk Hawlucha Flying Press vs. 0 HP / 0 Def Cacturne: 484-576 (172.2 - 204.9%) -- guaranteed OHKO');
249
+ }
250
+ else {
251
+ expect(result.range()).toEqual([612, 720]);
252
+ expect(result.desc()).toBe('0 Atk Hawlucha Flying Press vs. 0 HP / 0 Def Cacturne: 612-720 (217.7 - 256.2%) -- guaranteed OHKO');
253
+ }
254
+ var result2 = calculate(attacker, Pokemon('Spiritomb'), flyingPress);
255
+ expect(result2.range()).toEqual([0, 0]);
256
+ var scrappyAttacker = Pokemon('Hawlucha', { 'ability': 'Scrappy' });
257
+ var ringTargetSpiritomb = Pokemon('Spiritomb', { 'item': 'Ring Target' });
258
+ var result3 = calculate(attacker, ringTargetSpiritomb, flyingPress);
259
+ var result4 = calculate(scrappyAttacker, Pokemon('Spiritomb'), flyingPress);
260
+ if (gen === 6) {
261
+ expect(result3.range()).toEqual([152, 180]);
262
+ expect(result4.range()).toEqual([152, 180]);
263
+ }
264
+ else {
265
+ expect(result3.range()).toEqual([188, 224]);
266
+ expect(result4.range()).toEqual([188, 224]);
267
+ }
268
+ });
269
+ });
270
+ (0, helper_1.inGens)(6, 9, function (_a) {
271
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
272
+ test("Thousand Arrows and Ring Target Should negate damage nullfiers (gen ".concat(gen, ")"), function () {
273
+ var result = calculate(Pokemon('Zygarde'), Pokemon('Swellow'), Move('Thousand Arrows'));
274
+ expect(result.range()).toEqual([147, 174]);
275
+ expect(result.desc()).toBe('0 Atk Zygarde Thousand Arrows vs. 0 HP / 0 Def Swellow: 147-174 (56.3 - 66.6%) -- guaranteed 2HKO');
276
+ });
277
+ });
278
+ (0, helper_1.inGens)(5, 9, function (_a) {
279
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
280
+ test("Ring Target should negate type nullfiers (gen ".concat(gen, ")"), function () {
281
+ var attacker = Pokemon('Mew');
282
+ var defender = Pokemon('Skarmory', { 'item': 'Ring Target' });
283
+ var result = calculate(attacker, defender, Move('Sludge Bomb'));
284
+ expect(result.range()).toEqual([87, 103]);
285
+ expect(result.desc()).toBe('0 SpA Mew Sludge Bomb vs. 0 HP / 0 SpD Skarmory: 87-103 (32.1 - 38%) -- 94.6% chance to 3HKO');
286
+ var result2 = calculate(attacker, defender, Move('Earth Power'));
287
+ expect(result2.range()).toEqual([174, 206]);
288
+ expect(result2.desc()).toBe('0 SpA Mew Earth Power vs. 0 HP / 0 SpD Skarmory: 174-206 (64.2 - 76%) -- guaranteed 2HKO');
289
+ });
290
+ });
291
+ describe('IVs are shown if applicable', function () {
292
+ (0, helper_1.inGens)(3, 9, function (_a) {
293
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
294
+ test("Gen ".concat(gen), function () {
295
+ var ivs = { spa: 9, spd: 9, hp: 9 };
296
+ var evs = { spa: 9, spd: 9, hp: 9 };
297
+ var result = calculate(Pokemon('Mew', { ivs: ivs }), Pokemon('Mew', { evs: evs }), Move('Psychic'));
298
+ expect(result.desc()).toBe('0 SpA 9 IVs Mew Psychic vs. 9 HP / 9 SpD Mew: 43-51 (12.5 - 14.8%) -- possible 7HKO');
299
+ result = calculate(Pokemon('Mew', { evs: evs }), Pokemon('Mew', { ivs: ivs }), Move('Psychic'));
300
+ expect(result.desc()).toBe('9 SpA Mew Psychic vs. 0 HP 9 IVs / 0 SpD 9 IVs Mew: 54-64 (16.9 - 20%) -- possible 5HKO');
301
+ });
302
+ });
303
+ });
304
+ (0, helper_1.inGens)(4, 9, function (_a) {
305
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
306
+ var zapdos = Pokemon('Zapdos', { item: 'Iron Ball' });
307
+ if (gen === 4) {
308
+ test("Iron Ball negates ground immunities (gen ".concat(gen, ")"), function () {
309
+ var result = calculate(Pokemon('Vibrava'), zapdos, Move('Earthquake'));
310
+ expect(result.range()).toEqual([186, 218]);
311
+ expect(result.desc()).toBe('0 Atk Vibrava Earthquake vs. 0 HP / 0 Def Zapdos: 186-218 (57.9 - 67.9%) -- guaranteed 2HKO');
312
+ });
313
+ }
314
+ else {
315
+ test("Iron Ball Should negate damage nullifiers (gen ".concat(gen, ")"), function () {
316
+ var result = calculate(Pokemon('Vibrava'), zapdos, Move('Earthquake'));
317
+ expect(result.range()).toEqual([93, 109]);
318
+ expect(result.desc()).toBe('0 Atk Vibrava Earthquake vs. 0 HP / 0 Def Zapdos: 93-109 (28.9 - 33.9%) -- 1.2% chance to 3HKO');
319
+ });
320
+ }
321
+ test("Iron Ball negates levitate (gen ".concat(gen, ")"), function () {
322
+ var result = calculate(Pokemon('Poliwrath'), Pokemon('Mismagius', { item: 'Iron Ball' }), Move('Mud Shot'));
323
+ expect(result.range()).toEqual([29, 35]);
324
+ expect(result.desc()).toBe('0 SpA Poliwrath Mud Shot vs. 0 HP / 0 SpD Mismagius: 29-35 (11.1 - 13.4%) -- possible 8HKO');
325
+ });
326
+ });
327
+ (0, helper_1.inGens)(5, 9, function (_a) {
328
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
329
+ var dragonite = Pokemon('Dragonite', { ability: 'Multiscale' });
330
+ var dragonite1 = Pokemon('Dragonite', { ability: 'Multiscale', curHP: 69 });
331
+ var dragonite2 = Pokemon('Dragonite', { ability: 'Shadow Shield', item: 'Heavy-Duty Boots' });
332
+ if (gen > 7) {
333
+ test("Multiscale and Shadow Shield halves damage even if there are hazzards if holding Heavy-Duty Boots (gen ".concat(gen, ")"), function () {
334
+ var field = Field({ defenderSide: { isSR: true } });
335
+ var result = calculate(Pokemon('Abomasnow'), dragonite2, Move('Blizzard'), field);
336
+ expect(result.range()).toEqual([222, 264]);
337
+ expect(result.desc()).toBe('0 SpA Abomasnow Blizzard vs. 0 HP / 0 SpD Shadow Shield Dragonite: 222-264 (68.7 - 81.7%) -- guaranteed 2HKO');
338
+ });
339
+ }
340
+ test("Multiscale and Shadow Shield should not halve damage if less than 100% HP (gen ".concat(gen, ")"), function () {
341
+ var result = calculate(Pokemon('Abomasnow'), dragonite1, Move('Ice Shard'));
342
+ expect(result.range()).toEqual([168, 204]);
343
+ expect(result.desc()).toBe('0 Atk Abomasnow Ice Shard vs. 0 HP / 0 Def Dragonite: 168-204 (52 - 63.1%) -- guaranteed OHKO');
344
+ });
345
+ test("Multiscale and Shadow Shield Should halve damage taken (gen ".concat(gen, ")"), function () {
346
+ var result = calculate(Pokemon('Abomasnow'), dragonite, Move('Ice Shard'));
347
+ expect(result.range()).toEqual([84, 102]);
348
+ expect(result.desc()).toBe('0 Atk Abomasnow Ice Shard vs. 0 HP / 0 Def Multiscale Dragonite: 84-102 (26 - 31.5%) -- guaranteed 4HKO');
349
+ });
350
+ describe('Weight', function () {
351
+ describe('Heavy Metal', function () {
352
+ function testBP(ability) {
353
+ return calculate(Pokemon('Simisage', { ability: ability }), Pokemon('Simisear', { ability: 'Heavy Metal' }), Move('Grass Knot')).rawDesc.moveBP;
354
+ }
355
+ it('should double the weight of a Pokemon', function () { return expect(testBP('Gluttony')).toBe(80); });
356
+ it('should be negated by Mold Breaker', function () { return expect(testBP('Mold Breaker')).toBe(60); });
357
+ });
358
+ describe('Light Metal', function () {
359
+ function testBP(ability) {
360
+ return calculate(Pokemon('Simisage', { ability: ability }), Pokemon('Registeel', { ability: 'Light Metal' }), Move('Grass Knot')).rawDesc.moveBP;
361
+ }
362
+ it('should halve the weight of a Pokemon', function () { return expect(testBP('Gluttony')).toBe(100); });
363
+ it('should be negated by Mold Breaker', function () { return expect(testBP('Mold Breaker')).toBe(120); });
364
+ });
365
+ describe('Float Stone', function () {
366
+ function testBP(ability) {
367
+ return calculate(Pokemon('Simisage', { ability: 'Gluttony' }), Pokemon('Registeel', { ability: ability, item: 'Float Stone' }), Move('Grass Knot')).rawDesc.moveBP;
368
+ }
369
+ it('should halve the weight of a Pokemon', function () { return expect(testBP()).toBe(100); });
370
+ it('should stack with Light Metal', function () { return expect(testBP('Light Metal')).toBe(80); });
371
+ });
372
+ });
373
+ });
374
+ (0, helper_1.inGen)(8, function (_a) {
375
+ var gen = _a.gen, Pokemon = _a.Pokemon;
376
+ test("Pokemon should double their HP stat when dynamaxing (gen ".concat(gen, ")"), function () {
377
+ var munchlax = Pokemon('Munchlax', { isDynamaxed: true });
378
+ expect(munchlax.curHP()).toBe(822);
379
+ });
380
+ });
381
+ (0, helper_1.inGens)(7, 9, function (_a) {
382
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
383
+ test("Psychic Terrain (gen ".concat(gen, ")"), function () {
384
+ var field = Field({ terrain: 'Psychic' });
385
+ var Mewtwo = Pokemon('Mewtwo', {
386
+ nature: 'Timid',
387
+ evs: { spa: 252 },
388
+ boosts: { spa: 2 }
389
+ });
390
+ var Milotic = Pokemon('Milotic', {
391
+ item: 'Flame Orb',
392
+ nature: 'Bold',
393
+ ability: 'Marvel Scale',
394
+ evs: { hp: 248, def: 184 },
395
+ status: 'brn',
396
+ boosts: { spd: 1 }
397
+ });
398
+ var Psystrike = Move('Psystrike');
399
+ var sPunch = Move('Sucker Punch');
400
+ var result = calculate(Mewtwo, Milotic, Psystrike, field);
401
+ if (gen < 8) {
402
+ expect(result.range()).toEqual([331, 391]);
403
+ expect(result.desc()).toBe('+2 252 SpA Mewtwo Psystrike vs. 248 HP / 184+ Def Marvel Scale Milotic in Psychic Terrain: 331-391 (84.2 - 99.4%) -- 37.5% chance to OHKO after burn damage');
404
+ }
405
+ else {
406
+ expect(result.range()).toEqual([288, 339]);
407
+ expect(result.desc()).toBe('+2 252 SpA Mewtwo Psystrike vs. 248 HP / 184+ Def Marvel Scale Milotic in Psychic Terrain: 288-339 (73.2 - 86.2%) -- guaranteed 2HKO after burn damage');
408
+ }
409
+ result = calculate(Mewtwo, Milotic, sPunch, field);
410
+ expect(result.range()).toEqual([0, 0]);
411
+ });
412
+ });
413
+ (0, helper_1.inGens)(6, 9, function (_a) {
414
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
415
+ test("Parental Bond (gen ".concat(gen, ")"), function () {
416
+ var result = calculate(Pokemon('Kangaskhan-Mega', { evs: { atk: 152 } }), Pokemon('Amoonguss', { nature: 'Bold', evs: { hp: 252, def: 152 } }), Move('Frustration'));
417
+ if (gen === 6) {
418
+ expect(result.damage).toEqual([
419
+ [153, 154, 156, 157, 159, 162, 163, 165, 166, 168, 171, 172, 174, 175, 177, 180],
420
+ [76, 76, 78, 78, 79, 81, 81, 82, 82, 84, 85, 85, 87, 87, 88, 90],
421
+ ]);
422
+ expect(result.desc()).toBe('152 Atk Parental Bond Kangaskhan-Mega Frustration vs. 252 HP / 152+ Def Amoonguss: 229-270 (53 - 62.5%) -- approx. 2HKO');
423
+ }
424
+ else {
425
+ expect(result.damage).toEqual([
426
+ [153, 154, 156, 157, 159, 162, 163, 165, 166, 168, 171, 172, 174, 175, 177, 180],
427
+ [37, 37, 39, 39, 39, 40, 40, 40, 40, 42, 42, 42, 43, 43, 43, 45],
428
+ ]);
429
+ expect(result.desc()).toBe('152 Atk Parental Bond Kangaskhan-Mega Frustration vs. 252 HP / 152+ Def Amoonguss: 190-225 (43.9 - 52%) -- approx. 6.6% chance to 2HKO');
430
+ }
431
+ result = calculate(Pokemon('Kangaskhan-Mega', { level: 88 }), Pokemon('Amoonguss'), Move('Seismic Toss'));
432
+ expect(result.damage).toEqual([88, 88]);
433
+ expect(result.desc()).toBe('Lvl 88 Parental Bond Kangaskhan-Mega Seismic Toss vs. 0 HP Amoonguss: 176-176 (47.6 - 47.6%) -- guaranteed 3HKO');
434
+ result = calculate(Pokemon('Kangaskhan-Mega', { evs: { atk: 252 } }), Pokemon('Aggron', { level: 72 }), Move('Power-Up Punch'));
435
+ if (gen === 6) {
436
+ expect(result.desc()).toBe('252 Atk Parental Bond Kangaskhan-Mega Power-Up Punch vs. Lvl 72 0 HP / 0 Def Aggron: 248-296 (120.9 - 144.3%) -- guaranteed OHKO');
437
+ }
438
+ else {
439
+ expect(result.desc()).toBe('252 Atk Parental Bond Kangaskhan-Mega Power-Up Punch vs. Lvl 72 0 HP / 0 Def Aggron: 196-236 (95.6 - 115.1%) -- 78.9% chance to OHKO');
440
+ }
441
+ if (gen === 6)
442
+ return;
443
+ result = calculate(Pokemon('Kangaskhan-Mega', { evs: { atk: 252 } }), Pokemon('Lunala'), Move('Crunch'));
444
+ expect(result.damage).toEqual([
445
+ [188, 190, 192, 194, 196, 198, 202, 204, 206, 208, 210, 212, 214, 216, 218, 222],
446
+ [92, 96, 96, 96, 96, 100, 100, 100, 104, 104, 104, 104, 108, 108, 108, 112],
447
+ ]);
448
+ expect(result.desc()).toBe('252 Atk Parental Bond Kangaskhan-Mega Crunch vs. 0 HP / 0 Def Shadow Shield Lunala: 280-334 (67.4 - 80.4%) -- approx. 2HKO');
449
+ });
450
+ });
451
+ (0, helper_1.inGens)(6, 9, function (_a) {
452
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
453
+ test('Knock Off vs. Klutz', function () {
454
+ var weavile = Pokemon('Weavile');
455
+ var audino = Pokemon('Audino', { ability: 'Klutz', item: 'Leftovers' });
456
+ var audinoMega = Pokemon('Audino', { ability: 'Klutz', item: 'Audinite' });
457
+ var knockoff = Move('Knock Off');
458
+ var result = calculate(weavile, audino, knockoff);
459
+ expect(result.desc()).toBe('0 Atk Weavile Knock Off (97.5 BP) vs. 0 HP / 0 Def Audino: 139-165 (40 - 47.5%) -- guaranteed 3HKO');
460
+ var result2 = calculate(weavile, audinoMega, knockoff);
461
+ expect(result2.desc()).toBe('0 Atk Weavile Knock Off vs. 0 HP / 0 Def Audino: 93-111 (26.8 - 31.9%) -- guaranteed 4HKO');
462
+ });
463
+ });
464
+ (0, helper_1.inGens)(5, 9, function (_a) {
465
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
466
+ test("Multi-hit interaction with Multiscale (gen ".concat(gen, ")"), function () {
467
+ var result = calculate(Pokemon('Mamoswine'), Pokemon('Dragonite', {
468
+ ability: 'Multiscale'
469
+ }), Move('Icicle Spear'));
470
+ expect(result.range()).toEqual([360, 430]);
471
+ expect(result.desc()).toBe('0 Atk Mamoswine Icicle Spear (3 hits) vs. 0 HP / 0 Def Multiscale Dragonite: 360-430 (111.4 - 133.1%) -- guaranteed OHKO');
472
+ });
473
+ });
474
+ (0, helper_1.inGens)(5, 9, function (_a) {
475
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
476
+ test("Multi-hit interaction with Weak Armor (gen ".concat(gen, ")"), function () {
477
+ var result = calculate(Pokemon('Mamoswine'), Pokemon('Skarmory', {
478
+ ability: 'Weak Armor'
479
+ }), Move('Icicle Spear'));
480
+ expect(result.range()).toEqual([115, 138]);
481
+ expect(result.desc()).toBe('0 Atk Mamoswine Icicle Spear (3 hits) vs. 0 HP / 0 Def Weak Armor Skarmory: 115-138 (42.4 - 50.9%) -- approx. 2.7% chance to 2HKO');
482
+ result = calculate(Pokemon('Mamoswine'), Pokemon('Skarmory', {
483
+ ability: 'Weak Armor',
484
+ item: 'White Herb'
485
+ }), Move('Icicle Spear'));
486
+ expect(result.range()).toEqual([89, 108]);
487
+ expect(result.desc()).toBe('0 Atk Mamoswine Icicle Spear (3 hits) vs. 0 HP / 0 Def White Herb Weak Armor Skarmory: 89-108 (32.8 - 39.8%) -- approx. 99.9% chance to 3HKO');
488
+ result = calculate(Pokemon('Mamoswine'), Pokemon('Skarmory', {
489
+ ability: 'Weak Armor',
490
+ item: 'White Herb',
491
+ boosts: { def: 2 }
492
+ }), Move('Icicle Spear'));
493
+ expect(result.range()).toEqual([56, 69]);
494
+ expect(result.desc()).toBe('0 Atk Mamoswine Icicle Spear (3 hits) vs. +2 0 HP / 0 Def Weak Armor Skarmory: 56-69 (20.6 - 25.4%) -- approx. 0.1% chance to 4HKO');
495
+ result = calculate(Pokemon('Mamoswine', {
496
+ ability: 'Unaware'
497
+ }), Pokemon('Skarmory', {
498
+ ability: 'Weak Armor',
499
+ item: 'White Herb',
500
+ boosts: { def: 2 }
501
+ }), Move('Icicle Spear'));
502
+ expect(result.range()).toEqual([75, 93]);
503
+ expect(result.desc()).toBe('0 Atk Unaware Mamoswine Icicle Spear (3 hits) vs. 0 HP / 0 Def Skarmory: 75-93 (27.6 - 34.3%) -- approx. 1.5% chance to 3HKO');
504
+ });
505
+ });
506
+ (0, helper_1.inGens)(6, 9, function (_a) {
507
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
508
+ test("Multi-hit interaction with Mummy (gen ".concat(gen, ")"), function () {
509
+ var result = calculate(Pokemon('Pinsir-Mega'), Pokemon('Cofagrigus', {
510
+ ability: 'Mummy'
511
+ }), Move('Double Hit'));
512
+ if (gen === 6) {
513
+ expect(result.range()).toEqual([96, 113]);
514
+ expect(result.desc()).toBe('0 Atk Aerilate Pinsir-Mega Double Hit (2 hits) vs. 0 HP / 0 Def Mummy Cofagrigus: 96-113 (37.3 - 43.9%) -- approx. 3HKO');
515
+ }
516
+ else {
517
+ expect(result.range()).toEqual([91, 107]);
518
+ expect(result.desc()).toBe('0 Atk Aerilate Pinsir-Mega Double Hit (2 hits) vs. 0 HP / 0 Def Mummy Cofagrigus: 91-107 (35.4 - 41.6%) -- approx. 3HKO');
519
+ }
520
+ });
521
+ });
522
+ (0, helper_1.inGens)(7, 9, function (_a) {
523
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
524
+ test("Multi-hit interaction with Items (gen ".concat(gen, ")"), function () {
525
+ var result = calculate(Pokemon('Greninja'), Pokemon('Gliscor', {
526
+ item: 'Luminous Moss'
527
+ }), Move('Water Shuriken'));
528
+ expect(result.range()).toEqual([104, 126]);
529
+ expect(result.desc()).toBe('0 SpA Greninja Water Shuriken (15 BP) (3 hits) vs. 0 HP / 0 SpD Luminous Moss Gliscor: 104-126 (35.7 - 43.2%) -- approx. 3HKO');
530
+ result = calculate(Pokemon('Greninja'), Pokemon('Gliscor', {
531
+ ability: 'Simple',
532
+ item: 'Luminous Moss'
533
+ }), Move('Water Shuriken'));
534
+ expect(result.range()).toEqual([92, 114]);
535
+ expect(result.desc()).toBe('0 SpA Greninja Water Shuriken (15 BP) (3 hits) vs. 0 HP / 0 SpD Luminous Moss Simple Gliscor: 92-114 (31.6 - 39.1%) -- approx. 79.4% chance to 3HKO');
536
+ result = calculate(Pokemon('Greninja'), Pokemon('Gliscor', {
537
+ ability: 'Contrary',
538
+ item: 'Luminous Moss'
539
+ }), Move('Water Shuriken'));
540
+ expect(result.range()).toEqual([176, 210]);
541
+ expect(result.desc()).toBe('0 SpA Greninja Water Shuriken (15 BP) (3 hits) vs. 0 HP / 0 SpD Luminous Moss Contrary Gliscor: 176-210 (60.4 - 72.1%) -- approx. 2HKO');
542
+ });
543
+ });
544
+ (0, helper_1.inGens)(3, 9, function (_a) {
545
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
546
+ test("KOed Pokemon don't receive HP recovery after 5+ turns (gen ".concat(gen, ")"), function () {
547
+ var chansey = Pokemon('Chansey', {
548
+ level: 25
549
+ });
550
+ var mew = Pokemon('Mew', {
551
+ level: 30,
552
+ item: 'Leftovers',
553
+ ivs: { hp: 0 }
554
+ });
555
+ var seismicToss = Move('Seismic Toss');
556
+ var result = calculate(chansey, mew, seismicToss);
557
+ expect(result.damage).toBe(25);
558
+ expect(result.desc()).toBe('Lvl 25 Chansey Seismic Toss vs. Lvl 30 0 HP 0 IVs Mew: 25-25 (25 - 25%) -- guaranteed 5HKO after Leftovers recovery');
559
+ });
560
+ });
561
+ (0, helper_1.inGens)(3, 9, function (_a) {
562
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
563
+ test("KOed Pokemon don't receive HP recovery after 1-4 turns (gen ".concat(gen, ")"), function () {
564
+ var chansey = Pokemon('Chansey', {
565
+ level: 55
566
+ });
567
+ var mew = Pokemon('Mew', {
568
+ level: 30,
569
+ item: 'Leftovers',
570
+ ivs: { hp: 0 }
571
+ });
572
+ var seismicToss = Move('Seismic Toss');
573
+ var result = calculate(chansey, mew, seismicToss);
574
+ expect(result.damage).toBe(55);
575
+ expect(result.desc()).toBe('Lvl 55 Chansey Seismic Toss vs. Lvl 30 0 HP 0 IVs Mew: 55-55 (55 - 55%) -- guaranteed 2HKO after Leftovers recovery');
576
+ });
577
+ });
578
+ (0, helper_1.inGens)(3, 9, function (_a) {
579
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
580
+ test("End of turn damage is calculated correctly after 5+ turns (gen ".concat(gen, ")"), function () {
581
+ var chansey = Pokemon('Chansey', {
582
+ level: 1
583
+ });
584
+ var mew = Pokemon('Mew', {
585
+ level: 30,
586
+ status: 'tox',
587
+ toxicCounter: 1,
588
+ ivs: { hp: 0 }
589
+ });
590
+ var seismicToss = Move('Seismic Toss');
591
+ var result = calculate(chansey, mew, seismicToss);
592
+ expect(result.damage).toBe(1);
593
+ expect(result.desc()).toBe('Lvl 1 Chansey Seismic Toss vs. Lvl 30 0 HP 0 IVs Mew: 1-1 (1 - 1%) -- guaranteed 6HKO after toxic damage');
594
+ });
595
+ });
596
+ (0, helper_1.inGens)(3, 9, function (_a) {
597
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
598
+ test("End of turn damage is calculated correctly after 1-4 turns (gen ".concat(gen, ")"), function () {
599
+ var field = Field({
600
+ weather: 'Sand',
601
+ defenderSide: {
602
+ isSeeded: true
603
+ }
604
+ });
605
+ var chansey = Pokemon('Chansey', {
606
+ level: 1
607
+ });
608
+ var mew = Pokemon('Mew', {
609
+ level: 30,
610
+ status: 'tox',
611
+ toxicCounter: 1,
612
+ ivs: { hp: 0 }
613
+ });
614
+ var seismicToss = Move('Seismic Toss');
615
+ var result = calculate(chansey, mew, seismicToss, field);
616
+ expect(result.damage).toBe(1);
617
+ expect(result.desc()).toBe('Lvl 1 Chansey Seismic Toss vs. Lvl 30 0 HP 0 IVs Mew: 1-1 (1 - 1%) -- guaranteed 4HKO after sandstorm damage, Leech Seed damage, and toxic damage');
618
+ });
619
+ });
620
+ (0, helper_1.inGens)(3, 9, function (_a) {
621
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
622
+ test("End of turn damage is calculated correctly on the first turn (gen ".concat(gen, ")"), function () {
623
+ var field = Field({
624
+ weather: 'Sand'
625
+ });
626
+ var chansey = Pokemon('Chansey', {
627
+ level: 90
628
+ });
629
+ var mew = Pokemon('Mew', {
630
+ level: 30,
631
+ status: 'brn',
632
+ ivs: { hp: 0 }
633
+ });
634
+ var seismicToss = Move('Seismic Toss');
635
+ var result = calculate(chansey, mew, seismicToss, field);
636
+ expect(result.damage).toBe(90);
637
+ expect(result.desc()).toBe('Lvl 90 Chansey Seismic Toss vs. Lvl 30 0 HP 0 IVs Mew: 90-90 (90 - 90%) -- guaranteed OHKO after sandstorm damage and burn damage');
638
+ });
639
+ });
640
+ (0, helper_1.inGens)(4, 9, function (_a) {
641
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
642
+ test("Mold Breaker does not disable abilities that don't affect direct damage (gen ".concat(gen, ")"), function () {
643
+ var attacker = Pokemon('Rampardos', {
644
+ ability: 'Mold Breaker'
645
+ });
646
+ var defender = Pokemon('Blastoise', {
647
+ ability: 'Rain Dish'
648
+ });
649
+ var field = Field({
650
+ weather: 'Rain'
651
+ });
652
+ var move = Move('Stone Edge');
653
+ var result = calculate(attacker, defender, move, field);
654
+ expect(result.defender.ability).toBe('Rain Dish');
655
+ expect(result.desc()).toBe('0 Atk Rampardos Stone Edge vs. 0 HP / 0 Def Blastoise: 168-198 (56.1 - 66.2%) -- guaranteed 2HKO after Rain Dish recovery');
656
+ });
657
+ });
658
+ (0, helper_1.inGens)(8, 9, function (_a) {
659
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
660
+ test('Steely Spirit should boost Steel-type moves as a field effect.', function () {
661
+ var pokemon = Pokemon('Perrserker', {
662
+ ability: 'Battle Armor'
663
+ });
664
+ var move = Move('Iron Head');
665
+ var result = calculate(pokemon, pokemon, move);
666
+ expect(result.desc()).toBe('0 Atk Perrserker Iron Head vs. 0 HP / 0 Def Perrserker: 46-55 (16.3 - 19.5%) -- possible 6HKO');
667
+ var field = Field({ attackerSide: { isSteelySpirit: true } });
668
+ result = calculate(pokemon, pokemon, move, field);
669
+ expect(result.desc()).toBe('0 Atk Perrserker with an ally\'s Steely Spirit Iron Head vs. 0 HP / 0 Def Perrserker: 70-83 (24.9 - 29.5%) -- 99.9% chance to 4HKO');
670
+ pokemon.ability = 'Steely Spirit';
671
+ result = calculate(pokemon, pokemon, move, field);
672
+ expect(result.desc()).toBe('0 Atk Steely Spirit Perrserker with an ally\'s Steely Spirit Iron Head vs. 0 HP / 0 Def Perrserker: 105-124 (37.3 - 44.1%) -- guaranteed 3HKO');
673
+ });
674
+ });
675
+ });
676
+ describe('Gen 1', function () {
677
+ (0, helper_1.inGen)(1, function (_a) {
678
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
679
+ test('Basic: Gengar vs. Chansey', function () {
680
+ var result = calculate(Pokemon('Gengar'), Pokemon('Chansey'), Move('Thunderbolt'));
681
+ expect(result.range()).toEqual([79, 94]);
682
+ expect(result.desc()).toBe('Gengar Thunderbolt vs. Chansey: 79-94 (11.2 - 13.3%) -- possible 8HKO');
683
+ });
684
+ test('Light Screen', function () {
685
+ var field = Field({ defenderSide: { isLightScreen: true } });
686
+ var result = calculate(Pokemon('Gengar'), Pokemon('Vulpix'), Move('Surf'), field);
687
+ expect(result.desc()).toBe('Gengar Surf vs. Vulpix through Light Screen: 108-128 (38.7 - 45.8%) -- guaranteed 3HKO');
688
+ });
689
+ });
690
+ });
691
+ describe('Gen 2', function () {
692
+ (0, helper_1.inGen)(2, function (_a) {
693
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
694
+ test('Basic: Gengar vs. Chansey', function () {
695
+ var result = calculate(Pokemon('Gengar'), Pokemon('Chansey', { item: 'Leftovers' }), Move('Dynamic Punch'));
696
+ expect(result.range()).toEqual([304, 358]);
697
+ expect(result.desc()).toBe('Gengar Dynamic Punch vs. Chansey: 304-358 (43.2 - 50.9%) -- guaranteed 3HKO after Leftovers recovery');
698
+ });
699
+ test('Struggle', function () {
700
+ var attacker = Pokemon('Skarmory', { boosts: { atk: 6, def: 6 } });
701
+ var defender = Pokemon('Skarmory', { boosts: { atk: 6, def: 6 } });
702
+ var move = Move('Struggle');
703
+ var result = calculate(attacker, defender, move);
704
+ expect(result.range()).toEqual([37, 44]);
705
+ expect(result.desc()).toBe('+6 Skarmory Struggle vs. +6 Skarmory: 37-44 (11.1 - 13.2%) -- possible 8HKO');
706
+ });
707
+ test('Present', function () {
708
+ var attacker = Pokemon('Togepi', { level: 5, boosts: { atk: -6 }, status: 'brn' });
709
+ var defender = Pokemon('Umbreon', { boosts: { def: 6 } });
710
+ var move = Move('Present');
711
+ var field = Field({ defenderSide: { isReflect: true } });
712
+ var result = calculate(attacker, defender, move, field);
713
+ expect(result.range()).toEqual([125, 147]);
714
+ expect(result.desc()).toBe('-6 Lvl 5 burned Togepi Present vs. +6 Umbreon through Reflect: 125-147 (31.8 - 37.4%) -- 89.1% chance to 3HKO');
715
+ });
716
+ test('DVs', function () {
717
+ var aerodactyl = Pokemon('Aerodactyl');
718
+ var zapdos = Pokemon('Zapdos', { ivs: { atk: 29, def: 27 }, item: 'Leftovers' });
719
+ expect(zapdos.ivs.hp).toBe(14);
720
+ var move = Move('Ancient Power');
721
+ var result = calculate(aerodactyl, zapdos, move);
722
+ expect(result.range()).toEqual([153, 180]);
723
+ expect(result.desc()).toBe('Aerodactyl Ancient Power vs. Zapdos: 153-180 (41.6 - 49%) -- guaranteed 3HKO after Leftovers recovery');
724
+ });
725
+ });
726
+ });
727
+ describe('Gen 3', function () {
728
+ (0, helper_1.inGen)(3, function (_a) {
729
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
730
+ test('Basic: Gengar vs. Chansey', function () {
731
+ var result = calculate(Pokemon('Gengar', {
732
+ nature: 'Mild',
733
+ evs: { atk: 100 }
734
+ }), Pokemon('Chansey', {
735
+ item: 'Leftovers',
736
+ nature: 'Bold',
737
+ evs: { hp: 252, def: 252 }
738
+ }), Move('Focus Punch'));
739
+ expect(result.range()).toEqual([346, 408]);
740
+ expect(result.desc()).toBe('100 Atk Gengar Focus Punch vs. 252 HP / 252+ Def Chansey: 346-408 (49.1 - 57.9%) -- 59% chance to 2HKO after Leftovers recovery');
741
+ });
742
+ test('Water Absorb', function () {
743
+ var cacturne = Pokemon('Cacturne', {
744
+ ability: 'Sand Veil'
745
+ });
746
+ var blastoise = Pokemon('Blastoise', {
747
+ evs: { spa: 252 }
748
+ });
749
+ var surf = Move('Surf');
750
+ var result = calculate(blastoise, cacturne, surf);
751
+ expect(result.range()).toEqual([88, 104]);
752
+ expect(result.desc()).toBe('252 SpA Blastoise Surf vs. 0 HP / 0 SpD Cacturne: 88-104 (31.3 - 37%) -- 76.6% chance to 3HKO');
753
+ cacturne.ability = 'Water Absorb';
754
+ result = calculate(blastoise, cacturne, surf);
755
+ expect(result.damage).toBe(0);
756
+ });
757
+ describe('Spread Moves', function () {
758
+ test('allAdjacent', function () {
759
+ var gengar = Pokemon('Gengar', { nature: 'Mild', evs: { atk: 100 } });
760
+ var blissey = Pokemon('Chansey', {
761
+ item: 'Leftovers',
762
+ nature: 'Bold',
763
+ evs: { hp: 252, def: 252 }
764
+ });
765
+ var field = Field({ gameType: 'Doubles' });
766
+ var result = calculate(gengar, blissey, Move('Explosion'), field);
767
+ expect(result.range()).toEqual([578, 681]);
768
+ expect(result.desc()).toBe('100 Atk Gengar Explosion vs. 252 HP / 252+ Def Chansey: 578-681 (82.1 - 96.7%) -- guaranteed 2HKO after Leftovers recovery');
769
+ });
770
+ test('allAdjacentFoes', function () {
771
+ var gengar = Pokemon('Gengar', { nature: 'Modest', evs: { spa: 252 } });
772
+ var blissey = Pokemon('Chansey', {
773
+ item: 'Leftovers',
774
+ nature: 'Bold',
775
+ evs: { hp: 252, def: 252 }
776
+ });
777
+ var field = Field({ gameType: 'Doubles' });
778
+ var result = calculate(gengar, blissey, Move('Blizzard'), field);
779
+ expect(result.range()).toEqual([69, 82]);
780
+ expect(result.desc()).toBe('252+ SpA Gengar Blizzard vs. 252 HP / 0 SpD Chansey: 69-82 (9.8 - 11.6%)');
781
+ });
782
+ });
783
+ });
784
+ });
785
+ describe('Gen 4', function () {
786
+ (0, helper_1.inGen)(4, function (_a) {
787
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
788
+ test('Basic: Gengar vs. Chansey', function () {
789
+ var result = calculate(Pokemon('Gengar', {
790
+ item: 'Choice Specs',
791
+ nature: 'Timid',
792
+ evs: { spa: 252 },
793
+ boosts: { spa: 1 }
794
+ }), Pokemon('Chansey', {
795
+ item: 'Leftovers',
796
+ nature: 'Calm',
797
+ evs: { hp: 252, spd: 252 }
798
+ }), Move('Focus Blast'));
799
+ expect(result.range()).toEqual([408, 482]);
800
+ expect(result.desc()).toBe('+1 252 SpA Choice Specs Gengar Focus Blast vs. 252 HP / 252+ SpD Chansey: 408-482 (57.9 - 68.4%) -- guaranteed 2HKO after Leftovers recovery');
801
+ });
802
+ test('Mold Breaker', function () {
803
+ var pinsir = Pokemon('Pinsir', {
804
+ item: 'Choice Band',
805
+ nature: 'Adamant',
806
+ ability: 'Hyper Cutter',
807
+ evs: { atk: 252 }
808
+ });
809
+ var gengar = Pokemon('Gengar', {
810
+ item: 'Choice Specs',
811
+ nature: 'Timid',
812
+ evs: { spa: 252 },
813
+ boosts: { spa: 1 }
814
+ });
815
+ var earthquake = Move('Earthquake');
816
+ var result = calculate(pinsir, gengar, earthquake);
817
+ expect(result.damage).toBe(0);
818
+ pinsir.ability = 'Mold Breaker';
819
+ result = calculate(pinsir, gengar, earthquake);
820
+ expect(result.range()).toEqual([528, 622]);
821
+ expect(result.desc()).toBe('252+ Atk Choice Band Mold Breaker Pinsir Earthquake vs. 0 HP / 0 Def Gengar: 528-622 (202.2 - 238.3%) -- guaranteed OHKO');
822
+ pinsir.boosts.atk = 2;
823
+ gengar.ability = 'Unaware';
824
+ result = calculate(pinsir, gengar, earthquake);
825
+ expect(result.range()).toEqual([1054, 1240]);
826
+ });
827
+ });
828
+ });
829
+ describe('Gen 5', function () {
830
+ (0, helper_1.inGen)(5, function (_a) {
831
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
832
+ test('Basic: Gengar vs. Chansey', function () {
833
+ var result = calculate(Pokemon('Gengar', {
834
+ item: 'Choice Specs',
835
+ nature: 'Timid',
836
+ evs: { spa: 252 },
837
+ boosts: { spa: 1 }
838
+ }), Pokemon('Chansey', {
839
+ item: 'Eviolite',
840
+ nature: 'Calm',
841
+ evs: { hp: 252, spd: 252 }
842
+ }), Move('Focus Blast'));
843
+ expect(result.range()).toEqual([274, 324]);
844
+ expect(result.fullDesc('px')).toBe('+1 252 SpA Choice Specs Gengar Focus Blast vs. 252 HP / 252+ SpD Eviolite Chansey: 274-324 (18 - 22px) -- guaranteed 3HKO');
845
+ });
846
+ test('Technician with Low Kick', function () {
847
+ var ambipom = Pokemon('Ambipom', { level: 50, ability: 'Technician' });
848
+ var blissey = Pokemon('Blissey', { level: 50, evs: { hp: 252 } });
849
+ var result = calculate(ambipom, blissey, Move('Low Kick'));
850
+ expect(result.range()).toEqual([272, 320]);
851
+ expect(result.desc()).toBe('0 Atk Technician Ambipom Low Kick (60 BP) vs. 252 HP / 0 Def Blissey: 272-320 (75.1 - 88.3%) -- guaranteed 2HKO');
852
+ var aggron = Pokemon('Aggron', { level: 50, evs: { hp: 252 } });
853
+ result = calculate(ambipom, aggron, Move('Low Kick'));
854
+ expect(result.range()).toEqual([112, 132]);
855
+ expect(result.desc()).toBe('0 Atk Ambipom Low Kick (120 BP) vs. 252 HP / 0 Def Aggron: 112-132 (63.2 - 74.5%) -- guaranteed 2HKO');
856
+ });
857
+ });
858
+ });
859
+ describe('Gen 6', function () {
860
+ (0, helper_1.inGen)(6, function (_a) {
861
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
862
+ test('Basic: Gengar vs. Chansey', function () {
863
+ var result = calculate(Pokemon('Gengar', {
864
+ item: 'Life Orb',
865
+ nature: 'Modest',
866
+ evs: { spa: 252 }
867
+ }), Pokemon('Chansey', {
868
+ item: 'Eviolite',
869
+ nature: 'Bold',
870
+ evs: { hp: 252, def: 252 }
871
+ }), Move('Sludge Bomb'));
872
+ expect(result.range()).toEqual([134, 160]);
873
+ expect(result.desc()).toBe('252+ SpA Life Orb Gengar Sludge Bomb vs. 252 HP / 0 SpD Eviolite Chansey: 134-160 (19 - 22.7%) -- possible 5HKO');
874
+ });
875
+ });
876
+ });
877
+ describe('Gen 7', function () {
878
+ (0, helper_1.inGen)(7, function (_a) {
879
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
880
+ var abomasnow = Pokemon('Abomasnow', {
881
+ item: 'Icy Rock',
882
+ ability: 'Snow Warning',
883
+ nature: 'Hasty',
884
+ evs: { atk: 252, spd: 4, spe: 252 }
885
+ });
886
+ var hoopa = Pokemon('Hoopa-Unbound', {
887
+ item: 'Choice Band',
888
+ ability: 'Magician',
889
+ nature: 'Jolly',
890
+ evs: { hp: 32, atk: 224, spe: 252 }
891
+ });
892
+ test('Basic: Gengar vs. Chansey', function () {
893
+ var result = calculate(Pokemon('Gengar', {
894
+ item: 'Life Orb',
895
+ nature: 'Modest',
896
+ evs: { spa: 252 },
897
+ boosts: { spa: 3 }
898
+ }), Pokemon('Chansey', {
899
+ item: 'Eviolite',
900
+ nature: 'Bold',
901
+ evs: { hp: 100, spd: 100 },
902
+ boosts: { spd: 1 }
903
+ }), Move('Sludge Bomb'));
904
+ expect(result.range()).toEqual([204, 242]);
905
+ expect(result.desc()).toBe('+3 252+ SpA Life Orb Gengar Sludge Bomb vs. +1 100 HP / 100 SpD Eviolite Chansey: 204-242 (30.6 - 36.3%) -- 52.9% chance to 3HKO');
906
+ });
907
+ test('Z-Move critical hits', function () {
908
+ var zMove = Move('Wood Hammer', { useZ: true, isCrit: true });
909
+ var result = calculate(abomasnow, hoopa, zMove);
910
+ expect(result.range()).toEqual([555, 654]);
911
+ expect(result.desc()).toBe('252 Atk Abomasnow Bloom Doom (190 BP) vs. 32 HP / 0 Def Hoopa-Unbound on a critical hit: 555-654 (179.6 - 211.6%) -- guaranteed OHKO');
912
+ });
913
+ test('Recoil & Recovery', function () {
914
+ var result = calculate(abomasnow, hoopa, Move('Wood Hammer'));
915
+ expect(result.range()).toEqual([234, 276]);
916
+ expect(result.desc()).toBe('252 Atk Abomasnow Wood Hammer vs. 32 HP / 0 Def Hoopa-Unbound: 234-276 (75.7 - 89.3%) -- guaranteed 2HKO');
917
+ var recoil = result.recoil();
918
+ expect(recoil.recoil).toEqual([24, 28.3]);
919
+ expect(recoil.text).toBe('24 - 28.3% recoil damage');
920
+ result = calculate(hoopa, abomasnow, Move('Drain Punch'));
921
+ expect(result.range()).toEqual([398, 470]);
922
+ expect(result.desc()).toBe('224 Atk Choice Band Hoopa-Unbound Drain Punch vs. 0 HP / 0- Def Abomasnow: 398-470 (123.9 - 146.4%) -- guaranteed OHKO');
923
+ var recovery = result.recovery();
924
+ expect(recovery.recovery).toEqual([161, 161]);
925
+ expect(recovery.text).toBe('52.1 - 52.1% recovered');
926
+ });
927
+ test('Big Root', function () {
928
+ var bigRoot = Pokemon('Blissey', { item: 'Big Root' });
929
+ var result = calculate(bigRoot, abomasnow, Move('Drain Punch'));
930
+ expect(result.range()).toEqual([38, 46]);
931
+ expect(result.recovery().recovery).toEqual([24, 29]);
932
+ });
933
+ test('Loaded Field', function () {
934
+ var field = Field({
935
+ gameType: 'Doubles',
936
+ terrain: 'Grassy',
937
+ weather: 'Hail',
938
+ defenderSide: {
939
+ isSR: true,
940
+ spikes: 1,
941
+ isLightScreen: true,
942
+ isSeeded: true,
943
+ isFriendGuard: true
944
+ },
945
+ attackerSide: {
946
+ isHelpingHand: true,
947
+ isTailwind: true
948
+ }
949
+ });
950
+ var result = calculate(abomasnow, hoopa, Move('Blizzard'), field);
951
+ expect(result.range()).toEqual([50, 59]);
952
+ expect(result.desc()).toBe('0 SpA Abomasnow Helping Hand Blizzard vs. 32 HP / 0 SpD Hoopa-Unbound through Light Screen with an ally\'s Friend Guard: 50-59 (16.1 - 19%)' +
953
+ ' -- guaranteed 3HKO after Stealth Rock, 1 layer of Spikes, hail damage, Leech Seed damage, and Grassy Terrain recovery');
954
+ });
955
+ test('Wring Out', function () {
956
+ var smeargle = Pokemon('Smeargle', { level: 50, ability: 'Technician' });
957
+ var blissey = Pokemon('Blissey', { level: 50, evs: { hp: 252 }, curHP: 184 });
958
+ var result = calculate(smeargle, blissey, Move('Wring Out'));
959
+ expect(result.range()).toEqual([15, 18]);
960
+ expect(result.desc()).toBe('0 SpA Technician Smeargle Wring Out (60 BP) vs. 252 HP / 0 SpD Blissey: 15-18 (4.1 - 4.9%)');
961
+ });
962
+ test('Mold Breaker', function () {
963
+ var pinsir = Pokemon('Pinsir', {
964
+ item: 'Choice Band',
965
+ nature: 'Adamant',
966
+ ability: 'Hyper Cutter',
967
+ evs: { atk: 252 }
968
+ });
969
+ var gengar = Pokemon('Gengar', {
970
+ item: 'Choice Specs',
971
+ nature: 'Timid',
972
+ ability: 'Levitate',
973
+ evs: { spa: 252 },
974
+ boosts: { spa: 1 }
975
+ });
976
+ var earthquake = Move('Earthquake');
977
+ var result = calculate(pinsir, gengar, earthquake);
978
+ expect(result.damage).toBe(0);
979
+ pinsir.ability = 'Mold Breaker';
980
+ result = calculate(pinsir, gengar, earthquake);
981
+ expect(result.range()).toEqual([528, 622]);
982
+ expect(result.desc()).toBe('252+ Atk Choice Band Mold Breaker Pinsir Earthquake vs. 0 HP / 0 Def Gengar: 528-622 (202.2 - 238.3%) -- guaranteed OHKO');
983
+ pinsir.boosts.atk = 2;
984
+ gengar.ability = 'Unaware';
985
+ result = calculate(pinsir, gengar, earthquake);
986
+ expect(result.range()).toEqual([1054, 1240]);
987
+ });
988
+ test('16-bit Overflow', function () {
989
+ var result = calculate(Pokemon('Mewtwo-Mega-Y', { evs: { spa: 196 } }), Pokemon('Wynaut', { level: 1, boosts: { spd: -6 } }), Move('Fire Blast'), Field({ attackerSide: { isHelpingHand: true } }));
990
+ expect(result.damage).toEqual([
991
+ 55725, 56380, 57036, 57691,
992
+ 58347, 59003, 59658, 60314,
993
+ 60969, 61625, 62281, 62936,
994
+ 63592, 64247, 64903, 23,
995
+ ]);
996
+ });
997
+ test('32-bit Overflow', function () {
998
+ var kyogre = Pokemon('Kyogre', {
999
+ ability: 'Water Bubble',
1000
+ item: 'Choice Specs',
1001
+ curHP: 340,
1002
+ ivs: { spa: 6 },
1003
+ boosts: { spa: 6 }
1004
+ });
1005
+ var wynaut = Pokemon('Wynaut', { level: 1, boosts: { spd: -6 } });
1006
+ var waterSpout = Move('Water Spout');
1007
+ var field = Field({ weather: 'Rain', attackerSide: { isHelpingHand: true } });
1008
+ expect(calculate(kyogre, wynaut, waterSpout, field).range()).toEqual([55, 66]);
1009
+ kyogre = Pokemon('Kyogre', __assign(__assign({}, kyogre), { overrides: { types: ['Normal'] } }));
1010
+ expect(calculate(kyogre, wynaut, waterSpout, field).range()).toEqual([37, 44]);
1011
+ });
1012
+ });
1013
+ });
1014
+ describe('Gen 8', function () {
1015
+ (0, helper_1.inGen)(8, function (_a) {
1016
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
1017
+ test('Basic: Gengar vs. Chansey', function () {
1018
+ var result = calculate(Pokemon('Gengar', {
1019
+ item: 'Life Orb',
1020
+ nature: 'Modest',
1021
+ evs: { spa: 252 },
1022
+ boosts: { spa: 3 }
1023
+ }), Pokemon('Chansey', {
1024
+ item: 'Eviolite',
1025
+ nature: 'Bold',
1026
+ evs: { hp: 100, spd: 100 },
1027
+ boosts: { spd: 1 }
1028
+ }), Move('Sludge Bomb'));
1029
+ expect(result.range()).toEqual([204, 242]);
1030
+ expect(result.desc()).toBe('+3 252+ SpA Life Orb Gengar Sludge Bomb vs. +1 100 HP / 100 SpD Eviolite Chansey: 204-242 (30.6 - 36.3%) -- 52.9% chance to 3HKO');
1031
+ });
1032
+ test('Knock Off vs. Silvally', function () {
1033
+ var sawk = Pokemon('Sawk', { ability: 'Mold Breaker', evs: { atk: 252 } });
1034
+ var silvally = Pokemon('Silvally-Dark', { item: 'Dark Memory' });
1035
+ var knockoff = Move('Knock Off');
1036
+ var result = calculate(sawk, silvally, knockoff);
1037
+ expect(result.desc()).toBe('252 Atk Sawk Knock Off vs. 0 HP / 0 Def Silvally-Dark: 36-43 (10.8 - 12.9%) -- possible 8HKO');
1038
+ });
1039
+ test('-ate Abilities', function () {
1040
+ var sylveon = Pokemon('Sylveon', { ability: 'Pixilate', evs: { spa: 252 } });
1041
+ var silvally = Pokemon('Silvally');
1042
+ var hypervoice = Move('Hyper Voice');
1043
+ var result = calculate(sylveon, silvally, hypervoice);
1044
+ expect(result.desc()).toBe('252 SpA Pixilate Sylveon Hyper Voice vs. 0 HP / 0 SpD Silvally: 165-195 (49.8 - 58.9%) -- 99.6% chance to 2HKO');
1045
+ });
1046
+ test('% chance to OHKO', function () {
1047
+ var abomasnow = Pokemon('Abomasnow', {
1048
+ level: 55,
1049
+ item: 'Choice Specs',
1050
+ evs: { spa: 252 }
1051
+ });
1052
+ var deerling = Pokemon('Deerling', { evs: { hp: 36 } });
1053
+ var blizzard = Move('Blizzard');
1054
+ var hail = Field({ weather: 'Hail' });
1055
+ var result = calculate(abomasnow, deerling, blizzard, hail);
1056
+ expect(result.desc()).toBe('Lvl 55 252 SpA Choice Specs Abomasnow Blizzard vs. 36 HP / 0 SpD Deerling: 236-278 (87.4 - 102.9%) -- 25% chance to OHKO (56.3% chance to OHKO after hail damage)');
1057
+ });
1058
+ test('% chance to OHKO with Leftovers', function () {
1059
+ var kyurem = Pokemon('Kyurem', {
1060
+ level: 100,
1061
+ item: 'Choice Specs',
1062
+ evs: { spa: 252 }
1063
+ });
1064
+ var jirachi = Pokemon('Jirachi', { item: 'Leftovers' });
1065
+ var earthpower = Move('Earth Power');
1066
+ var result = calculate(kyurem, jirachi, earthpower);
1067
+ expect(result.desc()).toBe('252 SpA Choice Specs Kyurem Earth Power vs. 0 HP / 0 SpD Jirachi: 294-348 (86.2 - 102%) -- 12.5% chance to OHKO');
1068
+ });
1069
+ test('Technician with Low Kick', function () {
1070
+ var ambipom = Pokemon('Ambipom', { level: 50, ability: 'Technician' });
1071
+ var blissey = Pokemon('Blissey', { level: 50, evs: { hp: 252 } });
1072
+ var result = calculate(ambipom, blissey, Move('Low Kick'));
1073
+ expect(result.range()).toEqual([272, 320]);
1074
+ expect(result.desc()).toBe('0 Atk Technician Ambipom Low Kick (60 BP) vs. 252 HP / 0 Def Blissey: 272-320 (75.1 - 88.3%) -- guaranteed 2HKO');
1075
+ var aggron = Pokemon('Aggron', { level: 50, evs: { hp: 252 } });
1076
+ result = calculate(ambipom, aggron, Move('Low Kick'));
1077
+ expect(result.range()).toEqual([112, 132]);
1078
+ expect(result.desc()).toBe('0 Atk Ambipom Low Kick (120 BP) vs. 252 HP / 0 Def Aggron: 112-132 (63.2 - 74.5%) -- guaranteed 2HKO');
1079
+ });
1080
+ });
1081
+ });
1082
+ describe('Gen 9', function () {
1083
+ (0, helper_1.inGen)(9, function (_a) {
1084
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
1085
+ test('Supreme Overlord', function () {
1086
+ var kingambit = Pokemon('Kingambit', { level: 100, ability: 'Supreme Overlord', alliesFainted: 0 });
1087
+ var aggron = Pokemon('Aggron', { level: 100 });
1088
+ var result = calculate(kingambit, aggron, Move('Iron Head'));
1089
+ expect(result.range()).toEqual([67, 79]);
1090
+ expect(result.desc()).toBe('0 Atk Kingambit Iron Head vs. 0 HP / 0 Def Aggron: 67-79 (23.8 - 28.1%) -- 91.2% chance to 4HKO');
1091
+ kingambit.alliesFainted = 5;
1092
+ result = calculate(kingambit, aggron, Move('Iron Head'));
1093
+ expect(result.range()).toEqual([100, 118]);
1094
+ expect(result.desc()).toBe('0 Atk Supreme Overlord 5 allies fainted Kingambit Iron Head vs. 0 HP / 0 Def Aggron: 100-118 (35.5 - 41.9%) -- guaranteed 3HKO');
1095
+ kingambit.alliesFainted = 10;
1096
+ result = calculate(kingambit, aggron, Move('Iron Head'));
1097
+ expect(result.range()).toEqual([100, 118]);
1098
+ expect(result.desc()).toBe('0 Atk Supreme Overlord 5 allies fainted Kingambit Iron Head vs. 0 HP / 0 Def Aggron: 100-118 (35.5 - 41.9%) -- guaranteed 3HKO');
1099
+ });
1100
+ test('Electro Drift/Collision Course boost on Super Effective hits', function () {
1101
+ var attacker = Pokemon('Arceus');
1102
+ var defender = Pokemon('Mew');
1103
+ var calc = function (move) {
1104
+ if (move === void 0) { move = Move('Electro Drift'); }
1105
+ return calculate(attacker, defender, move).range();
1106
+ };
1107
+ var neutral = calc();
1108
+ var fusionBolt = Move('Fusion Bolt');
1109
+ expect(calc(fusionBolt)).toEqual(neutral);
1110
+ defender = Pokemon('Manaphy');
1111
+ var se = calc();
1112
+ expect(calc(fusionBolt)).not.toEqual(se);
1113
+ defender.teraType = 'Normal';
1114
+ expect(calc()).toEqual(neutral);
1115
+ var cc = Move('Collision Course');
1116
+ defender = Pokemon('Jirachi');
1117
+ expect(calc(cc)).toEqual(neutral);
1118
+ defender.teraType = 'Normal';
1119
+ expect(calc(cc)).toEqual(se);
1120
+ });
1121
+ function testQP(ability, field) {
1122
+ test("".concat(ability, " should take into account boosted stats by default"), function () {
1123
+ var attacker = Pokemon('Iron Leaves', { ability: ability, boostedStat: 'auto', boosts: { spa: 6 } });
1124
+ var defender = Pokemon('Iron Treads', { ability: ability, boostedStat: 'auto', boosts: { spd: 6 } });
1125
+ var result = calculate(attacker, defender, Move('Leaf Storm'), Field(field)).rawDesc;
1126
+ expect(result.attackerAbility).toBe(ability);
1127
+ expect(result.defenderAbility).toBe(ability);
1128
+ result = calculate(attacker, defender, Move('Psyblade'), Field(field)).rawDesc;
1129
+ expect(result.attackerAbility).toBeUndefined();
1130
+ expect(result.defenderAbility).toBeUndefined();
1131
+ });
1132
+ }
1133
+ function testQPOverride(ability, field) {
1134
+ test("".concat(ability, " should be able to be overridden with boostedStat"), function () {
1135
+ var attacker = Pokemon('Flutter Mane', { ability: ability, boostedStat: 'atk', boosts: { spa: 6 } });
1136
+ var defender = Pokemon('Walking Wake', { ability: ability, boostedStat: 'def', boosts: { spd: 6 } });
1137
+ var result = calculate(attacker, defender, Move('Leaf Storm'), Field(field)).rawDesc;
1138
+ expect(result.attackerAbility).toBeUndefined();
1139
+ expect(result.defenderAbility).toBeUndefined();
1140
+ result = calculate(attacker, defender, Move('Psyblade'), Field(field)).rawDesc;
1141
+ expect(result.attackerAbility).toBe(ability);
1142
+ expect(result.defenderAbility).toBe(ability);
1143
+ });
1144
+ }
1145
+ testQP('Quark Drive', { terrain: 'Electric' });
1146
+ testQP('Protosynthesis', { weather: 'Sun' });
1147
+ testQPOverride('Quark Drive', { terrain: 'Electric' });
1148
+ testQPOverride('Protosynthesis', { weather: 'Sun' });
1149
+ test('Meteor Beam/Electro Shot', function () {
1150
+ var defender = Pokemon('Arceus');
1151
+ var testCase = function (options, expected) {
1152
+ var result = calculate(Pokemon('Archaludon', options), defender, Move('Meteor Beam'));
1153
+ expect(result.attacker.boosts.spa).toBe(expected);
1154
+ result = calculate(Pokemon('Archaludon', options), defender, Move('Electro Shot'));
1155
+ expect(result.attacker.boosts.spa).toBe(expected);
1156
+ };
1157
+ testCase({}, 1);
1158
+ testCase({ boosts: { spa: 6 } }, 6);
1159
+ testCase({ ability: 'Simple' }, 2);
1160
+ testCase({ ability: 'Contrary' }, -1);
1161
+ });
1162
+ test('Revelation Dance should change type if Pokemon Terastallized', function () {
1163
+ var attacker = Pokemon('Oricorio-Pom-Pom');
1164
+ var defender = Pokemon('Sandaconda');
1165
+ var result = calculate(attacker, defender, Move('Revelation Dance'));
1166
+ expect(result.move.type).toBe('Electric');
1167
+ attacker.teraType = 'Water';
1168
+ result = calculate(attacker, defender, Move('Revelation Dance'));
1169
+ expect(result.move.type).toBe('Water');
1170
+ });
1171
+ test('Flower Gift, Power Spot, Battery, and switching boosts shouldn\'t have double spaces', function () {
1172
+ var attacker = Pokemon('Weavile');
1173
+ var defender = Pokemon('Vulpix');
1174
+ var field = Field({
1175
+ weather: 'Sun',
1176
+ attackerSide: {
1177
+ isFlowerGift: true,
1178
+ isPowerSpot: true
1179
+ },
1180
+ defenderSide: {
1181
+ isSwitching: 'out'
1182
+ }
1183
+ });
1184
+ var result = calculate(attacker, defender, Move('Pursuit'), field);
1185
+ expect(result.desc()).toBe("0 Atk Weavile with an ally's Flower Gift Power Spot boosted switching boosted Pursuit (80 BP) vs. 0 HP / 0 Def Vulpix in Sun: 399-469 (183.8 - 216.1%) -- guaranteed OHKO");
1186
+ });
1187
+ test('Wind Rider should give an Attack boost in Tailwind', function () {
1188
+ var attacker = Pokemon('Brambleghast', { 'ability': 'Wind Rider' });
1189
+ var defender = Pokemon('Brambleghast', { 'ability': 'Wind Rider' });
1190
+ var field = Field({
1191
+ attackerSide: {
1192
+ isTailwind: true
1193
+ }
1194
+ });
1195
+ var result = calculate(attacker, defender, Move('Power Whip'), field);
1196
+ expect(attacker.boosts.atk).toBe(0);
1197
+ expect(result.attacker.boosts.atk).toBe(1);
1198
+ });
1199
+ describe('Tera Stellar', function () {
1200
+ var terastal = Pokemon('Arceus', { teraType: 'Stellar' });
1201
+ var control = Pokemon('Arceus');
1202
+ test('should only be displayed on defender for Stellar attacks', function () {
1203
+ expect(calculate(control, terastal, Move('Tera Blast'))
1204
+ .rawDesc
1205
+ .defenderTera).toBeUndefined();
1206
+ expect(calculate(terastal, terastal, Move('Tera Blast'))
1207
+ .rawDesc
1208
+ .defenderTera).toBeDefined();
1209
+ expect(calculate(terastal, terastal, Move('Tera Blast', { isStellarFirstUse: true }))
1210
+ .rawDesc
1211
+ .defenderTera).toBeDefined();
1212
+ expect(calculate(control, terastal, Move('Tera Blast', { isStellarFirstUse: true }))
1213
+ .rawDesc
1214
+ .defenderTera).toBeUndefined();
1215
+ });
1216
+ test('should not be displayed for non-boosted attacks', function () { return expect(calculate(terastal, control, Move('Judgment', { isStellarFirstUse: false }))
1217
+ .rawDesc
1218
+ .attackerTera).toBeUndefined(); });
1219
+ test('should distinguish between first use for Tera Blast', function () {
1220
+ var result = [true, false].map(function (isStellarFirstUse) {
1221
+ var _ = [];
1222
+ for (var _i = 1; _i < arguments.length; _i++) {
1223
+ _[_i - 1] = arguments[_i];
1224
+ }
1225
+ return calculate(terastal, control, Move('Tera Blast', { isStellarFirstUse: isStellarFirstUse }))
1226
+ .rawDesc
1227
+ .isStellarFirstUse;
1228
+ });
1229
+ expect(result[0]).not.toEqual(result[1]);
1230
+ });
1231
+ });
1232
+ });
1233
+ describe('Descriptions', function () {
1234
+ (0, helper_1.inGen)(9, function (_a) {
1235
+ var gen = _a.gen, calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move;
1236
+ test('displayed chances should not round to 100%', function () {
1237
+ var result = calculate(Pokemon('Xerneas', { item: 'Choice Band', nature: 'Adamant', evs: { atk: 252 } }), Pokemon('Necrozma-Dusk-Mane', { nature: 'Impish', evs: { hp: 252, def: 252 } }), Move('Close Combat'));
1238
+ expect(result.kochance().chance).toBeGreaterThanOrEqual(0.9995);
1239
+ expect(result.kochance().text).toBe('99.9% chance to 3HKO');
1240
+ });
1241
+ test('displayed chances should not round to 0%', function () {
1242
+ var result = calculate(Pokemon('Deoxys-Attack', { evs: { spa: 44 } }), Pokemon('Blissey', { nature: 'Calm', evs: { hp: 252, spd: 252 } }), Move('Psycho Boost'));
1243
+ expect(result.kochance().chance).toBeLessThan(0.005);
1244
+ expect(result.kochance().text).toBe('0.1% chance to 4HKO');
1245
+ });
1246
+ });
1247
+ });
1248
+ describe('Some moves should break screens before doing damage', function () {
1249
+ (0, helper_1.inGens)(3, 9, function (_a) {
1250
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
1251
+ test('Brick Break should break screens', function () {
1252
+ var pokemon = Pokemon('Mew');
1253
+ var brickBreak = Move('Brick Break');
1254
+ var otherMove = Move('Vital Throw', { overrides: { basePower: 75 } });
1255
+ var field = Field({ defenderSide: { isReflect: true } });
1256
+ var brickBreakResult = calculate(pokemon, pokemon, brickBreak, field);
1257
+ expect(brickBreakResult.field.defenderSide.isReflect).toBe(false);
1258
+ var otherMoveResult = calculate(pokemon, pokemon, otherMove, field);
1259
+ expect(otherMoveResult.field.defenderSide.isReflect).toBe(true);
1260
+ expect(brickBreakResult.range()[0]).toBeGreaterThan(otherMoveResult.range()[0]);
1261
+ expect(brickBreakResult.range()[1]).toBeGreaterThan(otherMoveResult.range()[1]);
1262
+ });
1263
+ });
1264
+ (0, helper_1.inGens)(7, 9, function (_a) {
1265
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
1266
+ test('Psychic Fangs should break screens', function () {
1267
+ var pokemon = Pokemon('Mew');
1268
+ var psychicFangs = Move('Psychic Fangs');
1269
+ var otherMove = Move('Zen Headbutt', { overrides: { basePower: 75 } });
1270
+ var field = Field({ defenderSide: { isReflect: true } });
1271
+ var psychicFangsResult = calculate(pokemon, pokemon, psychicFangs, field);
1272
+ expect(psychicFangsResult.field.defenderSide.isReflect).toBe(false);
1273
+ var otherMoveResult = calculate(pokemon, pokemon, otherMove, field);
1274
+ expect(otherMoveResult.field.defenderSide.isReflect).toBe(true);
1275
+ expect(psychicFangsResult.range()[0]).toBeGreaterThan(otherMoveResult.range()[0]);
1276
+ expect(psychicFangsResult.range()[1]).toBeGreaterThan(otherMoveResult.range()[1]);
1277
+ });
1278
+ });
1279
+ (0, helper_1.inGen)(9, function (_a) {
1280
+ var calculate = _a.calculate, Pokemon = _a.Pokemon, Move = _a.Move, Field = _a.Field;
1281
+ test('Raging Bull should break screens', function () {
1282
+ var pokemon = Pokemon('Tauros-Paldea-Aqua');
1283
+ var ragingBull = Move('Raging Bull');
1284
+ var otherMove = Move('Waterfall', { overrides: { basePower: 90 } });
1285
+ var field = Field({ defenderSide: { isReflect: true } });
1286
+ var ragingBullResult = calculate(pokemon, pokemon, ragingBull, field);
1287
+ expect(ragingBullResult.field.defenderSide.isReflect).toBe(false);
1288
+ var otherMoveResult = calculate(pokemon, pokemon, otherMove, field);
1289
+ expect(otherMoveResult.field.defenderSide.isReflect).toBe(true);
1290
+ expect(ragingBullResult.range()[0]).toBeGreaterThan(otherMoveResult.range()[0]);
1291
+ expect(ragingBullResult.range()[1]).toBeGreaterThan(otherMoveResult.range()[1]);
1292
+ });
1293
+ });
1294
+ });
1295
+ });
1296
+ });
1297
+ //# sourceMappingURL=calc.test.js.map