Skip to content

Commit

Permalink
Improve Tera Stellar handling (smogon#605)
Browse files Browse the repository at this point in the history
Currently, Tera Stellar is shown in too many cases. When it's active, it is always shown, but this is misleading:

* When using a move other than tera blast, it only applies on the first time, otherwise it's identical to not being there at all
* It does nothing defensively, outside of giving a weakness to Stellar type attacks

Even in the case of Tera Blast, the first use has a different stab modifier, but both cases just display 'Tera Stellar' and 100 BP, which is incorrect --- the same description should ideally always yield the same numbers.
  • Loading branch information
Zrp200 authored Mar 21, 2024
1 parent 2b38194 commit 01467a8
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 9 deletions.
6 changes: 6 additions & 0 deletions calc/src/desc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface RawDesc {
defenseEVs?: string;
hits?: number;
alliesFainted?: number;
isStellarFirstUse?: boolean;
isBeadsOfRuin?: boolean;
isSwordOfRuin?: boolean;
isTabletsOfRuin?: boolean;
Expand Down Expand Up @@ -838,6 +839,11 @@ function buildDescription(description: RawDesc, attacker: Pokemon, defender: Pok
if (description.attackerTera) {
output += `Tera ${description.attackerTera} `;
}

if (description.isStellarFirstUse) {
output += '(First Use) ';
}

if (description.isBeadsOfRuin) {
output += 'Beads of Ruin ';
}
Expand Down
12 changes: 10 additions & 2 deletions calc/src/mechanics/gen789.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,21 @@ export function calculateSMSSSV(

const desc: RawDesc = {
attackerName: attacker.name,
attackerTera: attacker.teraType,
moveName: move.name,
defenderName: defender.name,
defenderTera: defender.teraType,
isDefenderDynamaxed: defender.isDynamaxed,
isWonderRoom: field.isWonderRoom,
};

// only display tera type if it applies
if (attacker.teraType !== 'Stellar' || move.name === 'Tera Blast' || move.isStellarFirstUse) {
// tera blast has special behavior with tera stellar
desc.isStellarFirstUse = attacker.name !== 'Terapagos-Stellar' && move.name === 'Tera Blast' &&
attacker.teraType === 'Stellar' && move.isStellarFirstUse;
desc.attackerTera = attacker.teraType;
}
if (defender.teraType !== 'Stellar') desc.defenderTera = defender.teraType;

const result = new Result(gen, attacker, defender, move, field, 0, desc);

if (move.category === 'Status' && !move.named('Nature Power')) {
Expand Down Expand Up @@ -360,6 +367,7 @@ export function calculateSMSSSV(
}

if (move.type === 'Stellar') {
desc.defenderTera = defender.teraType; // always show in this case
typeEffectiveness = !defender.teraType ? 1 : 2;
}

Expand Down
32 changes: 32 additions & 0 deletions calc/src/test/calc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,38 @@ describe('calc', () => {
"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"
);
});
describe('Tera Stellar', () => {
const terastal = Pokemon('Arceus', {teraType: 'Stellar'});
const control = Pokemon('Arceus');
test('should only be displayed on defender for Stellar attacks', () => {
expect(calculate(control, terastal, Move('Tera Blast'))
.rawDesc
.defenderTera).toBeUndefined();
expect(calculate(terastal, terastal, Move('Tera Blast'))
.rawDesc
.defenderTera).toBeDefined();
// make sure that it isn't caring about stellar first use
expect(calculate(terastal, terastal, Move('Tera Blast', {isStellarFirstUse: true}))
.rawDesc
.defenderTera).toBeDefined();
expect(calculate(control, terastal, Move('Tera Blast', {isStellarFirstUse: true}))
.rawDesc
.defenderTera).toBeUndefined();
});
test('should not be displayed for non-boosted attacks', () => expect(
calculate(terastal, control, Move('Judgment', {isStellarFirstUse: false}))
.rawDesc
.attackerTera
).toBeUndefined());
test('should distinguish between first use for Tera Blast', () => {
// I don't exactly care what the difference is
const result = [true, false].map((isStellarFirstUse, ..._) =>
calculate(terastal, control, Move('Tera Blast', {isStellarFirstUse}))
.rawDesc
.isStellarFirstUse);
expect(result[0]).not.toEqual(result[1]);
});
});
});
describe('Descriptions', () => {
inGen(9, ({gen, calculate, Pokemon, Move}) => {
Expand Down
31 changes: 24 additions & 7 deletions src/js/shared_controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,12 @@ function createPokemon(pokeInfo) {
var item = pokeInfo.find(".item").val();
var isDynamaxed = pokeInfo.find(".max").prop("checked");
var teraType = pokeInfo.find(".teraToggle").is(":checked") ? pokeInfo.find(".teraType").val() : undefined;
var opts = {
ability: ability,
item: item,
isDynamaxed: isDynamaxed,
teraType: teraType,
};
pokeInfo.isDynamaxed = isDynamaxed;
calcHP(pokeInfo);
var curHP = ~~pokeInfo.find(".current-hp").val();
Expand All @@ -1028,10 +1034,10 @@ function createPokemon(pokeInfo) {
status: CALC_STATUS[pokeInfo.find(".status").val()],
toxicCounter: status === 'Badly Poisoned' ? ~~pokeInfo.find(".toxic-counter").val() : 0,
moves: [
getMoveDetails(pokeInfo.find(".move1"), name, ability, item, isDynamaxed),
getMoveDetails(pokeInfo.find(".move2"), name, ability, item, isDynamaxed),
getMoveDetails(pokeInfo.find(".move3"), name, ability, item, isDynamaxed),
getMoveDetails(pokeInfo.find(".move4"), name, ability, item, isDynamaxed)
getMoveDetails(pokeInfo.find(".move1"), opts),
getMoveDetails(pokeInfo.find(".move2"), opts),
getMoveDetails(pokeInfo.find(".move3"), opts),
getMoveDetails(pokeInfo.find(".move4"), opts),
],
overrides: {
baseStats: baseStats,
Expand All @@ -1047,7 +1053,7 @@ function getGender(gender) {
return 'F';
}

function getMoveDetails(moveInfo, species, ability, item, useMax) {
function getMoveDetails(moveInfo, opts) {
var moveName = moveInfo.find("select.move-selector").val();
var isZMove = gen > 6 && moveInfo.find("input.move-z").prop("checked");
var isCrit = moveInfo.find(".move-crit").prop("checked");
Expand All @@ -1059,11 +1065,22 @@ function getMoveDetails(moveInfo, species, ability, item, useMax) {
basePower: +moveInfo.find(".move-bp").val(),
type: moveInfo.find(".move-type").val()
};
if (moveName === 'Tera Blast') {
// custom logic for stellar type tera blast
var isStellar = opts.teraType === 'Stellar';
var statDrops = moveInfo.find('.stat-drops');
var dropsStats = statDrops.is(':visible');
if (isStellar !== dropsStats) {
// update stat drop dropdown here
if (isStellar) statDrops.show(); else statDrops.hide();
}
if (isStellar) overrides.self = {boosts: {atk: -1, spa: -1}};
}
if (gen >= 4) overrides.category = moveInfo.find(".move-cat").val();
return new calc.Move(gen, moveName, {
ability: ability, item: item, useZ: isZMove, species: species, isCrit: isCrit, hits: hits,
ability: opts.ability, item: opts.item, useZ: isZMove, species: opts.species, isCrit: isCrit, hits: hits,
isStellarFirstUse: isStellarFirstUse, timesUsed: timesUsed, timesUsedWithMetronome: timesUsedWithMetronome,
overrides: overrides, useMax: useMax
overrides: overrides, useMax: opts.useMax
});
}

Expand Down

0 comments on commit 01467a8

Please sign in to comment.