@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,297 @@
1
+ import type {Generation} from '../data/interface';
2
+ import {getItemBoostType} from '../items';
3
+ import type {RawDesc} from '../desc';
4
+ import type {Field} from '../field';
5
+ import type {Move} from '../move';
6
+ import type {Pokemon} from '../pokemon';
7
+ import {Result} from '../result';
8
+ import {computeFinalStats, getMoveEffectiveness, handleFixedDamageMoves} from './util';
9
+
10
+ export function calculateRBYGSC(
11
+ gen: Generation,
12
+ attacker: Pokemon,
13
+ defender: Pokemon,
14
+ move: Move,
15
+ field: Field
16
+ ) {
17
+ computeFinalStats(gen, attacker, defender, field, 'atk', 'def', 'spa', 'spd', 'spe');
18
+
19
+ const desc: RawDesc = {
20
+ attackerName: attacker.name,
21
+ moveName: move.name,
22
+ defenderName: defender.name,
23
+ };
24
+
25
+ const result = new Result(gen, attacker, defender, move, field, 0, desc);
26
+
27
+ if (move.category === 'Status') {
28
+ return result;
29
+ }
30
+
31
+ if (field.defenderSide.isProtected) {
32
+ desc.isProtected = true;
33
+ return result;
34
+ }
35
+
36
+ if (move.name === 'Pain Split') {
37
+ const average = Math.floor((attacker.curHP() + defender.curHP()) / 2);
38
+ const damage = Math.max(0, defender.curHP() - average);
39
+ result.damage = damage;
40
+ return result;
41
+ }
42
+
43
+ // Fixed damage moves (eg. Night Shade) ignore type effectiveness in Gen 1
44
+ if (gen.num === 1) {
45
+ const fixedDamage = handleFixedDamageMoves(attacker, move);
46
+ if (fixedDamage) {
47
+ result.damage = fixedDamage;
48
+ return result;
49
+ }
50
+ }
51
+
52
+ const typeEffectivenessPrecedenceRules = [
53
+ 'Normal',
54
+ 'Fire',
55
+ 'Water',
56
+ 'Electric',
57
+ 'Grass',
58
+ 'Ice',
59
+ 'Fighting',
60
+ 'Poison',
61
+ 'Ground',
62
+ 'Flying',
63
+ 'Psychic',
64
+ 'Bug',
65
+ 'Rock',
66
+ 'Ghost',
67
+ 'Dragon',
68
+ 'Dark',
69
+ 'Steel',
70
+ ];
71
+
72
+ let firstDefenderType = defender.types[0];
73
+ let secondDefenderType = defender.types[1];
74
+
75
+ if (secondDefenderType && firstDefenderType !== secondDefenderType && gen.num === 2) {
76
+ const firstTypePrecedence = typeEffectivenessPrecedenceRules.indexOf(firstDefenderType);
77
+ const secondTypePrecedence = typeEffectivenessPrecedenceRules.indexOf(secondDefenderType);
78
+
79
+ if (firstTypePrecedence > secondTypePrecedence) {
80
+ [firstDefenderType, secondDefenderType] = [secondDefenderType, firstDefenderType];
81
+ }
82
+ }
83
+
84
+ const type1Effectiveness =
85
+ getMoveEffectiveness(gen, move, firstDefenderType, field.defenderSide.isForesight);
86
+ const type2Effectiveness = secondDefenderType
87
+ ? getMoveEffectiveness(gen, move, secondDefenderType, field.defenderSide.isForesight)
88
+ : 1;
89
+ const typeEffectiveness = type1Effectiveness * type2Effectiveness;
90
+
91
+ if (typeEffectiveness === 0) {
92
+ return result;
93
+ }
94
+
95
+ if (gen.num === 2) {
96
+ const fixedDamage = handleFixedDamageMoves(attacker, move);
97
+ if (fixedDamage) {
98
+ result.damage = fixedDamage;
99
+ return result;
100
+ }
101
+ }
102
+
103
+ if (move.hits > 1) {
104
+ desc.hits = move.hits;
105
+ }
106
+ // Triple Kick's damage increases by 10 after each consecutive hit (10, 20, 30), this is a hack
107
+ if (move.name === 'Triple Kick') {
108
+ move.bp = move.hits === 2 ? 15 : move.hits === 3 ? 20 : 10;
109
+ desc.moveBP = move.bp;
110
+ }
111
+
112
+ // Flail and Reversal are variable BP and never crit
113
+ if (move.named('Flail', 'Reversal')) {
114
+ move.isCrit = false;
115
+ const p = Math.floor((48 * attacker.curHP()) / attacker.maxHP());
116
+ move.bp = p <= 1 ? 200 : p <= 4 ? 150 : p <= 9 ? 100 : p <= 16 ? 80 : p <= 32 ? 40 : 20;
117
+ desc.moveBP = move.bp;
118
+ } else if (move.named('Present') && !move.bp) {
119
+ // Present is technically 0 BP so we default to 40 in that case, but the UI may override the
120
+ // base power in order to simulate the scenarios where it is 80 or 120 BP.
121
+ move.bp = 40;
122
+ }
123
+
124
+ if (move.bp === 0) {
125
+ return result;
126
+ }
127
+
128
+ const isPhysical = move.category === 'Physical';
129
+ const attackStat = isPhysical ? 'atk' : 'spa';
130
+ const defenseStat = isPhysical ? 'def' : 'spd';
131
+ let at = attacker.stats[attackStat]!;
132
+ let df = defender.stats[defenseStat]!;
133
+
134
+ // Whether we ignore Reflect, Light Screen, stat stages, and burns if attack is a crit differs
135
+ // by gen - in gen 2 we also need to check that the attacker does not have stat stage advantage
136
+ const ignoreMods = move.isCrit &&
137
+ (gen.num === 1 ||
138
+ (gen.num === 2 && attacker.boosts[attackStat]! <= defender.boosts[defenseStat]!));
139
+
140
+ let lv = attacker.level;
141
+ if (ignoreMods) {
142
+ at = attacker.rawStats[attackStat]!;
143
+ df = defender.rawStats[defenseStat]!;
144
+ if (gen.num === 1) {
145
+ lv *= 2;
146
+ desc.isCritical = true;
147
+ }
148
+ } else {
149
+ if (attacker.boosts[attackStat] !== 0) desc.attackBoost = attacker.boosts[attackStat];
150
+ if (defender.boosts[defenseStat] !== 0) desc.defenseBoost = defender.boosts[defenseStat];
151
+ if (isPhysical && attacker.hasStatus('brn')) {
152
+ at = Math.floor(at / 2);
153
+ desc.isBurned = true;
154
+ }
155
+ }
156
+
157
+ if (move.named('Explosion', 'Self-Destruct')) {
158
+ df = Math.floor(df / 2);
159
+ }
160
+
161
+ if (!ignoreMods) {
162
+ if (isPhysical && field.defenderSide.isReflect) {
163
+ df *= 2;
164
+ desc.isReflect = true;
165
+ } else if (!isPhysical && field.defenderSide.isLightScreen) {
166
+ df *= 2;
167
+ desc.isLightScreen = true;
168
+ }
169
+ }
170
+
171
+ if ((attacker.named('Pikachu') && attacker.hasItem('Light Ball') && !isPhysical) ||
172
+ (attacker.named('Cubone', 'Marowak') && attacker.hasItem('Thick Club') && isPhysical)) {
173
+ at *= 2;
174
+ desc.attackerItem = attacker.item;
175
+ }
176
+
177
+ if (at > 255 || df > 255) {
178
+ at = Math.floor(at / 4) % 256;
179
+ df = Math.floor(df / 4) % 256;
180
+ }
181
+
182
+ // Gen 2 Present has a glitched damage calculation using the secondary types of the Pokemon
183
+ // for the Attacker's Level and Defender's Defense.
184
+ if (move.named('Present')) {
185
+ const lookup: {[id: string]: number} = {
186
+ Normal: 0, Fighting: 1, Flying: 2, Poison: 3, Ground: 4, Rock: 5, Bug: 7,
187
+ Ghost: 8, Steel: 9, '???': 19, Fire: 20, Water: 21, Grass: 22, Electric: 23,
188
+ Psychic: 24, Ice: 25, Dragon: 26, Dark: 27,
189
+ };
190
+
191
+ at = 10;
192
+ df = Math.max(lookup[attacker.types[1] ? attacker.types[1] : attacker.types[0]], 1);
193
+ lv = Math.max(lookup[defender.types[1] ? defender.types[1] : defender.types[0]], 1);
194
+ }
195
+
196
+ if (defender.named('Ditto') && defender.hasItem('Metal Powder')) {
197
+ df = Math.floor(df * 1.5);
198
+ desc.defenderItem = defender.item;
199
+ }
200
+
201
+ let baseDamage = Math.floor(
202
+ Math.floor((Math.floor((2 * lv) / 5 + 2) * Math.max(1, at) * move.bp) / Math.max(1, df)) / 50
203
+ );
204
+
205
+ // Gen 1 handles move.isCrit above by doubling level
206
+ if (gen.num === 2 && move.isCrit) {
207
+ baseDamage *= 2;
208
+ desc.isCritical = true;
209
+ }
210
+
211
+ if (move.named('Pursuit') && field.defenderSide.isSwitching === 'out') {
212
+ baseDamage = Math.floor(baseDamage * 2);
213
+ desc.isSwitching = 'out';
214
+ }
215
+
216
+ // In Gen 2 and no other gens, Dragon Fang in a no-op and Dragon Scale erroneously has its effect
217
+ const itemBoostType =
218
+ attacker.hasItem('Dragon Fang')
219
+ ? undefined
220
+ : getItemBoostType(attacker.hasItem('Dragon Scale') ? 'Dragon Fang' : attacker.item);
221
+
222
+ if (move.hasType(itemBoostType)) {
223
+ baseDamage = Math.floor(baseDamage * 1.1);
224
+ desc.attackerItem = attacker.item;
225
+ }
226
+
227
+ baseDamage = Math.min(997, baseDamage) + 2;
228
+
229
+ if ((field.hasWeather('Sun') && move.hasType('Fire')) ||
230
+ (field.hasWeather('Rain') && move.hasType('Water'))) {
231
+ baseDamage = Math.floor(baseDamage * 1.5);
232
+ desc.weather = field.weather;
233
+ } else if (
234
+ (field.hasWeather('Sun') && move.hasType('Water')) ||
235
+ (field.hasWeather('Rain') && (move.hasType('Fire') || move.named('Solar Beam')))
236
+ ) {
237
+ baseDamage = Math.floor(baseDamage / 2);
238
+ desc.weather = field.weather;
239
+ }
240
+
241
+ if (move.hasType(...attacker.types)) {
242
+ baseDamage = Math.floor(baseDamage * 1.5);
243
+ }
244
+
245
+ if (gen.num === 1) {
246
+ baseDamage = Math.floor(baseDamage * type1Effectiveness);
247
+ baseDamage = Math.floor(baseDamage * type2Effectiveness);
248
+ } else {
249
+ baseDamage = Math.floor(baseDamage * typeEffectiveness);
250
+ }
251
+
252
+ // Flail and Reversal don't use random factor
253
+ if (move.named('Flail', 'Reversal')) {
254
+ result.damage = baseDamage;
255
+ return result;
256
+ }
257
+
258
+ result.damage = [];
259
+ for (let i = 217; i <= 255; i++) {
260
+ if (gen.num === 2) { // in gen 2 damage is always rounded up to 1. TODO ADD TESTS
261
+ result.damage[i - 217] = Math.max(1, Math.floor((baseDamage * i) / 255));
262
+ } else {
263
+ if (baseDamage === 1) { // in gen 1 the random factor multiplication is skipped if damage = 1
264
+ result.damage[i - 217] = 1;
265
+ } else {
266
+ result.damage[i - 217] = Math.floor((baseDamage * i) / 255);
267
+ }
268
+ }
269
+ }
270
+
271
+ if (move.hits > 1) {
272
+ for (let times = 0; times < move.hits; times++) {
273
+ let damageMultiplier = 217;
274
+ result.damage = result.damage.map(affectedAmount => {
275
+ if (times) {
276
+ let newFinalDamage = 0;
277
+ // in gen 2 damage is always rounded up to 1. TODO ADD TESTS
278
+ if (gen.num === 2) {
279
+ newFinalDamage = Math.max(1, Math.floor((baseDamage * damageMultiplier) / 255));
280
+ } else {
281
+ // in gen 1 the random factor multiplication is skipped if damage = 1
282
+ if (baseDamage === 1) {
283
+ newFinalDamage = 1;
284
+ } else {
285
+ newFinalDamage = Math.floor((baseDamage * damageMultiplier) / 255);
286
+ }
287
+ }
288
+ damageMultiplier++;
289
+ return affectedAmount + newFinalDamage;
290
+ }
291
+ return affectedAmount;
292
+ });
293
+ }
294
+ }
295
+
296
+ return result;
297
+ }