Skip to content

Commit 1b7b530

Browse files
authored
Improve Weight Handling (#535)
- All weight modification code is moved to `src/mechanics/util.ts` - Heavy/Light Metal and Float Stone can now stack. - Heavy Metal, Light Metal, and Float are now shown in the description when applicable - Weight modification is now done in Hectograms, though it still returns Kilograms after truncating fractional hectograms (Showdown implementation uses truncation, not rounding). - It is now impossible to get a weight lower than 0.1kg. - Tests have been written for weight handling that mirror Showdown's.
1 parent 4ad2c97 commit 1b7b530

File tree

4 files changed

+60
-11
lines changed

4 files changed

+60
-11
lines changed

calc/src/mechanics/gen56.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
getModifiedStat,
3232
getMoveEffectiveness,
3333
getStabMod,
34-
getWeightFactor,
34+
getWeight,
3535
handleFixedDamageMoves,
3636
isGrounded,
3737
OF16, OF32,
@@ -461,7 +461,7 @@ export function calculateBasePowerBWXY(
461461
break;
462462
case 'Low Kick':
463463
case 'Grass Knot':
464-
const w = defender.weightkg * getWeightFactor(defender);
464+
const w = getWeight(defender, desc, 'defender');
465465
basePower = w >= 200 ? 120 : w >= 100 ? 100 : w >= 50 ? 80 : w >= 25 ? 60 : w >= 10 ? 40 : 20;
466466
desc.moveBP = basePower;
467467
break;
@@ -472,8 +472,8 @@ export function calculateBasePowerBWXY(
472472
case 'Heavy Slam':
473473
case 'Heat Crash':
474474
const wr =
475-
(attacker.weightkg * getWeightFactor(attacker)) /
476-
(defender.weightkg * getWeightFactor(defender));
475+
getWeight(attacker, desc, 'attacker') /
476+
getWeight(defender, desc, 'defender');
477477
basePower = wr >= 5 ? 120 : wr >= 4 ? 100 : wr >= 3 ? 80 : wr >= 2 ? 60 : 40;
478478
desc.moveBP = basePower;
479479
break;

calc/src/mechanics/gen789.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
getQPBoostedStat,
3939
getMoveEffectiveness,
4040
getShellSideArmCategory,
41-
getWeightFactor,
41+
getWeight,
4242
handleFixedDamageMoves,
4343
isGrounded,
4444
OF16, OF32,
@@ -716,7 +716,7 @@ export function calculateBasePowerSMSSSV(
716716
break;
717717
case 'Low Kick':
718718
case 'Grass Knot':
719-
const w = defender.weightkg * getWeightFactor(defender);
719+
const w = getWeight(defender, desc, 'defender');
720720
basePower = w >= 200 ? 120 : w >= 100 ? 100 : w >= 50 ? 80 : w >= 25 ? 60 : w >= 10 ? 40 : 20;
721721
desc.moveBP = basePower;
722722
break;
@@ -733,8 +733,8 @@ export function calculateBasePowerSMSSSV(
733733
case 'Heavy Slam':
734734
case 'Heat Crash':
735735
const wr =
736-
(attacker.weightkg * getWeightFactor(attacker)) /
737-
(defender.weightkg * getWeightFactor(defender));
736+
getWeight(attacker, desc, 'attacker') /
737+
getWeight(defender, desc, 'defender');
738738
basePower = wr >= 5 ? 120 : wr >= 4 ? 100 : wr >= 3 ? 80 : wr >= 2 ? 60 : 40;
739739
desc.moveBP = basePower;
740740
break;

calc/src/mechanics/util.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,23 @@ export function getShellSideArmCategory(source: Pokemon, target: Pokemon): MoveC
552552
return physicalDamage > specialDamage ? 'Physical' : 'Special';
553553
}
554554

555-
export function getWeightFactor(pokemon: Pokemon) {
556-
return pokemon.hasAbility('Heavy Metal') ? 2
557-
: (pokemon.hasAbility('Light Metal') || pokemon.hasItem('Float Stone')) ? 0.5 : 1;
555+
export function getWeight(pokemon: Pokemon, desc: RawDesc, role: 'defender' | 'attacker') {
556+
let weightHG = pokemon.weightkg * 10;
557+
const abilityFactor = pokemon.hasAbility('Heavy Metal') ? 2
558+
: pokemon.hasAbility('Light Metal') ? 0.5
559+
: 1;
560+
if (abilityFactor !== 1) {
561+
weightHG = Math.max(Math.trunc(weightHG * abilityFactor), 1);
562+
desc[`${role}Ability`] = pokemon.ability;
563+
}
564+
565+
if (pokemon.hasItem('Float Stone')) {
566+
weightHG = Math.max(Math.trunc(weightHG * 0.5), 1);
567+
desc[`${role}Item`] = pokemon.item;
568+
}
569+
570+
// convert back to kg
571+
return weightHG / 10;
558572
}
559573

560574
export function getStabMod(pokemon: Pokemon, move: Move, desc: RawDesc) {

calc/src/test/calc.test.ts

+35
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,41 @@ describe('calc', () => {
264264
'0 Atk Abomasnow Ice Shard vs. 0 HP / 0 Def Multiscale Dragonite: 84-102 (26 - 31.5%) -- guaranteed 4HKO'
265265
);
266266
});
267+
describe('Weight', function () {
268+
describe('Heavy Metal', () => {
269+
function testBP(ability: string) {
270+
return calculate(
271+
Pokemon('Simisage', {ability}),
272+
Pokemon('Simisear', {ability: 'Heavy Metal'}),
273+
Move('Grass Knot')
274+
).rawDesc.moveBP;
275+
}
276+
it('should double the weight of a Pokemon', () => expect(testBP('Gluttony')).toBe(80));
277+
it('should be negated by Mold Breaker', () => expect(testBP('Mold Breaker')).toBe(60));
278+
});
279+
describe('Light Metal', () => {
280+
function testBP(ability: string) {
281+
return calculate(
282+
Pokemon('Simisage', {ability}),
283+
Pokemon('Registeel', {ability: 'Light Metal'}),
284+
Move('Grass Knot')
285+
).rawDesc.moveBP;
286+
}
287+
it('should halve the weight of a Pokemon', () => expect(testBP('Gluttony')).toBe(100));
288+
it('should be negated by Mold Breaker', () => expect(testBP('Mold Breaker')).toBe(120));
289+
});
290+
describe('Float Stone', () => {
291+
function testBP(ability?: string) {
292+
return calculate(
293+
Pokemon('Simisage', {ability: 'Gluttony'}),
294+
Pokemon('Registeel', {ability, item: 'Float Stone'}),
295+
Move('Grass Knot')
296+
).rawDesc.moveBP;
297+
}
298+
it('should halve the weight of a Pokemon', () => expect(testBP()).toBe(100));
299+
it('should stack with Light Metal', () => expect(testBP('Light Metal')).toBe(80));
300+
});
301+
});
267302
});
268303

269304
inGen(8, ({gen, Pokemon}) => {

0 commit comments

Comments
 (0)