Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Power Spot, Fixed Dauntless Shield, Made Competitive activate from Intimidate, Cleaned up EV and IV exporting #435

Merged
merged 10 commits into from
Apr 27, 2021
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ $ cd calc && npm install
```

Next, run `node build` from the root directory of your clone of this repository. This should
run `npm run compile` in the `calc/` subdirectory to compile the `@smoogon/calc` package from
run `npm run compile` in the `calc/` subdirectory to compile the `@smogon/calc` package from
TypeScript to JavaScript that can be run in the browser, and then compile the 'templated' HTML
and copy everything into the top-level `dist/` folder. To then view the UI, open `dist/index.html` -
simply double-clicking on the file from your operating system's file manager UI should open it in
Expand Down
4 changes: 4 additions & 0 deletions calc/src/desc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface RawDesc {
isProtected?: boolean;
isReflect?: boolean;
isBattery?: boolean;
isPowerSpot?: boolean;
isSwitching?: 'out' | 'in';
moveBP?: number;
moveName: string;
Expand Down Expand Up @@ -825,6 +826,9 @@ function buildDescription(description: RawDesc, attacker: Pokemon, defender: Pok
if (description.isBattery) {
output += ' Battery boosted ';
}
if (description.isPowerSpot) {
output += ' Power Spot boosted ';
}
if (description.isSwitching) {
output += ' switching boosted ';
}
Expand Down
2 changes: 2 additions & 0 deletions calc/src/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class Side implements State.Side {
isFriendGuard: boolean;
isAuroraVeil: boolean;
isBattery: boolean;
isPowerSpot: boolean;
isSwitching?: 'out' | 'in';

constructor(side: State.Side = {}) {
Expand All @@ -91,6 +92,7 @@ export class Side implements State.Side {
this.isFriendGuard = !!side.isFriendGuard;
this.isAuroraVeil = !!side.isAuroraVeil;
this.isBattery = !!side.isBattery;
this.isPowerSpot = !!side.isPowerSpot;
this.isSwitching = side.isSwitching;
}

Expand Down
10 changes: 9 additions & 1 deletion calc/src/mechanics/gen56.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ export function calculateBWXY(
} else if (defender.types[1] && effectiveness[defender.types[1]]! === 0) {
typeEffectiveness = type1Effectiveness;
}
} else if (typeEffectiveness === 0 && move.hasType('Ground') && defender.hasItem('Iron Ball')) {
typeEffectiveness = 1;
}

if (typeEffectiveness === 0) {
Expand All @@ -202,7 +204,8 @@ export function calculateBWXY(
(move.hasType('Electric') &&
defender.hasAbility('Lightning Rod', 'Motor Drive', 'Volt Absorb')) ||
(move.hasType('Ground') &&
!field.isGravity && !move.named('Thousand Arrows') && defender.hasAbility('Levitate')) ||
!field.isGravity && !defender.hasItem('Iron Ball') &&
!move.named('Thousand Arrows') && defender.hasAbility('Levitate')) ||
(move.flags.bullet && defender.hasAbility('Bulletproof')) ||
(move.flags.sound && defender.hasAbility('Soundproof'))
) {
Expand Down Expand Up @@ -456,6 +459,11 @@ export function calculateBWXY(
desc.isBattery = true;
}

if (field.attackerSide.isPowerSpot) {
bpMods.push(0x14cc);
desc.isPowerSpot = true;
}

if (isAerilate || isPixilate || isRefrigerate || isNormalize) {
bpMods.push(0x14cd);
desc.attackerAbility = attacker.ability;
Expand Down
22 changes: 15 additions & 7 deletions calc/src/mechanics/gen78.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export function calculateSMSS(
checkKlutz(defender);
checkSeedBoost(attacker, field);
checkSeedBoost(defender, field);
checkDauntlessShield(attacker);
checkDauntlessShield(defender);

computeFinalStats(gen, attacker, defender, field, 'def', 'spd', 'spe');

Expand All @@ -67,8 +69,6 @@ export function calculateSMSS(
checkDownload(defender, attacker);
checkIntrepidSword(attacker);
checkIntrepidSword(defender);
checkDauntlessShield(attacker);
checkDauntlessShield(defender);

computeFinalStats(gen, attacker, defender, field, 'atk', 'spa');

Expand Down Expand Up @@ -130,8 +130,7 @@ export function calculateSMSS(
const isCritical = !defender.hasAbility('Battle Armor', 'Shell Armor') &&
(move.isCrit || (attacker.hasAbility('Merciless') && defender.hasStatus('psn', 'tox'))) &&
move.timesUsed === 1;

if (move.named('Weather Ball')) {
if (move.named('Weather Ball') || move.originalName === 'Weather Ball') {
const holdingUmbrella = attacker.hasItem('Utility Umbrella');
move.type =
field.hasWeather('Sun', 'Harsh Sunshine') && !holdingUmbrella ? 'Fire'
Expand All @@ -154,7 +153,8 @@ export function calculateSMSS(
desc.attackerItem = attacker.item;
desc.moveBP = move.bp;
desc.moveType = move.type;
} else if (move.named('Nature Power', 'Terrain Pulse')) {
} else if (move.named('Nature Power') || move.originalName === 'Nature Power' ||
((move.named('Terrain Pulse') || move.originalName === 'Terrain Pulse') && isGrounded(attacker, field))) {
move.type =
field.hasTerrain('Electric') ? 'Electric'
: field.hasTerrain('Grassy') ? 'Grass'
Expand Down Expand Up @@ -234,6 +234,8 @@ export function calculateSMSS(
} else if (defender.types[1] && effectiveness[defender.types[1]]! === 0) {
typeEffectiveness = type1Effectiveness;
}
} else if (typeEffectiveness === 0 && move.hasType('Ground') && defender.hasItem('Iron Ball')) {
typeEffectiveness = 1;
}

if (typeEffectiveness === 0) {
Expand All @@ -256,7 +258,8 @@ export function calculateSMSS(
(move.hasType('Electric') &&
defender.hasAbility('Lightning Rod', 'Motor Drive', 'Volt Absorb')) ||
(move.hasType('Ground') &&
!field.isGravity && !move.named('Thousand Arrows') && defender.hasAbility('Levitate')) ||
!field.isGravity && !defender.hasItem('Iron Ball') &&
!move.named('Thousand Arrows') && defender.hasAbility('Levitate')) ||
(move.flags.bullet && defender.hasAbility('Bulletproof')) ||
(move.flags.sound && !move.named('Clangorous Soul') && defender.hasAbility('Soundproof')) ||
(move.priority > 0 && defender.hasAbility('Queenly Majesty', 'Dazzling'))
Expand Down Expand Up @@ -648,7 +651,7 @@ export function calculateBasePowerSMSS(
desc.moveBP = basePower;
break;
case 'Terrain Pulse':
basePower = move.bp * (field.terrain ? 2 : 1);
basePower = move.bp * (field.terrain && isGrounded(attacker, field) ? 2 : 1);
desc.moveBP = basePower;
break;
case 'Fling':
Expand Down Expand Up @@ -812,6 +815,11 @@ export function calculateBPModsSMSS(
desc.isBattery = true;
}

if (field.attackerSide.isPowerSpot) {
bpMods.push(0x14CD);
desc.isPowerSpot = true;
}

// Sheer Force does not power up max moves or remove the effects (SadisticMystic)
const analyticBoost = attacker.hasAbility('Analytic') &&
(turnOrder !== 'first' || field.defenderSide.isSwitching === 'out');
Expand Down
5 changes: 4 additions & 1 deletion calc/src/mechanics/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const EV_ITEMS = [
];

export function isGrounded(pokemon: Pokemon, field: Field) {
return (field.isGravity ||
return (field.isGravity || pokemon.hasItem('Iron Ball') ||
(!pokemon.hasType('Flying') &&
!pokemon.hasAbility('Levitate') &&
!pokemon.hasItem('Air Balloon')));
Expand Down Expand Up @@ -181,6 +181,9 @@ export function checkIntimidate(gen: Generation, source: Pokemon, target: Pokemo
} else {
target.boosts.atk = Math.max(-6, target.boosts.atk - 1);
}
if(target.hasAbility('Competitive')) {
target.boosts.spa = Math.min(6, target.boosts.spa + 2);
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions calc/src/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export class Move implements State.Move {
const maxMoveName: string = getMaxMoveName(
data.type,
options.species,
!!(data.category === 'Status')
!!(data.category === 'Status'),
options.ability
);
const maxMove = gen.moves.get(toID(maxMoveName));
const maxPower = () => {
Expand Down Expand Up @@ -232,8 +233,9 @@ const ZMOVES_TYPING: {
Water: 'Hydro Vortex',
};

export function getMaxMoveName(moveType: I.TypeName, pokemonSpecies?: string, isStatus?: boolean) {
export function getMaxMoveName(moveType: I.TypeName, pokemonSpecies?: string, isStatus?: boolean, pokemonAbility?: string) {
if (isStatus) return 'Max Guard';
if (pokemonAbility === 'Normalize') return 'Max Strike';
if (moveType === 'Fire') {
if (pokemonSpecies === 'Charizard-Gmax') return 'G-Max Wildfire';
if (pokemonSpecies === 'Centiskorch-Gmax') return 'G-Max Centiferno';
Expand All @@ -243,6 +245,10 @@ export function getMaxMoveName(moveType: I.TypeName, pokemonSpecies?: string, is
if (pokemonSpecies === 'Eevee-Gmax') return 'G-Max Cuddle';
if (pokemonSpecies === 'Meowth-Gmax') return 'G-Max Gold Rush';
if (pokemonSpecies === 'Snorlax-Gmax') return 'G-Max Replenish';
if (pokemonAbility === 'Pixilate') return 'Max Starfall';
if (pokemonAbility === 'Aerilate') return 'Max Airstream';
if (pokemonAbility === 'Refrigerate') return 'Max Hailstorm';
if (pokemonAbility === 'Galvanize') return 'Max Lightning';
}
if (moveType === 'Fairy') {
if (pokemonSpecies === 'Alcremie-Gmax') return 'G-Max Finale';
Expand Down
1 change: 1 addition & 0 deletions calc/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export namespace State {
isFriendGuard?: boolean;
isAuroraVeil?: boolean;
isBattery?: boolean;
isPowerSpot?: boolean;
isSwitching?: 'out' | 'in';
}
}
10 changes: 10 additions & 0 deletions src/honkalculate.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,16 @@
<input class="visually-hidden" type="checkbox" id="batteryR" />
<label class="btn btn-xxwide" for="batteryR">Battery</label>
</div>
<div class="btn-group gen-specific g8">
<div class="left" title="Has this Pok&eacute;mon's power been boosted by an ally's Power Spot ability?">
<input class="visually-hidden" type="checkbox" id="powerSpotL" />
<label class="btn btn-xxwide" for="powerSpotL">Power Spot</label>
</div>
<div class="right" title="Has this Pok&eacute;mon's power been boosted by an ally's Power Spot ability?">
<input class="visually-hidden" type="checkbox" id="powerSpotR" />
<label class="btn btn-xxwide" for="powerSpotR">Power Spot</label>
</div>
</div>
<div class="btn-group gen-specific g2 g3 g4 g5 g6 g7 g8">
<div class="left" title="Is the defending Pok&eacute;mon switching out?">
<input class="visually-hidden" type="checkbox" id="switchingL" />
Expand Down
11 changes: 11 additions & 0 deletions src/index.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,17 @@
<label class="btn btn-xxwide" for="batteryR">Battery</label>
</div></td>
</tr>
<tr class="gen-specific g8">
<td><div class="left" title="Is the Pok&eacute;mon boosted by an ally's Power Spot ability?">
<div hidden id="selectPowerSpotInstruction">Is the Pok&eacute;mon boosted by an ally's Power Spot ability?</div>
<input aria-describedby="selectPowerSpotInstruction" class="visually-hidden calc-trigger" type="checkbox" id="powerSpotL" />
<label class="btn btn-xxwide" for="powerSpotL">Power Spot</label>
</div></td>
<td><div class="right" title="Is the Pok&eacute;mon boosted by an ally's Power Spot ability?">
<input aria-describedby="selectPowerSpotInstruction" class="visually-hidden calc-trigger" type="checkbox" id="powerSpotR" />
<label class="btn btn-xxwide" for="powerSpotR">Power Spot</label>
</div></td>
</tr>
<tr class="gen-specific g2 g3 g4 g5 g6 g7 g8">
<td><div class="left" title="Is the defending Pok&eacute;mon switching out?">
<div hidden id="selectSwitchingInstruction">Is the defending Pok&eacute;mon switching out?</div>
Expand Down
28 changes: 18 additions & 10 deletions src/js/moveset_import.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,35 @@ function ExportPokemon(pokeInfo) {
finalText += "Level: " + pokemon.level + "\n";
finalText += pokemon.nature && gen > 2 ? pokemon.nature + " Nature" + "\n" : "";
finalText += pokemon.ability ? "Ability: " + pokemon.ability + "\n" : "";
if (gen > 2) {
finalText += "EVs: ";
if (gen > 2) {
var EVs_Array = [];
for (var stat in pokemon.evs) {
var ev = pokemon.evs[stat] ? pokemon.evs[stat] : 0;
EVs_Array.push(ev + " " + calc.Stats.displayStat(stat));
if(ev > 0) {
EVs_Array.push(ev + " " + calc.Stats.displayStat(stat));
}
EV_counter += ev;
if (EV_counter > 510) break;
}
finalText += serialize(EVs_Array, " / ");
finalText += "\n";
if(EVs_Array.length > 0){
finalText += "EVs: ";
finalText += serialize(EVs_Array, " / ");
finalText += "\n";
}
}

finalText += "IVs: ";

var IVs_Array = [];
for (var stat in pokemon.ivs) {
var iv = pokemon.ivs[stat] ? pokemon.ivs[stat] : 0;
IVs_Array.push(iv + " " + calc.Stats.displayStat(stat));
if(iv < 31) {
IVs_Array.push(iv + " " + calc.Stats.displayStat(stat));
}
}
if(IVs_Array.length > 0) {
finalText += "IVs: ";
finalText += serialize(IVs_Array, " / ");
finalText += "\n";
}
finalText += serialize(IVs_Array, " / ");
finalText += "\n";

for (var i = 0; i < 4; i++) {
var moveName = pokemon.moves[i].name;
Expand Down
3 changes: 2 additions & 1 deletion src/js/shared_controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ function createField() {
var isFriendGuard = [$("#friendGuardL").prop("checked"), $("#friendGuardR").prop("checked")];
var isAuroraVeil = [$("#auroraVeilL").prop("checked"), $("#auroraVeilR").prop("checked")];
var isBattery = [$("#batteryL").prop("checked"), $("#batteryR").prop("checked")];
var isPowerSpot = [$("#powerSpotL").prop("checked"), $("#powerSpotR").prop("checked")];
// TODO: support switching in as well!
var isSwitchingOut = [$("#switchingL").prop("checked"), $("#switchingR").prop("checked")];

Expand All @@ -856,7 +857,7 @@ function createField() {
isReflect: isReflect[i], isLightScreen: isLightScreen[i],
isProtected: isProtected[i], isSeeded: isSeeded[i], isForesight: isForesight[i],
isTailwind: isTailwind[i], isHelpingHand: isHelpingHand[i], isFriendGuard: isFriendGuard[i],
isAuroraVeil: isAuroraVeil[i], isBattery: isBattery[i], isSwitching: isSwitchingOut[i] ? 'out' : undefined
isAuroraVeil: isAuroraVeil[i], isBattery: isBattery[i], isPowerSpot: isPowerSpot[i], isSwitching: isSwitchingOut[i] ? 'out' : undefined
});
};
return new calc.Field({
Expand Down
11 changes: 11 additions & 0 deletions src/randoms.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,17 @@
<label class="btn btn-xxwide" for="batteryR">Battery</label>
</div></td>
</tr>
<tr class="gen-specific g8">
<td><div class="left" title="Is the Pok&eacute;mon boosted by an ally's Power Spot ability?">
<div hidden id="selectPowerSpotInstruction">Is the Pok&eacute;mon boosted by an ally's Power Spot ability?</div>
<input aria-describedby="selectPowerSpotInstruction" class="visually-hidden calc-trigger" type="checkbox" id="powerSpotL" />
<label class="btn btn-xxwide" for="powerSpotR">Power Spot</label>
</div></td>
<td><div class="right" title="Is the Pok&eacute;mon boosted by an ally's Power Spot ability?">
<input aria-describedby="selectPowerSpotInstruction" class="visually-hidden calc-trigger" type="checkbox" id="powerSpotR" />
<label class="btn btn-xxwide" for="powerSpotR">Power Spot</label>
</div></td>
</tr>
<tr class="gen-specific g2 g3 g4 g5 g6 g7 g8">
<td><div class="left" title="Is the defending Pok&eacute;mon switching out?">
<div hidden id="selectSwitchingInstruction">Is the defending Pok&eacute;mon switching out?</div>
Expand Down