pokerogue/src/data/weather.ts
flx-sta a07d2c57a4
[Refactor] use typescript strict-null (#3259)
* TS: enable strict-null

* fix battle-scene.ts

* fix voucher.ts

* adapt more files to strict-null

* adapt more files to strict-null ( 2)

* adapt ability.ts to strict-null

* adapt `arena.ts` to strict-null

* adapt TagAddedEvent constructor to strict-null

* adapt phases.ts.to strict-null

* adapt status-effect.ts to strict-null

* adapt `account.ts` to strict-null

* adapt `configHandler.ts` to strict-null

* adapt `ability.ts` to strict-null

* adapt `biomes.ts` to strict-null

* adapt `challenge.ts` to strict-null

* adapt `daily-run.ts` to strict-null

* adapt `nature.ts` to strict-null

* adapt `pokemon-forms.ts` to strict-null

* adapt `tainer-names.ts` to strict-null

* adapt `types.ts` to strict-null

* adapt `weather.ts` to strict-null

* adapt `egg-hatch-phase.ts` to strict-null

* adapt `evolution-phase.ts` to strict-null

* adapt `pokemon-sprite-sparkle-handler.ts` to strict-null

* adapt `evolution-phase.ts` to strict-null

* adapt `game-mode.ts` to strict-null

* adapt `utils.ts` to strict-null

* adapt `voucher-ui-handler.ts` to strict-null

* adapt `src/ui/unavailable-modal-ui-handler.ts` to strict-null

* adapt `src/ui/ui.ts` to strict-null

* adapt `src/ui/ui-theme.ts` to strict-null

* adapt `src/ui/title-ui-handler.ts` to strict-null

* adapt `src/ui/time-of-day-widget.ts` to strict-null

* adapt `src/ui/text.ts` to strict-null

* adapt `src/ui/target-select-ui-handler.ts` to strict-null

* adapt `src/ui/settings/settings-keyboard-ui-handler.ts` to strict-null

* adapt more files to strict-null (3)

* adapt more files to strict-null (4)

* adapt more files (mostly tests) to strict-null (5)

* adapt more files to strict-null (6)

* adapt more files to strict-null (7)

* Update `src/data/pokemon-evolutions.ts` for strict-null

Partial update `src/data/pokemon-species.ts` for strict-null

* adapt more files to strict-null (8)

* adapt more files to strict-null (9)

* Strict some more nulls (still a few errors remaining)

* adapt rest of the files to strict-null (9)

* fix tests (check for null instead of undefined)

* repalce a lot of `??` with bangs

And added TODO notice as usual

* fix more tests

* all tests pass now

* fix broken game-loop after trainer battle

add some console.warn for missing cases and falling back to default

* remove guessed fallback from utils.rgbHexToRgba

* add TODO for this.currentBattle = null

* adjust   getPokemonById() return to include `null`

* fix compilation errors

* add test for pokemon.trySetStatus

* `chanceMultiplier` shouldn't be optional

* allow `null` for currentPhase

* adjust hasExpSprite logic for no keymatch found

* reduce bang usage in account.updateUserInfo()

* fix new strict-null issues after merge

* fix `strict-null` issues in dropdown.ts

and sand_spit.test.ts

* fix egg-gacha

* adapt gul_missile.test.ts to strict-null

* fix move.ts strict-null

* fix i18n.ts strict-null

* fix strict-null issues

* fix baton_pass test

after accidentially breaking it

* chore: fix compiler errors

* revert accidential changes in baton_pass.test.ts

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2024-08-07 17:23:12 +01:00

393 lines
11 KiB
TypeScript

import { Biome } from "#enums/biome";
import { WeatherType } from "#enums/weather-type";
import { getPokemonNameWithAffix } from "../messages";
import Pokemon from "../field/pokemon";
import { Type } from "./type";
import Move, { AttackMove } from "./move";
import * as Utils from "../utils";
import BattleScene from "../battle-scene";
import { SuppressWeatherEffectAbAttr } from "./ability";
import { TerrainType, getTerrainName } from "./terrain";
import i18next from "i18next";
export { WeatherType };
export class Weather {
public weatherType: WeatherType;
public turnsLeft: integer;
constructor(weatherType: WeatherType, turnsLeft?: integer) {
this.weatherType = weatherType;
this.turnsLeft = !this.isImmutable() ? turnsLeft || 0 : 0;
}
lapse(): boolean {
if (this.isImmutable()) {
return true;
}
if (this.turnsLeft) {
return !!--this.turnsLeft;
}
return true;
}
isImmutable(): boolean {
switch (this.weatherType) {
case WeatherType.HEAVY_RAIN:
case WeatherType.HARSH_SUN:
case WeatherType.STRONG_WINDS:
return true;
}
return false;
}
isDamaging(): boolean {
switch (this.weatherType) {
case WeatherType.SANDSTORM:
case WeatherType.HAIL:
return true;
}
return false;
}
isTypeDamageImmune(type: Type): boolean {
switch (this.weatherType) {
case WeatherType.SANDSTORM:
return type === Type.GROUND || type === Type.ROCK || type === Type.STEEL;
case WeatherType.HAIL:
return type === Type.ICE;
}
return false;
}
getAttackTypeMultiplier(attackType: Type): number {
switch (this.weatherType) {
case WeatherType.SUNNY:
case WeatherType.HARSH_SUN:
if (attackType === Type.FIRE) {
return 1.5;
}
if (attackType === Type.WATER) {
return 0.5;
}
break;
case WeatherType.RAIN:
case WeatherType.HEAVY_RAIN:
if (attackType === Type.FIRE) {
return 0.5;
}
if (attackType === Type.WATER) {
return 1.5;
}
break;
}
return 1;
}
isMoveWeatherCancelled(move: Move): boolean {
switch (this.weatherType) {
case WeatherType.HARSH_SUN:
return move instanceof AttackMove && move.type === Type.WATER;
case WeatherType.HEAVY_RAIN:
return move instanceof AttackMove && move.type === Type.FIRE;
}
return false;
}
isEffectSuppressed(scene: BattleScene): boolean {
const field = scene.getField(true);
for (const pokemon of field) {
let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0];
if (!suppressWeatherEffectAbAttr) {
suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0] : null;
}
if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) {
return true;
}
}
return false;
}
}
export function getWeatherStartMessage(weatherType: WeatherType): string | null {
switch (weatherType) {
case WeatherType.SUNNY:
return i18next.t("weather:sunnyStartMessage");
case WeatherType.RAIN:
return i18next.t("weather:rainStartMessage");
case WeatherType.SANDSTORM:
return i18next.t("weather:sandstormStartMessage");
case WeatherType.HAIL:
return i18next.t("weather:hailStartMessage");
case WeatherType.SNOW:
return i18next.t("weather:snowStartMessage");
case WeatherType.FOG:
return i18next.t("weather:fogStartMessage");
case WeatherType.HEAVY_RAIN:
return i18next.t("weather:heavyRainStartMessage");
case WeatherType.HARSH_SUN:
return i18next.t("weather:harshSunStartMessage");
case WeatherType.STRONG_WINDS:
return i18next.t("weather:strongWindsStartMessage");
}
return null;
}
export function getWeatherLapseMessage(weatherType: WeatherType): string | null {
switch (weatherType) {
case WeatherType.SUNNY:
return i18next.t("weather:sunnyLapseMessage");
case WeatherType.RAIN:
return i18next.t("weather:rainLapseMessage");
case WeatherType.SANDSTORM:
return i18next.t("weather:sandstormLapseMessage");
case WeatherType.HAIL:
return i18next.t("weather:hailLapseMessage");
case WeatherType.SNOW:
return i18next.t("weather:snowLapseMessage");
case WeatherType.FOG:
return i18next.t("weather:fogLapseMessage");
case WeatherType.HEAVY_RAIN:
return i18next.t("weather:heavyRainLapseMessage");
case WeatherType.HARSH_SUN:
return i18next.t("weather:harshSunLapseMessage");
case WeatherType.STRONG_WINDS:
return i18next.t("weather:strongWindsLapseMessage");
}
return null;
}
export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string | null {
switch (weatherType) {
case WeatherType.SANDSTORM:
return i18next.t("weather:sandstormDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)});
case WeatherType.HAIL:
return i18next.t("weather:hailDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)});
}
return null;
}
export function getWeatherClearMessage(weatherType: WeatherType): string | null {
switch (weatherType) {
case WeatherType.SUNNY:
return i18next.t("weather:sunnyClearMessage");
case WeatherType.RAIN:
return i18next.t("weather:rainClearMessage");
case WeatherType.SANDSTORM:
return i18next.t("weather:sandstormClearMessage");
case WeatherType.HAIL:
return i18next.t("weather:hailClearMessage");
case WeatherType.SNOW:
return i18next.t("weather:snowClearMessage");
case WeatherType.FOG:
return i18next.t("weather:fogClearMessage");
case WeatherType.HEAVY_RAIN:
return i18next.t("weather:heavyRainClearMessage");
case WeatherType.HARSH_SUN:
return i18next.t("weather:harshSunClearMessage");
case WeatherType.STRONG_WINDS:
return i18next.t("weather:strongWindsClearMessage");
}
return null;
}
export function getTerrainStartMessage(terrainType: TerrainType): string | null {
switch (terrainType) {
case TerrainType.MISTY:
return i18next.t("terrain:mistyStartMessage");
case TerrainType.ELECTRIC:
return i18next.t("terrain:electricStartMessage");
case TerrainType.GRASSY:
return i18next.t("terrain:grassyStartMessage");
case TerrainType.PSYCHIC:
return i18next.t("terrain:psychicStartMessage");
default:
console.warn("getTerrainStartMessage not defined. Using default null");
return null;
}
}
export function getTerrainClearMessage(terrainType: TerrainType): string | null {
switch (terrainType) {
case TerrainType.MISTY:
return i18next.t("terrain:mistyClearMessage");
case TerrainType.ELECTRIC:
return i18next.t("terrain:electricClearMessage");
case TerrainType.GRASSY:
return i18next.t("terrain:grassyClearMessage");
case TerrainType.PSYCHIC:
return i18next.t("terrain:psychicClearMessage");
default:
console.warn("getTerrainClearMessage not defined. Using default null");
return null;
}
}
export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainType): string {
if (terrainType === TerrainType.MISTY) {
return i18next.t("terrain:mistyBlockMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)});
}
return i18next.t("terrain:defaultBlockMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), terrainName: getTerrainName(terrainType)});
}
interface WeatherPoolEntry {
weatherType: WeatherType;
weight: integer;
}
export function getRandomWeatherType(arena: any /* Importing from arena causes a circular dependency */): WeatherType {
let weatherPool: WeatherPoolEntry[] = [];
const hasSun = arena.getTimeOfDay() < 2;
switch (arena.biomeType) {
case Biome.GRASS:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 7 }
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 3 });
}
break;
case Biome.TALL_GRASS:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 8 },
{ weatherType: WeatherType.RAIN, weight: 5 },
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 8 });
}
break;
case Biome.FOREST:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 8 },
{ weatherType: WeatherType.RAIN, weight: 5 }
];
break;
case Biome.SEA:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 3 },
{ weatherType: WeatherType.RAIN, weight: 12 }
];
break;
case Biome.SWAMP:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 3 },
{ weatherType: WeatherType.RAIN, weight: 4 },
{ weatherType: WeatherType.FOG, weight: 1 }
];
break;
case Biome.BEACH:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 8 },
{ weatherType: WeatherType.RAIN, weight: 3 }
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 5 });
}
break;
case Biome.LAKE:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 10 },
{ weatherType: WeatherType.RAIN, weight: 5 },
{ weatherType: WeatherType.FOG, weight: 1 }
];
break;
case Biome.SEABED:
weatherPool = [
{ weatherType: WeatherType.RAIN, weight: 1 }
];
break;
case Biome.BADLANDS:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 8 },
{ weatherType: WeatherType.SANDSTORM, weight: 2 }
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 5 });
}
break;
case Biome.DESERT:
weatherPool = [
{ weatherType: WeatherType.SANDSTORM, weight: 2 }
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 2 });
}
break;
case Biome.ICE_CAVE:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 3 },
{ weatherType: WeatherType.SNOW, weight: 4 },
{ weatherType: WeatherType.HAIL, weight: 1 }
];
break;
case Biome.MEADOW:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 2 }
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 2 });
}
case Biome.VOLCANO:
weatherPool = [
{ weatherType: hasSun ? WeatherType.SUNNY : WeatherType.NONE, weight: 1 }
];
break;
case Biome.GRAVEYARD:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 3 },
{ weatherType: WeatherType.FOG, weight: 1 }
];
break;
case Biome.JUNGLE:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 8 },
{ weatherType: WeatherType.RAIN, weight: 2 }
];
break;
case Biome.SNOWY_FOREST:
weatherPool = [
{ weatherType: WeatherType.SNOW, weight: 7 },
{ weatherType: WeatherType.HAIL, weight: 1 }
];
break;
case Biome.ISLAND:
weatherPool = [
{ weatherType: WeatherType.NONE, weight: 5 },
{ weatherType: WeatherType.RAIN, weight: 1 },
];
if (hasSun) {
weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 2 });
}
break;
}
if (weatherPool.length > 1) {
let totalWeight = 0;
weatherPool.forEach(w => totalWeight += w.weight);
const rand = Utils.randSeedInt(totalWeight);
let w = 0;
for (const weather of weatherPool) {
w += weather.weight;
if (rand < w) {
return weather.weatherType;
}
}
}
return weatherPool.length
? weatherPool[0].weatherType
: WeatherType.NONE;
}