Skip to content

Commit

Permalink
Improve Weight Handling (#535)
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
Zrp200 committed Mar 5, 2024
1 parent 4ad2c97 commit 1b7b530
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 11 deletions.
8 changes: 4 additions & 4 deletions calc/src/mechanics/gen56.ts
Expand Up @@ -31,7 +31,7 @@ import {
getModifiedStat,
getMoveEffectiveness,
getStabMod,
getWeightFactor,
getWeight,
handleFixedDamageMoves,
isGrounded,
OF16, OF32,
Expand Down Expand Up @@ -461,7 +461,7 @@ export function calculateBasePowerBWXY(
break;
case 'Low Kick':
case 'Grass Knot':
const w = defender.weightkg * getWeightFactor(defender);
const w = getWeight(defender, desc, 'defender');
basePower = w >= 200 ? 120 : w >= 100 ? 100 : w >= 50 ? 80 : w >= 25 ? 60 : w >= 10 ? 40 : 20;
desc.moveBP = basePower;
break;
Expand All @@ -472,8 +472,8 @@ export function calculateBasePowerBWXY(
case 'Heavy Slam':
case 'Heat Crash':
const wr =
(attacker.weightkg * getWeightFactor(attacker)) /
(defender.weightkg * getWeightFactor(defender));
getWeight(attacker, desc, 'attacker') /
getWeight(defender, desc, 'defender');
basePower = wr >= 5 ? 120 : wr >= 4 ? 100 : wr >= 3 ? 80 : wr >= 2 ? 60 : 40;
desc.moveBP = basePower;
break;
Expand Down
8 changes: 4 additions & 4 deletions calc/src/mechanics/gen789.ts
Expand Up @@ -38,7 +38,7 @@ import {
getQPBoostedStat,
getMoveEffectiveness,
getShellSideArmCategory,
getWeightFactor,
getWeight,
handleFixedDamageMoves,
isGrounded,
OF16, OF32,
Expand Down Expand Up @@ -716,7 +716,7 @@ export function calculateBasePowerSMSSSV(
break;
case 'Low Kick':
case 'Grass Knot':
const w = defender.weightkg * getWeightFactor(defender);
const w = getWeight(defender, desc, 'defender');
basePower = w >= 200 ? 120 : w >= 100 ? 100 : w >= 50 ? 80 : w >= 25 ? 60 : w >= 10 ? 40 : 20;
desc.moveBP = basePower;
break;
Expand All @@ -733,8 +733,8 @@ export function calculateBasePowerSMSSSV(
case 'Heavy Slam':
case 'Heat Crash':
const wr =
(attacker.weightkg * getWeightFactor(attacker)) /
(defender.weightkg * getWeightFactor(defender));
getWeight(attacker, desc, 'attacker') /
getWeight(defender, desc, 'defender');
basePower = wr >= 5 ? 120 : wr >= 4 ? 100 : wr >= 3 ? 80 : wr >= 2 ? 60 : 40;
desc.moveBP = basePower;
break;
Expand Down
20 changes: 17 additions & 3 deletions calc/src/mechanics/util.ts
Expand Up @@ -552,9 +552,23 @@ export function getShellSideArmCategory(source: Pokemon, target: Pokemon): MoveC
return physicalDamage > specialDamage ? 'Physical' : 'Special';
}

export function getWeightFactor(pokemon: Pokemon) {
return pokemon.hasAbility('Heavy Metal') ? 2
: (pokemon.hasAbility('Light Metal') || pokemon.hasItem('Float Stone')) ? 0.5 : 1;
export function getWeight(pokemon: Pokemon, desc: RawDesc, role: 'defender' | 'attacker') {
let weightHG = pokemon.weightkg * 10;
const abilityFactor = pokemon.hasAbility('Heavy Metal') ? 2
: pokemon.hasAbility('Light Metal') ? 0.5
: 1;
if (abilityFactor !== 1) {
weightHG = Math.max(Math.trunc(weightHG * abilityFactor), 1);
desc[`${role}Ability`] = pokemon.ability;
}

if (pokemon.hasItem('Float Stone')) {
weightHG = Math.max(Math.trunc(weightHG * 0.5), 1);
desc[`${role}Item`] = pokemon.item;
}

// convert back to kg
return weightHG / 10;
}

export function getStabMod(pokemon: Pokemon, move: Move, desc: RawDesc) {
Expand Down
35 changes: 35 additions & 0 deletions calc/src/test/calc.test.ts
Expand Up @@ -264,6 +264,41 @@ describe('calc', () => {
'0 Atk Abomasnow Ice Shard vs. 0 HP / 0 Def Multiscale Dragonite: 84-102 (26 - 31.5%) -- guaranteed 4HKO'
);
});
describe('Weight', function () {
describe('Heavy Metal', () => {
function testBP(ability: string) {
return calculate(
Pokemon('Simisage', {ability}),
Pokemon('Simisear', {ability: 'Heavy Metal'}),
Move('Grass Knot')
).rawDesc.moveBP;
}
it('should double the weight of a Pokemon', () => expect(testBP('Gluttony')).toBe(80));
it('should be negated by Mold Breaker', () => expect(testBP('Mold Breaker')).toBe(60));
});
describe('Light Metal', () => {
function testBP(ability: string) {
return calculate(
Pokemon('Simisage', {ability}),
Pokemon('Registeel', {ability: 'Light Metal'}),
Move('Grass Knot')
).rawDesc.moveBP;
}
it('should halve the weight of a Pokemon', () => expect(testBP('Gluttony')).toBe(100));
it('should be negated by Mold Breaker', () => expect(testBP('Mold Breaker')).toBe(120));
});
describe('Float Stone', () => {
function testBP(ability?: string) {
return calculate(
Pokemon('Simisage', {ability: 'Gluttony'}),
Pokemon('Registeel', {ability, item: 'Float Stone'}),
Move('Grass Knot')
).rawDesc.moveBP;
}
it('should halve the weight of a Pokemon', () => expect(testBP()).toBe(100));
it('should stack with Light Metal', () => expect(testBP('Light Metal')).toBe(80));
});
});
});

inGen(8, ({gen, Pokemon}) => {
Expand Down

0 comments on commit 1b7b530

Please sign in to comment.