Add Terestallization mechanics

Add Terestallization mechanics; implement Stellar type; fix bug with held item weights
pull/16/head
Flashfyre 2024-02-17 00:40:03 -05:00
parent 5ec199a428
commit dadc08d16e
60 changed files with 3585 additions and 2258 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,293 @@
{
"textures": [
{
"image": "tera_sparkle.png",
"format": "RGBA8888",
"size": {
"w": 14,
"h": 19
},
"scale": 1,
"frames": [
{
"filename": "0",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 2,
"y": 2,
"w": 3,
"h": 3
},
"frame": {
"x": 0,
"y": 0,
"w": 3,
"h": 3
}
},
{
"filename": "12",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 2,
"y": 2,
"w": 3,
"h": 3
},
"frame": {
"x": 0,
"y": 0,
"w": 3,
"h": 3
}
},
{
"filename": "1",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 1,
"y": 1,
"w": 5,
"h": 5
},
"frame": {
"x": 3,
"y": 0,
"w": 5,
"h": 5
}
},
{
"filename": "11",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 1,
"y": 1,
"w": 5,
"h": 5
},
"frame": {
"x": 3,
"y": 0,
"w": 5,
"h": 5
}
},
{
"filename": "2",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 1,
"y": 1,
"w": 5,
"h": 5
},
"frame": {
"x": 8,
"y": 0,
"w": 5,
"h": 5
}
},
{
"filename": "10",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 1,
"y": 1,
"w": 5,
"h": 5
},
"frame": {
"x": 8,
"y": 0,
"w": 5,
"h": 5
}
},
{
"filename": "3",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 0,
"y": 5,
"w": 7,
"h": 7
}
},
{
"filename": "9",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 0,
"y": 5,
"w": 7,
"h": 7
}
},
{
"filename": "4",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 7,
"y": 5,
"w": 7,
"h": 7
}
},
{
"filename": "8",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 7,
"y": 5,
"w": 7,
"h": 7
}
},
{
"filename": "5",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 0,
"y": 12,
"w": 7,
"h": 7
}
},
{
"filename": "7",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 0,
"y": 12,
"w": 7,
"h": 7
}
},
{
"filename": "6",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 7,
"h": 7
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 7,
"h": 7
},
"frame": {
"x": 7,
"y": 12,
"w": 7,
"h": 7
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:6f9430b9e2e68d62275dbdf2e06fddbf:15e62f334efdea197e83a90f915a48ba:8d978bad4c3f82a9eaac116b284e702d$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

View File

@ -5,7 +5,7 @@
"format": "RGBA8888",
"size": {
"w": 32,
"h": 266
"h": 280
},
"scale": 1,
"frames": [
@ -407,6 +407,27 @@
"w": 32,
"h": 14
}
},
{
"filename": "stellar",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 266,
"w": 32,
"h": 14
}
}
]
}
@ -414,6 +435,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:194223c9662d1ecd87e27433f731e65a:63fb5df5cbaa3edbf6f88332a579bdac:5961efbfbf4c56b8745347e7a663a32f$"
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

@ -6,7 +6,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov
import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat";
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyInstantReviveChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, PokemonFriendshipBoosterModifier } from "./modifier/modifier";
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyInstantReviveChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, PokemonFriendshipBoosterModifier, LapsingPokemonHeldItemModifier } from "./modifier/modifier";
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
@ -585,7 +585,7 @@ export class EncounterPhase extends BattlePhase {
doSummon();
else {
let message: string;
this.scene.executeWithSeedOffset(() => message = Phaser.Math.RND.pick(encounterMessages), this.scene.currentBattle.waveIndex);
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
this.scene.ui.showDialogue(message, trainer.getName(), null, doSummon);
}
}
@ -1699,9 +1699,12 @@ export class BattleEndPhase extends BattlePhase {
this.scene.clearEnemyHeldItemModifiers();
const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier) as LapsingPersistentModifier[];
const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[];
for (let m of lapsingModifiers) {
if (!m.lapse())
const args: any[] = [];
if (m instanceof LapsingPokemonHeldItemModifier)
args.push(this.scene.getPokemonById(m.pokemonId));
if (!m.lapse(args))
this.scene.removeModifier(m);
}
@ -2322,7 +2325,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
};
this.executeForAll((pokemon: Pokemon) => {
const immune = !pokemon || !!pokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length;
const immune = !pokemon || !!pokemon.getTypes(true).filter(t => this.weather.isTypeDamageImmune(t)).length;
if (!immune)
inflictDamage(pokemon);
});
@ -2753,7 +2756,7 @@ export class TrainerVictoryPhase extends BattlePhase {
let showMessageAndEnd = () => this.end();
if (victoryMessages?.length) {
let message: string;
this.scene.executeWithSeedOffset(() => message = Phaser.Math.RND.pick(victoryMessages), this.scene.currentBattle.waveIndex);
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex);
const messagePages = message.split(/\$/g).map(m => m.trim());
for (let p = messagePages.length - 1; p >= 0; p--) {

View File

@ -4,7 +4,7 @@ import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies, speciesStarters } from './data/pokemon-species';
import * as Utils from './utils';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier } from './modifier/modifier';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier } from './modifier/modifier';
import { PokeballType } from './data/pokeball';
import { initAutoPlay } from './system/auto-play';
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from './data/battle-anims';
@ -49,6 +49,8 @@ import { Nature } from './data/nature';
import { SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from './data/pokemon-forms';
import { FormChangePhase, QuietFormChangePhase } from './form-change-phase';
import { BattleSpec } from './enums/battle-spec';
import { getTypeRgb } from './data/type';
import PokemonSpriteSparkleHandler from './sprite/pokemon-sprite-sparkle-handler';
const enableAuto = true;
const quickStart = false;
@ -141,6 +143,8 @@ export default class BattleScene extends Phaser.Scene {
public seed: string;
public waveSeed: string;
private spriteSparkleHandler: PokemonSpriteSparkleHandler;
public fieldSpritePipeline: FieldSpritePipeline;
public spritePipeline: SpritePipeline;
@ -235,6 +239,8 @@ export default class BattleScene extends Phaser.Scene {
this.loadImage('achv_bar_4', 'ui');
this.loadImage('shiny_star', 'ui', 'shiny.png');
this.loadImage('icon_spliced', 'ui');
this.loadImage('icon_tera', 'ui');
this.loadImage('type_tera', 'ui');
this.loadImage('pb_tray_overlay_player', 'ui');
this.loadImage('pb_tray_overlay_enemy', 'ui');
@ -318,8 +324,10 @@ export default class BattleScene extends Phaser.Scene {
this.loadImage(`pkmn__sub`, 'pokemon', 'sub.png');
this.loadAtlas('battle_stats', 'effects');
this.loadAtlas('shiny', 'effects');
this.loadImage('tera', 'effects');
this.loadAtlas('pb_particles', 'effects');
this.loadImage('evo_sparkle', 'effects');
this.loadAtlas('tera_sparkle', 'effects');
this.load.video('evo_bg', 'images/effects/evo_bg.mp4', true);
this.loadAtlas('pb', '');
@ -501,6 +509,9 @@ export default class BattleScene extends Phaser.Scene {
this.updateUIPositions();
this.spriteSparkleHandler = new PokemonSpriteSparkleHandler();
this.spriteSparkleHandler.setup(this);
this.party = [];
let loadPokemonAssets = [];
@ -537,6 +548,15 @@ export default class BattleScene extends Phaser.Scene {
showOnStart: true
});
this.anims.create({
key: 'tera_sparkle',
frames: this.anims.generateFrameNumbers('tera_sparkle', { start: 0, end: 12 }),
frameRate: 18,
repeat: 0,
showOnStart: true,
hideOnComplete: true
});
this.reset();
if (this.quickStart) {
@ -1036,6 +1056,18 @@ export default class BattleScene extends Phaser.Scene {
return ret;
}
addPokemonSprite(pokemon: Pokemon, x: number, y: number, texture: string | Phaser.Textures.Texture, frame?: string | number, hasShadow: boolean = false, ignoreOverride: boolean = false): Phaser.GameObjects.Sprite {
const ret = this.addFieldSprite(x, y, texture, frame);
this.initPokemonSprite(ret, pokemon);
return ret;
}
initPokemonSprite(sprite: Phaser.GameObjects.Sprite, pokemon?: Pokemon, hasShadow: boolean = false, ignoreOverride: boolean = false): Phaser.GameObjects.Sprite {
sprite.setPipeline(this.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: hasShadow, ignoreOverride: ignoreOverride, teraColor: pokemon ? getTypeRgb(pokemon.getTeraType()) : undefined });
this.spriteSparkleHandler.add(sprite);
return sprite;
}
showFieldOverlay(duration: integer): Promise<void> {
return new Promise(resolve => {
this.tweens.add({
@ -1460,10 +1492,13 @@ export default class BattleScene extends Phaser.Scene {
return new Promise(resolve => {
const soundName = modifier.type.soundName;
this.validateAchvs(ModifierAchv, modifier);
const modifiersToRemove: PersistentModifier[] = [];
if (modifier instanceof PersistentModifier) {
if (modifier instanceof TerastallizeModifier)
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId)));
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) {
if (modifier instanceof PokemonFormChangeItemModifier)
modifier.apply([ this.getPokemonById(modifier.pokemonId) ]);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier)
modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]);
if (playSound && !this.sound.get(soundName))
this.playSound(soundName);
} else if (!virtual) {
@ -1471,6 +1506,9 @@ export default class BattleScene extends Phaser.Scene {
this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, null, true);
return this.addModifier(defaultModifierType.newModifier(), ignoreUpdate, playSound, false, instant).then(() => resolve());
}
for (let rm of modifiersToRemove)
this.removeModifier(rm);
if (!ignoreUpdate && !virtual)
return this.updateModifiers(true, instant).then(() => resolve());
@ -1509,9 +1547,17 @@ export default class BattleScene extends Phaser.Scene {
});
}
addEnemyModifier(itemModifier: PersistentModifier, ignoreUpdate?: boolean, instant?: boolean): Promise<void> {
addEnemyModifier(modifier: PersistentModifier, ignoreUpdate?: boolean, instant?: boolean): Promise<void> {
return new Promise(resolve => {
itemModifier.add(this.enemyModifiers, false, this);
const modifiersToRemove: PersistentModifier[] = [];
if (modifier instanceof TerastallizeModifier)
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId, false)));
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier)
modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]);
for (let rm of modifiersToRemove)
this.removeModifier(rm, true);
}
if (!ignoreUpdate)
this.updateModifiers(false, instant).then(() => resolve());
else
@ -1586,7 +1632,15 @@ export default class BattleScene extends Phaser.Scene {
else
modifierChance = !isBoss ? 12 : 4;
this.getEnemyParty().forEach((enemyPokemon: EnemyPokemon, i: integer) => {
const party = this.getEnemyParty();
if (this.currentBattle.trainer) {
const modifiers = this.currentBattle.trainer.genModifiers(party);
for (let modifier of modifiers)
this.addEnemyModifier(modifier, true, true);
}
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
let pokemonModifierChance = modifierChance;
if (this.currentBattle.battleType === BattleType.TRAINER)
pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i));
@ -1657,33 +1711,27 @@ export default class BattleScene extends Phaser.Scene {
const modifierIndex = modifiers.indexOf(modifier);
if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier)
modifier.apply([ this.getPokemonById(modifier.pokemonId), false ]);
return true;
}
return false;
}
getModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean): PersistentModifier[] {
if (player === undefined)
player = true;
getModifiers(modifierType: { new(...args: any[]): Modifier }, player: boolean = true): PersistentModifier[] {
return (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType);
}
findModifiers(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier[] {
if (player === undefined)
player = true;
findModifiers(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier[] {
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m));
}
findModifier(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier {
if (player === undefined)
player = true;
findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier {
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m));
}
applyModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean, ...args: any[]): void {
if (player === undefined)
player = true;
applyModifiers(modifierType: { new(...args: any[]): Modifier }, player: boolean = true, ...args: any[]): void {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
for (let modifier of modifiers) {
if (modifier.apply(args))
@ -1691,9 +1739,7 @@ export default class BattleScene extends Phaser.Scene {
}
}
applyModifier(modifierType: { new(...args: any[]): Modifier }, player?: boolean, ...args: any[]): PersistentModifier {
if (player === undefined)
player = true;
applyModifier(modifierType: { new(...args: any[]): Modifier }, player: boolean = true, ...args: any[]): PersistentModifier {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
for (let modifier of modifiers) {
if (modifier.apply(args)) {

View File

@ -216,7 +216,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[]): Get
const trainerTypes: TrainerType[] = [];
for (let trainerPoolEntry of trainerPool) {
const trainerType = Array.isArray(trainerPoolEntry)
? Phaser.Math.RND.pick(trainerPoolEntry)
? Utils.randSeedItem(trainerPoolEntry)
: trainerPoolEntry;
trainerTypes.push(trainerType);
}

View File

@ -322,7 +322,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
const type = move.getMove().type;
const pokemonTypes = pokemon.getTypes();
const pokemonTypes = pokemon.getTypes(true);
if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) {
pokemon.summonData.types = [ type ];
return true;
@ -333,7 +333,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
}
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string {
return getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nmade it the ${Utils.toReadableString(Type[pokemon.getTypes()[0]])} type!`);
return getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nmade it the ${Utils.toReadableString(Type[pokemon.getTypes(true)[0]])} type!`);
}
}
@ -656,7 +656,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
const targets = pokemon.getOpponents();
let target: Pokemon;
if (targets.length > 1)
pokemon.scene.executeWithSeedOffset(() => target = Phaser.Math.RND.pick(targets), pokemon.scene.currentBattle.waveIndex);
pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex);
else
target = targets[0];

View File

@ -6,6 +6,7 @@ import * as Utils from "../utils";
import { BattlerIndex } from "../battle";
import stringify, { Element } from "json-stable-stringify";
import { Moves } from "./enums/moves";
import { getTypeRgb } from "./type";
//import fs from 'vite-plugin-fs/browser';
export enum AnimFrameTarget {
@ -764,8 +765,7 @@ export abstract class BattleAnim {
const spriteSource = isUser ? userSprite : targetSprite;
if ((isUser ? u : t) === sprites.length) {
let sprite: Phaser.GameObjects.Sprite;
sprite = scene.addFieldSprite(0, 0, spriteSource.texture, spriteSource.frame.name);
sprite.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: true });
sprite = scene.addPokemonSprite(isUser ? user : target, 0, 0, spriteSource.texture, spriteSource.frame.name, true);
[ 'spriteColors', 'fusionSpriteColors' ].map(k => sprite.pipelineData[k] = (isUser ? user : target).getSprite().pipelineData[k]);
spriteSource.on('animationupdate', (_anim, frame) => sprite.setFrame(frame.textureFrame));
scene.field.add(sprite);

View File

@ -98,7 +98,7 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
let ret: Species;
scene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.pick(legendarySpecies);
ret = Utils.randSeedItem(legendarySpecies);
}, Utils.getSunday(new Date(timestamp)).getTime(), EGG_SEED.toString());
return ret;
@ -109,7 +109,7 @@ export function getTypeGachaTypeForTimestamp(scene: BattleScene, timestamp: inte
let ret: Type;
scene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.pick(types);
ret = Utils.randSeedItem(types);
}, Utils.getSunday(new Date(timestamp)).getTime(), EGG_SEED.toString());
return ret;

View File

@ -1813,7 +1813,7 @@ export class CopyTypeAttr extends MoveEffectAttr {
if (!super.apply(user, target, move, args))
return false;
user.summonData.types = target.getTypes();
user.summonData.types = target.getTypes(true);
user.scene.queueMessage(getPokemonMessage(user, `'s type\nchanged to match ${target.name}'s!`));

View File

@ -841,7 +841,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GOGOAT, 32, null, null)
],
[Species.PANCHAM]: [
new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.getParty().find(p => p.getTypes(true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM)
new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.getParty().find(p => p.getTypes(false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM)
],
[Species.ESPURR]: [
new SpeciesFormEvolution(Species.MEOWSTIC, '', '', 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)),

View File

@ -2372,6 +2372,7 @@ export const speciesStarters = {
[Species.TOGEPI]: 3,
[Species.NATU]: 2,
[Species.MAREEP]: 3,
[Species.MARILL]: 4,
[Species.HOPPIP]: 1,
[Species.AIPOM]: 3,
[Species.SUNKERN]: 1,

View File

@ -11,6 +11,7 @@ import { Species } from "./enums/species";
import { tmSpecies } from "./tms";
import { Type } from "./type";
import { initTrainerTypeDialogue } from "./dialogue";
import { PersistentModifier, TerastallizeModifier } from "../modifier/modifier";
export enum TrainerPoolTier {
COMMON,
@ -163,6 +164,7 @@ export const trainerPartyTemplates = {
type PartyTemplateFunc = (scene: BattleScene) => TrainerPartyTemplate;
type PartyMemberFunc = (scene: BattleScene, level: integer) => EnemyPokemon;
type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
export interface PartyMemberFuncs {
[key: integer]: PartyMemberFunc
@ -183,6 +185,7 @@ export class TrainerConfig {
public encounterBgm: string;
public femaleEncounterBgm: string;
public victoryBgm: string;
public genModifiersFunc: GenModifiersFunc;
public modifierRewardFuncs: ModifierTypeFunc[] = [];
public partyTemplates: TrainerPartyTemplate[];
public partyTemplateFunc: PartyTemplateFunc;
@ -325,11 +328,16 @@ export class TrainerConfig {
return this;
}
setGenModifiersFunc(genModifiersFunc: GenModifiersFunc): TrainerConfig {
this.genModifiersFunc = genModifiersFunc;
return this;
}
setModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig {
this.modifierRewardFuncs = modifierTypeFuncs.map(func => () => {
const modifierTypeFunc = func();
const modifierType = modifierTypeFunc();
modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc);
modifierType.withIdFromFunc(modifierTypeFunc);
return modifierType;
});
return this;
@ -352,6 +360,10 @@ export class TrainerConfig {
this.setStaticParty();
this.setBattleBgm('battle_gym');
this.setVictoryBgm('victory_gym');
this.setGenModifiersFunc(party => {
const waveIndex = party[0].scene.currentBattle.waveIndex;
return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : null);
});
return this;
}
@ -373,6 +385,7 @@ export class TrainerConfig {
this.setStaticParty();
this.setBattleBgm('battle_elite');
this.setVictoryBgm('victory_gym');
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null));
return this;
}
@ -390,6 +403,7 @@ export class TrainerConfig {
this.setStaticParty();
this.setBattleBgm('battle_champion');
this.setVictoryBgm('victory_champion');
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 3));
return this;
}
@ -447,7 +461,7 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
function getRandomPartyMemberFunc(speciesPool: Species[], postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
return (scene: BattleScene, level: integer) => {
const species = getPokemonSpecies(Phaser.Math.RND.pick(speciesPool)).getSpeciesForLevel(level, true, true, scene.currentBattle.trainer.config.isBoss);
const species = getPokemonSpecies(Utils.randSeedItem(speciesPool)).getSpeciesForLevel(level, true, true, scene.currentBattle.trainer.config.isBoss);
return scene.addEnemyPokemon(getPokemonSpecies(species), level, true, undefined, undefined, postProcess);
};
}
@ -461,6 +475,17 @@ function getSpeciesFilterRandomPartyMemberFunc(speciesFilter: PokemonSpeciesFilt
};
}
function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: Type[]): PersistentModifier[] {
const ret: PersistentModifier[] = [];
const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i);
for (let t = 0; t < Math.min(count, party.length); t++) {
const randomIndex = Utils.randSeedItem(partyMemberIndexes);
partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
ret.push(modifierTypes.TERA_SHARD().generateType(null, [ Utils.randSeedItem(types ? types : party[randomIndex].getTypes()) ]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier);
}
return ret;
}
export const trainerConfigs: TrainerConfigs = {
[TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(),
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
@ -753,7 +778,11 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ]))
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.setSpeciesFilter(species => species.baseTotal >= 540),
.setSpeciesFilter(species => species.baseTotal >= 540)
.setGenModifiersFunc(party => {
const starter = party[0];
return [ modifierTypes.TERA_SHARD().generateType(null, [ starter.species.type1 ]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ];
}),
[TrainerType.RIVAL_5]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setTitle('Rival').setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_5)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ]))
@ -762,7 +791,11 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.RAYQUAZA ], p => {
p.setBoss();
p.pokeball = PokeballType.MASTER_BALL;
})),
}))
.setGenModifiersFunc(party => {
const starter = party[0];
return [ modifierTypes.TERA_SHARD().generateType(null, [ starter.species.type1 ]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ];
}),
[TrainerType.RIVAL_6]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setTitle('Rival').setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm('final').setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_6)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT ]))
@ -772,7 +805,11 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss();
p.pokeball = PokeballType.MASTER_BALL;
p.formIndex = 1;
})),
}))
.setGenModifiersFunc(party => {
const starter = party[0];
return [ modifierTypes.TERA_SHARD().generateType(null, [ starter.species.type1 ]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier ];
}),
};
(function() {

View File

@ -17,7 +17,8 @@ export enum Type {
ICE,
DRAGON,
DARK,
FAIRY
FAIRY,
STELLAR
};
export type TypeDamageMultiplier = 0 | 0.25 | 0.5 | 1 | 2 | 4;
@ -491,5 +492,52 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
default:
return 0;
}
case Type.STELLAR:
return 1;
}
}
export function getTypeRgb(type: Type): [ integer, integer, integer ] {
switch (type) {
case Type.NORMAL:
return [ 168, 168, 120 ];
case Type.FIGHTING:
return [ 192, 48, 40 ];
case Type.FLYING:
return [ 168, 144, 240 ];
case Type.POISON:
return [ 160, 64, 160 ];
case Type.GROUND:
return [ 224, 192, 104 ];
case Type.ROCK:
return [ 184, 160, 56 ];
case Type.BUG:
return [ 168, 184, 32 ];
case Type.GHOST:
return [ 112, 88, 152 ];
case Type.STEEL:
return [ 184, 184, 208 ];
case Type.FIRE:
return [ 240, 128, 48 ];
case Type.WATER:
return [ 104, 144, 240 ];
case Type.GRASS:
return [ 120, 200, 80 ];
case Type.ELECTRIC:
return [ 248, 208, 48 ];
case Type.PSYCHIC:
return [ 248, 88, 136 ];
case Type.ICE:
return [ 152, 216, 216 ];
case Type.DRAGON:
return [ 112, 56, 248 ];
case Type.DARK:
return [ 112, 88, 72 ];
case Type.FAIRY:
return [ 232, 136, 200 ];
case Type.STELLAR:
return [ 255, 255, 255 ];
default:
return [ 0, 0, 0 ];
}
}

View File

@ -8,6 +8,7 @@ import { Mode } from "./ui/ui";
import { LearnMovePhase } from "./battle-phases";
import { cos, sin } from "./anims";
import { PlayerPokemon } from "./pokemon";
import { getTypeRgb } from "./data/type";
export class EvolutionPhase extends BattlePhase {
protected pokemon: PlayerPokemon;
@ -70,7 +71,7 @@ export class EvolutionPhase extends BattlePhase {
this.evolutionBgOverlay.setAlpha(0);
this.evolutionContainer.add(this.evolutionBgOverlay);
const getPokemonSprite = () => this.scene.addFieldSprite(this.evolutionBaseBg.displayWidth / 2, this.evolutionBaseBg.displayHeight / 2, `pkmn__sub`);
const getPokemonSprite = () => this.scene.addPokemonSprite(this.pokemon, this.evolutionBaseBg.displayWidth / 2, this.evolutionBaseBg.displayHeight / 2, `pkmn__sub`);
this.evolutionContainer.add((this.pokemonSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonTintSprite = getPokemonSprite()));
@ -90,7 +91,7 @@ export class EvolutionPhase extends BattlePhase {
[ this.pokemonSprite, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite ].map(sprite => {
sprite.play(this.pokemon.getSpriteKey(true));
sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false });
sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) });
sprite.pipelineData['ignoreTimeTint'] = true;
[ 'spriteColors', 'fusionSpriteColors' ].map(k => {
if (this.pokemon.summonData?.speciesForm)

View File

@ -10,6 +10,7 @@ import PartyUiHandler from "./ui/party-ui-handler";
import { BattlePhase } from "./battle-phase";
import { BattleSpec } from "./enums/battle-spec";
import { MovePhase, PokemonHealPhase } from "./battle-phases";
import { getTypeRgb } from "./data/type";
export class FormChangePhase extends EvolutionPhase {
private formChange: SpeciesFormChange;
@ -184,10 +185,10 @@ export class QuietFormChangePhase extends BattlePhase {
const preName = this.pokemon.name;
const getPokemonSprite = () => {
const sprite = this.scene.addFieldSprite(this.pokemon.x + this.pokemon.getSprite().x, this.pokemon.y + this.pokemon.getSprite().y, `pkmn__sub`);
const sprite = this.scene.addPokemonSprite(this.pokemon, this.pokemon.x + this.pokemon.getSprite().x, this.pokemon.y + this.pokemon.getSprite().y, `pkmn__sub`);
sprite.setOrigin(0.5, 1);
sprite.play(this.pokemon.getSpriteKey()).stop();
sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false });
sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) });
[ 'spriteColors', 'fusionSpriteColors' ].map(k => {
if (this.pokemon.summonData?.speciesForm)
k += 'Base';

View File

@ -59,6 +59,11 @@ export class ModifierType {
this.tier = tier;
}
withIdFromFunc(func: ModifierTypeFunc): ModifierType {
this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func);
return this;
}
newModifier(...args: any[]): Modifier {
return this.newModifierFunc(this, args);
}
@ -615,6 +620,20 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
}
}
export class TerastallizeModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
private teraType: Type;
constructor(teraType: Type) {
super(`${Utils.toReadableString(Type[teraType])} Tera Shard`, `${Utils.toReadableString(Type[teraType])} Terastallizes the holder for up to 10 battles`, (type, args) => new Modifiers.TerastallizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), null, 'tera_shard');
this.teraType = teraType;
}
getPregenArgs(): any[] {
return [ this.teraType ];
}
}
export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemModifierType {
constructor(name: string, chancePercent: integer, iconImage?: string, group?: string, soundName?: string) {
super(name, `Upon attacking, there is a ${chancePercent}% chance the foe's held item will be stolen`, (type, args) => new Modifiers.ContactHeldItemTransferChanceModifier(type, (args[0] as Pokemon).id, chancePercent), iconImage, group, soundName);
@ -670,7 +689,8 @@ export const modifierTypes = {
FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(),
MEGA_BRACELET: () => new ModifierType('Mega Bracelet', 'Mega stones become available', (type, _args) => new Modifiers.MegaEvolutionAccessModifier(type)),
DYNAMAX_BAND: () => new ModifierType('Dynamax Band', 'Gigantamaxing becomes available', (type, _args) => new Modifiers.GigantamaxAccessModifier(type)),
DYNAMAX_BAND: () => new ModifierType('Dynamax Band', 'Max Mushrooms become available', (type, _args) => new Modifiers.GigantamaxAccessModifier(type)),
TERA_ORB: () => new ModifierType('Tera Orb', 'Tera Shards become available', (type, _args) => new Modifiers.TerastallizeAccessModifier(type)),
MAP: () => new ModifierType('Map', 'Allows you to choose your destination at a crossroads', (type, _args) => new Modifiers.MapModifier(type)),
@ -721,6 +741,20 @@ export const modifierTypes = {
ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterModifierTypeGenerator(),
TERA_SHARD: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs)
return new TerastallizeModifierType(pregenArgs[0] as Type);
if (!party[0].scene.getModifiers(Modifiers.TerastallizeAccessModifier).length)
return null;
let type: Type;
if (!Utils.randInt(3)) {
const partyMemberTypes = party.map(p => p.getTypes(false, true)).flat();
type = Utils.randSeedItem(partyMemberTypes);
} else
type = Utils.randSeedInt(64) ? Utils.randSeedInt(18) as Type : Type.STELLAR;
return new TerastallizeModifierType(type);
}),
BERRY: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs)
return new BerryModifierType(pregenArgs[0] as BerryType);
@ -902,6 +936,7 @@ const modifierPool = {
return Math.min(Math.ceil(highestPartyLevel / 20), 4);
}),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
new WeightedModifierType(modifierTypes.TERA_SHARD, 1),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode === GameMode.SPLICED_ENDLESS && party.filter(p => !p.fusionSpecies).length > 1 ? 4 : 0),
new WeightedModifierType(modifierTypes.REVERSE_DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode === GameMode.SPLICED_ENDLESS && party.filter(p => p.fusionSpecies).length ? 6 : 0),
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
@ -934,6 +969,7 @@ const modifierPool = {
new WeightedModifierType(modifierTypes.OVAL_CHARM, 2),
new WeightedModifierType(modifierTypes.ABILITY_CHARM, 2),
new WeightedModifierType(modifierTypes.IV_SCANNER, 2),
new WeightedModifierType(modifierTypes.TERA_ORB, 3),
new WeightedModifierType(modifierTypes.EXP_BALANCE, 1),
new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, 1),
new WeightedModifierType(modifierTypes.REVERSE_DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode !== GameMode.SPLICED_ENDLESS && party.filter(p => p.fusionSpecies).length ? 3 : 0),
@ -1054,7 +1090,10 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
pool[t].reduce((total: integer, modifierType: WeightedModifierType) => {
const weightedModifierType = modifierType as WeightedModifierType;
const existingModifiers = party[0].scene.findModifiers(m => (m.type.generatorId || m.type.id) === weightedModifierType.modifierType.id, player);
const weight = !existingModifiers.length || existingModifiers.filter(m => m.stackCount < m.getMaxStackCount(party[0].scene, true)).length
const weight = !existingModifiers.length
|| weightedModifierType.modifierType instanceof PokemonHeldItemModifierType
|| (weightedModifierType.modifierType instanceof ModifierTypeGenerator && weightedModifierType.modifierType.generateType(party) instanceof PokemonHeldItemModifierType)
|| existingModifiers.find(m => m.stackCount < m.getMaxStackCount(party[0].scene, true))
? weightedModifierType.weight instanceof Function
? (weightedModifierType.weight as Function)(party)
: weightedModifierType.weight as integer

View File

@ -265,7 +265,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
this.battlesLeft = battlesLeft;
}
lapse(): boolean {
lapse(args: any[]): boolean {
return !!--this.battlesLeft;
}
@ -281,6 +281,10 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
return container;
}
getBattlesLeft(): integer {
return this.battlesLeft;
}
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number {
return 99;
}
@ -404,6 +408,24 @@ export class GigantamaxAccessModifier extends PersistentModifier {
}
}
export class TerastallizeAccessModifier extends PersistentModifier {
constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount);
}
clone(): TerastallizeAccessModifier {
return new TerastallizeAccessModifier(this.type, this.stackCount);
}
apply(args: any[]): boolean {
return true;
}
getMaxStackCount(scene: BattleScene): integer {
return 1;
}
}
export abstract class PokemonHeldItemModifier extends PersistentModifier {
public pokemonId: integer;
@ -481,6 +503,85 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
abstract getMaxHeldItemCount(pokemon: Pokemon): integer
}
export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier {
protected battlesLeft: integer;
constructor(type: ModifierTypes.ModifierType, pokemonId: integer, battlesLeft?: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
this.battlesLeft = battlesLeft;
}
lapse(args: any[]): boolean {
return !!--this.battlesLeft;
}
getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container {
const container = super.getIcon(scene, forSummary);
if (this.getPokemon(scene).isPlayer()) {
const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: '66px', color: '#f89890' });
battleCountText.setShadow(0, 0, null);
battleCountText.setStroke('#984038', 16)
battleCountText.setOrigin(1, 0);
container.add(battleCountText);
}
return container;
}
getBattlesLeft(): integer {
return this.battlesLeft;
}
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number {
return 1;
}
}
export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
public teraType: Type;
constructor(type: ModifierTypes.TerastallizeModifierType, pokemonId: integer, teraType: Type, battlesLeft?: integer, stackCount?: integer) {
super(type, pokemonId, battlesLeft || 10, stackCount);
this.teraType = teraType;
}
matchType(modifier: Modifier): boolean {
if (modifier instanceof TerastallizeModifier && modifier.teraType === this.teraType)
return true;
return false;
}
clone(): TerastallizeModifier {
return new TerastallizeModifier(this.type as ModifierTypes.TerastallizeModifierType, this.pokemonId, this.teraType, this.battlesLeft, this.stackCount);
}
getArgs(): any[] {
return [ this.pokemonId, this.teraType, this.battlesLeft ];
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
pokemon.updateSpritePipelineData();
return true;
}
lapse(args: any[]): boolean {
const ret = super.lapse(args);
if (!ret) {
const pokemon = args[0] as Pokemon;
pokemon.updateSpritePipelineData();
}
return ret;
}
getMaxHeldItemCount(pokemon: Pokemon): integer {
return 1;
}
}
export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
protected stat: Stat;
@ -1323,8 +1424,19 @@ export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier {
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
const active = args[1] as boolean;
return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger);
let switchActive = this.active && !active;
if (switchActive)
this.active = false;
const ret = pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger);
if (switchActive)
this.active = true;
return ret;
}
getMaxHeldItemCount(pokemon: Pokemon): integer {

View File

@ -34,8 +34,7 @@ vec3 blendHardLight(vec3 base, vec3 blend) {
return blendOverlay(blend, base);
}
void main()
{
void main() {
vec4 texture;
%forloop%
@ -83,13 +82,11 @@ void main()
`;
const spriteVertShader = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform mat4 uProjectionMatrix;
uniform int uRoundPixels;
uniform vec2 uResolution;
attribute vec2 inPosition;
attribute vec2 inTexCoord;
@ -99,15 +96,19 @@ attribute vec4 inTint;
varying vec2 outTexCoord;
varying float outTexId;
varying vec2 outPosition;
varying float outTintEffect;
varying vec4 outTint;
void main()
{
void main() {
gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0);
if (uRoundPixels == 1)
{
gl_Position.xy = floor(((gl_Position.xy + 1.0) * 0.5 * uResolution) + 0.5) / uResolution * 2.0 - 1.0;
}
outTexCoord = inTexCoord;
outTexId = inTexId;
outPosition = inPosition;
outTint = inTint;
outTintEffect = inTintEffect;
}

View File

@ -23,12 +23,16 @@ uniform int isOutside;
uniform vec3 dayTint;
uniform vec3 duskTint;
uniform vec3 nightTint;
uniform float teraTime;
uniform vec3 teraColor;
uniform int hasShadow;
uniform int yCenter;
uniform float fieldScale;
uniform float vCutoff;
uniform vec2 relPosition;
uniform vec2 texFrameUv;
uniform vec2 size;
uniform vec2 texSize;
uniform float yOffset;
uniform vec4 tone;
uniform ivec4 spriteColors[32];
@ -48,11 +52,107 @@ vec3 blendHardLight(vec3 base, vec3 blend) {
return blendOverlay(blend, base);
}
void main()
{
vec4 texture;
float hue2rgb(float f1, float f2, float hue) {
if (hue < 0.0)
hue += 1.0;
else if (hue > 1.0)
hue -= 1.0;
float res;
if ((6.0 * hue) < 1.0)
res = f1 + (f2 - f1) * 6.0 * hue;
else if ((2.0 * hue) < 1.0)
res = f2;
else if ((3.0 * hue) < 2.0)
res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
else
res = f1;
return res;
}
%forloop%
vec3 rgb2hsl(vec3 color) {
vec3 hsl;
float fmin = min(min(color.r, color.g), color.b);
float fmax = max(max(color.r, color.g), color.b);
float delta = fmax - fmin;
hsl.z = (fmax + fmin) / 2.0;
if (delta == 0.0) {
hsl.x = 0.0;
hsl.y = 0.0;
} else {
if (hsl.z < 0.5)
hsl.y = delta / (fmax + fmin);
else
hsl.y = delta / (2.0 - fmax - fmin);
float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;
float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;
float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;
if (color.r == fmax )
hsl.x = deltaB - deltaG;
else if (color.g == fmax)
hsl.x = (1.0 / 3.0) + deltaR - deltaB;
else if (color.b == fmax)
hsl.x = (2.0 / 3.0) + deltaG - deltaR;
if (hsl.x < 0.0)
hsl.x += 1.0;
else if (hsl.x > 1.0)
hsl.x -= 1.0;
}
return hsl;
}
vec3 hsl2rgb(vec3 hsl) {
vec3 rgb;
if (hsl.y == 0.0)
rgb = vec3(hsl.z);
else {
float f2;
if (hsl.z < 0.5)
f2 = hsl.z * (1.0 + hsl.y);
else
f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
float f1 = 2.0 * hsl.z - f2;
rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0));
rgb.g = hue2rgb(f1, f2, hsl.x);
rgb.b= hue2rgb(f1, f2, hsl.x - (1.0/3.0));
}
return rgb;
}
vec3 blendHue(vec3 base, vec3 blend) {
vec3 baseHSL = rgb2hsl(base);
return hsl2rgb(vec3(rgb2hsl(blend).r, baseHSL.g, baseHSL.b));
}
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
vec4 texture = texture2D(uMainSampler[0], outTexCoord);
for (int i = 0; i < 32; i++) {
if (spriteColors[i][3] == 0)
@ -73,6 +173,24 @@ void main()
// Multiply texture tint
vec4 color = texture * texel;
if (color.a > 0.0 && teraColor.r > 0.0 && teraColor.g > 0.0 && teraColor.b > 0.0) {
vec2 relUv = vec2((outTexCoord.x - texFrameUv.x) / (size.x / texSize.x), (outTexCoord.y - texFrameUv.y) / (size.y / texSize.y));
vec2 teraTexCoord = vec2(relUv.x * (size.x / 200.0), relUv.y * (size.y / 120.0));
vec4 teraCol = texture2D(uMainSampler[1], teraTexCoord);
float floorValue = 86.0 / 255.0;
vec3 teraPatternHsv = rgb2hsv(teraCol.rgb);
teraCol.rgb = hsv2rgb(vec3((teraPatternHsv.b - floorValue) * 4.0 + teraTexCoord.x * fieldScale / 2.0 + teraTexCoord.y * fieldScale / 2.0 + teraTime * 255.0, teraPatternHsv.b, teraPatternHsv.b));
color.rgb = mix(color.rgb, blendHue(color.rgb, teraColor), 0.625);
teraCol.rgb = mix(teraCol.rgb, teraColor, 0.5);
color.rgb = blendOverlay(color.rgb, teraCol.rgb);
if (teraColor.r < 1.0 || teraColor.g < 1.0 || teraColor.b < 1.0) {
vec3 teraColHsv = rgb2hsv(teraColor);
color.rgb = mix(color.rgb, teraColor, (1.0 - teraColHsv.g) / 2.0);
}
}
if (outTintEffect == 1.0) {
// Solid color + texture alpha
color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a);
@ -110,7 +228,7 @@ void main()
dayNightTint = mix(duskTint, dayTint, (time - 0.875) / 0.125);
}
color = vec4(blendHardLight(color.rgb, dayNightTint), color.a);
color.rgb = blendHardLight(color.rgb, dayNightTint);
}
if (hasShadow == 1) {
@ -144,13 +262,11 @@ void main()
`;
const spriteVertShader = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform mat4 uProjectionMatrix;
uniform int uRoundPixels;
uniform vec2 uResolution;
attribute vec2 inPosition;
attribute vec2 inTexCoord;
@ -159,6 +275,7 @@ attribute float inTintEffect;
attribute vec4 inTint;
varying vec2 outTexCoord;
varying vec2 outtexFrameUv;
varying float outTexId;
varying vec2 outPosition;
varying float outTintEffect;
@ -167,7 +284,10 @@ varying vec4 outTint;
void main()
{
gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0);
if (uRoundPixels == 1)
{
gl_Position.xy = floor(((gl_Position.xy + 1.0) * 0.5 * uResolution) + 0.5) / uResolution * 2.0 - 1.0;
}
outTexCoord = inTexCoord;
outTexId = inTexId;
outPosition = inPosition;
@ -193,10 +313,14 @@ export default class SpritePipeline extends FieldSpritePipeline {
onPreRender(): void {
super.onPreRender();
this.set1f('teraTime', 0);
this.set3fv('teraColor', [ 0, 0, 0 ]);
this.set1i('hasShadow', 0);
this.set1i('yCenter', 0);
this.set2f('relPosition', 0, 0);
this.set2f('texFrameUv', 0, 0);
this.set2f('size', 0, 0);
this.set2f('texSize', 0, 0);
this.set1f('yOffset', 0);
this.set4fv('tone', this._tone);
}
@ -208,6 +332,7 @@ export default class SpritePipeline extends FieldSpritePipeline {
const data = sprite.pipelineData;
const tone = data['tone'] as number[];
const teraColor = data['teraColor'] as integer[] ?? [ 0, 0, 0 ];
const hasShadow = data['hasShadow'] as boolean;
const ignoreOverride = data['ignoreOverride'] as boolean;
const spriteColors = (ignoreOverride && data['spriteColorsBase']) || data['spriteColors'] || [] as number[][];
@ -215,7 +340,6 @@ export default class SpritePipeline extends FieldSpritePipeline {
const isEntityObj = sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer;
const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer;
const fieldScaleRatio = field.scale / 6;
const position = isEntityObj
? [ sprite.parentContainer.x, sprite.parentContainer.y ]
: [ sprite.x, sprite.y ];
@ -224,13 +348,18 @@ export default class SpritePipeline extends FieldSpritePipeline {
position[0] += -(sprite.width - (sprite.frame.width)) / 2 + sprite.frame.x;
if (sprite.originY === 0.5)
position[1] += (sprite.height / 2) * ((isEntityObj ? sprite.parentContainer : sprite).scale - 1);
this.set1f('teraTime', (this.game.getTime() % 500000) / 500000);
this.set3fv('teraColor', teraColor.map(c => c / 255));
this.set1i('hasShadow', hasShadow ? 1 : 0);
this.set1i('yCenter', sprite.originY === 0.5 ? 1 : 0);
this.set1f('fieldScale', field.scale);
this.set2f('relPosition', position[0], position[1]);
this.set2f('texFrameUv', sprite.frame.u0, sprite.frame.v0);
this.set2f('size', sprite.frame.width, sprite.height);
this.set2f('texSize', sprite.texture.source[0].width, sprite.texture.source[0].height);
this.set1f('yOffset', sprite.height - sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale));
this.set4fv('tone', tone);
this.bindTexture(this.game.textures.get('tera').source[0].glTexture, 1);
const emptyColors = [ 0, 0, 0, 0 ];
const flatSpriteColors: integer[] = [];
const flatFusionSpriteColors: integer[] = [];

View File

@ -2,13 +2,13 @@ import Phaser from 'phaser';
import BattleScene, { AnySound } from './battle-scene';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info';
import { Moves } from "./data/enums/moves";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, AttackMove, AddBattlerTagAttr, OneHitKOAttr } from "./data/move";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr } from "./data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies } from './data/pokemon-species';
import * as Utils from './utils';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier } from './data/type';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from './data/type';
import { getLevelTotalExp } from './data/exp';
import { Stat } from './data/pokemon-stat';
import { AttackTypeBoosterModifier, DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier';
import { AttackTypeBoosterModifier, DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier, TerastallizeModifier } from './modifier/modifier';
import { PokeballType } from './data/pokeball';
import { Gender } from './data/gender';
import { initMoveAnim, loadMoveAnimAssets } from './data/battle-anims';
@ -203,9 +203,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.fieldUI.addAt(this.battleInfo, 0);
const getSprite = (hasShadow?: boolean) => {
const ret = this.scene.addFieldSprite(0, 0, `pkmn__${this.isPlayer() ? 'back__' : ''}sub`);
const ret = this.scene.addPokemonSprite(this, 0, 0, `pkmn__${this.isPlayer() ? 'back__' : ''}sub`, undefined, true);
ret.setOrigin(0.5, 1);
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow });
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, teraColor: getTypeRgb(this.getTeraType()) });
return ret;
};
@ -406,6 +406,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return 1;
}
updateSpritePipelineData(): void {
[ this.getSprite(), this.getTintSprite() ].map(s => s.pipelineData['teraColor'] = getTypeRgb(this.getTeraType()));
this.updateInfo(true);
}
initShinySparkle(): void {
const shinySparkle = this.scene.addFieldSprite(0, 0, 'shiny');
shinySparkle.setVisible(false);
@ -614,26 +619,32 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.getLevelMoves(1, true).filter(lm => !this.moveset.filter(m => m.moveId === lm).length).filter((move: Moves, i: integer, array: Moves[]) => array.indexOf(move) === i);
}
getTypes(ignoreOverride?: boolean): Type[] {
getTypes(includeTeraType = false, ignoreOverride?: boolean): Type[] {
const types = [];
if (!ignoreOverride && this.summonData?.types)
this.summonData.types.forEach(t => types.push(t));
else {
const speciesForm = this.getSpeciesForm();
types.push(speciesForm.type1);
if (includeTeraType) {
const teraType = this.getTeraType();
if (teraType != Type.UNKNOWN)
types.push(teraType);
} else {
if (!ignoreOverride && this.summonData?.types)
this.summonData.types.forEach(t => types.push(t));
else {
const speciesForm = this.getSpeciesForm();
types.push(speciesForm.type1);
const fusionSpeciesForm = this.getFusionSpeciesForm();
if (fusionSpeciesForm) {
if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1)
types.push(fusionSpeciesForm.type2);
else if (fusionSpeciesForm.type1 !== speciesForm.type1)
types.push(fusionSpeciesForm.type1);
const fusionSpeciesForm = this.getFusionSpeciesForm();
if (fusionSpeciesForm) {
if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1)
types.push(fusionSpeciesForm.type2);
else if (fusionSpeciesForm.type1 !== speciesForm.type1)
types.push(fusionSpeciesForm.type1);
}
if (types.length === 1 && speciesForm.type2 !== null)
types.push(speciesForm.type2);
}
if (types.length === 1 && speciesForm.type2 !== null)
types.push(speciesForm.type2);
}
if (this.getTag(BattlerTagType.IGNORE_FLYING) || this.scene.arena.getTag(ArenaTagType.GRAVITY)) {
@ -649,7 +660,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
isOfType(type: Type) {
return this.getTypes().indexOf(type) > -1;
return this.getTypes(true).indexOf(type) > -1;
}
getAbility(): Ability {
@ -677,14 +688,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return weight.value;
}
getTeraType(): Type {
const teraModifier = this.scene.findModifier(m => m instanceof TerastallizeModifier
&& m.pokemonId === this.id && !!m.getBattlesLeft(), this.isPlayer()) as TerastallizeModifier;
if (teraModifier)
return teraModifier.teraType;
return Type.UNKNOWN;
}
isTerastallized(): boolean {
return this.getTeraType() !== Type.UNKNOWN;
}
getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier {
const types = this.getTypes();
if (moveType === Type.STELLAR)
return this.isTerastallized() ? 2 : 1;
const types = this.getTypes(true);
return getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
}
getMatchupScore(pokemon: Pokemon): number {
const types = this.getTypes();
const enemyTypes = pokemon.getTypes();
const types = this.getTypes(true);
const enemyTypes = pokemon.getTypes(true);
let atkScore = pokemon.getAttackMoveEffectiveness(types[0]);
let defScore = 1 / this.getAttackMoveEffectiveness(enemyTypes[0]);
if (types.length > 1)
@ -965,7 +991,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const cancelled = new Utils.BooleanHolder(false);
const typeless = !!move.getAttrs(TypelessAttr).length;
const types = this.getTypes();
const types = this.getTypes(true);
const typeMultiplier = new Utils.NumberHolder(!typeless && moveCategory !== MoveCategory.STATUS
? getTypeDamageMultiplier(move.type, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(move.type, types[1]) : 1)
: 1);
@ -977,6 +1003,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
case MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const power = new Utils.NumberHolder(move.power);
const sourceTeraType = source.getTeraType();
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type && power.value < 60 && move.priority <= 0 && !move.getAttrs(MultiHitAttr).length)
power.value = 60;
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power);
if (!typeless)
@ -1011,13 +1040,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this);
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source);
const sourceTypes = source.getTypes();
const stabMultiplier = new Utils.NumberHolder(sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type) ? 1.5 : 1);
const criticalMultiplier = isCritical ? 2 : 1;
const isTypeImmune = (typeMultiplier.value * weatherTypeMultiplier) === 0;
const sourceTypes = source.getTypes();
const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type);
let stabMultiplier = new Utils.NumberHolder(1);
if (sourceTeraType === Type.UNKNOWN && matchesSourceType)
stabMultiplier.value += 0.5;
else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type)
stabMultiplier.value += 0.5;
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType)
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
if (!isTypeImmune) {
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((this.scene.currentBattle.randSeedInt(15) + 85) / 100)) * criticalMultiplier;
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)

View File

@ -0,0 +1,70 @@
import BattleScene from "../battle-scene";
import Pokemon from "../pokemon";
import * as Utils from "../utils";
export default class PokemonSpriteSparkleHandler {
private sprites: Set<Phaser.GameObjects.Sprite>;
setup(scene: BattleScene): void {
this.sprites = new Set();
scene.tweens.addCounter({
duration: Utils.fixedInt(200),
from: 0,
to: 1,
yoyo: true,
repeat: -1,
onRepeat: () => this.onLapse()
});
}
onLapse(): void {
Array.from(this.sprites.values()).filter(s => !s.scene).map(s => this.sprites.delete(s));
for (let s of this.sprites.values()) {
if (!s.pipelineData['teraColor'] || !(s.pipelineData['teraColor'] as number[]).find(c => c))
continue;
if (!s.visible || (s.parentContainer instanceof Pokemon && !s.parentContainer.parentContainer))
continue;
const pokemon = s.parentContainer instanceof Pokemon ? s.parentContainer as Pokemon : null;
const parent = (pokemon || s).parentContainer;
const texture = s.texture;
const [ width, height ] = [ texture.source[0].width, texture.source[0].height ];
const [ pixelX, pixelY ] = [ Utils.randInt(width), Utils.randInt(height) ];
const ratioX = s.width / width;
const ratioY = s.height / height;
const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, '__BASE');
if (pixel.alpha) {
const [ xOffset, yOffset ] = [ -s.originX * s.width, -s.originY * s.height];
const sparkle = (s.scene as BattleScene).addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), 'tera_sparkle');
sparkle.pipelineData['ignoreTimeTint'] = s.pipelineData['ignoreTimeTint'];
sparkle.play('tera_sparkle');
const teraColor = s.pipelineData['teraColor'] as number[];
parent.add(sparkle);
s.scene.time.delayedCall(Utils.fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy());
}
}
}
add(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
if (!Array.isArray(sprites))
sprites = [ sprites ];
for (let s of sprites) {
if (this.sprites.has(s))
continue;
this.sprites.add(s);
}
}
remove(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
if (!Array.isArray(sprites))
sprites = [ sprites ];
for (let s of sprites) {
this.sprites.delete(s);
}
}
removeAll(): void {
for (let s of this.sprites.values())
this.sprites.delete(s);
}
}

View File

@ -125,6 +125,9 @@ export const achvs = {
TRANSFER_MAX_BATTLE_STAT: new Achv('Teamwork', 'Baton pass to another party member with at least one stat maxed out', 'stick', 20),
MAX_FRIENDSHIP: new Achv('Friendmaxxing', 'Reach max friendship on a Pokémon', 'soothe_bell', 25),
MEGA_EVOLVE: new Achv('Megamorph', 'Mega evolve a Pokémon', 'mega_bracelet', 50),
GIGANTAMAX: new Achv('Absolute Unit', 'Gigantamax a Pokémon', 'dynamax_band', 50),
TERASTALLIZE: new Achv('STAB Enthusiast', 'Terastallize a Pokémon', 'tera_orb', 25),
STELLAR_TERASTALLIZE: new Achv('The Hidden Type', 'Stellar Terastallize a Pokémon', 'stellar_tera_shard', 25).setSecret(true),
SPLICE: new Achv('Infinite Fusion', 'Splice two Pokémon together with DNA Splicers', 'dna_splicers', 10),
MINI_BLACK_HOLE: new ModifierAchv('A Hole Lot of Items', 'Acquire a Mini Black Hole', 'mini_black_hole', 25, modifier => modifier instanceof TurnHeldItemTransferModifier).setSecret(),
CATCH_MYTHICAL: new Achv('Mythical', 'Catch a mythical Pokémon', 'strange_ball', 50).setSecret(),

View File

@ -763,7 +763,7 @@ export class GameData {
this.scene.executeWithSeedOffset(() => {
const neutralNatures = [ Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY ];
for (let s = 0; s < defaultStarters.length; s++)
defaultStarterNatures.push(Phaser.Math.RND.pick(neutralNatures));
defaultStarterNatures.push(Utils.randSeedItem(neutralNatures));
}, 0, 'default');
for (let ds = 0; ds < defaultStarters.length; ds++) {

View File

@ -5,6 +5,7 @@ import { TrainerConfig, TrainerPartyCompoundTemplate, TrainerPartyMemberStrength
import { TrainerType } from "./data/enums/trainer-type";
import { EnemyPokemon } from "./pokemon";
import * as Utils from "./utils";
import { PersistentModifier } from "./modifier/modifier";
export default class Trainer extends Phaser.GameObjects.Container {
public config: TrainerConfig;
@ -17,7 +18,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
? trainerConfigs[trainerType]
: trainerConfigs[TrainerType.ACE_TRAINER];
this.female = female;
this.partyTemplateIndex = Math.min(partyTemplateIndex !== undefined ? partyTemplateIndex : Phaser.Math.RND.weightedPick(this.config.partyTemplates.map((_, i) => i)),
this.partyTemplateIndex = Math.min(partyTemplateIndex !== undefined ? partyTemplateIndex : Utils.randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
this.config.partyTemplates.length - 1);
// TODO: Remove when Phaser weightedPick bug is fixed
@ -174,7 +175,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
tier--;
}
const tierPool = this.config.speciesPools[tier];
species = getPokemonSpecies(Phaser.Math.RND.pick(tierPool));
species = getPokemonSpecies(Utils.randSeedItem(tierPool));
} else
species = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
@ -186,7 +187,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
if (pokemonPrevolutions.hasOwnProperty(species.speciesId) && ret.speciesId !== species.speciesId)
retry = true;
else if (template.isBalanced(battle.enemyParty.length)) {
const partyMemberTypes = battle.enemyParty.map(p => p.getTypes()).flat();
const partyMemberTypes = battle.enemyParty.map(p => p.getTypes(true)).flat();
if (partyMemberTypes.indexOf(ret.type1) > -1 || (ret.type2 !== null && partyMemberTypes.indexOf(ret.type2) > -1))
retry = true;
}
@ -271,6 +272,12 @@ export default class Trainer extends Phaser.GameObjects.Container {
}
}
genModifiers(party: EnemyPokemon[]): PersistentModifier[] {
if (this.config.genModifiersFunc)
return this.config.genModifiersFunc(party);
return [];
}
loadAssets(): Promise<void> {
return this.config.loadAssets(this.scene, this.female);
}

View File

@ -5,6 +5,7 @@ import { addTextObject, TextStyle } from './text';
import { getGenderSymbol, getGenderColor, Gender } from '../data/gender';
import { StatusEffect } from '../data/status-effect';
import BattleScene from '../battle-scene';
import { Type, getTypeRgb } from '../data/type';
export default class BattleInfo extends Phaser.GameObjects.Container {
private player: boolean;
@ -13,6 +14,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
private bossSegments: integer;
private offset: boolean;
private lastName: string;
private lastTeraType: Type;
private lastStatus: StatusEffect;
private lastHp: integer;
private lastMaxHp: integer;
@ -26,6 +28,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
private nameText: Phaser.GameObjects.Text;
private genderText: Phaser.GameObjects.Text;
private ownedIcon: Phaser.GameObjects.Sprite;
private teraIcon: Phaser.GameObjects.Sprite;
private splicedIcon: Phaser.GameObjects.Sprite;
private shinyIcon: Phaser.GameObjects.Sprite;
private statusIndicator: Phaser.GameObjects.Sprite;
@ -43,6 +46,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.boss = false;
this.offset = false;
this.lastName = null;
this.lastTeraType = Type.UNKNOWN;
this.lastStatus = StatusEffect.NONE;
this.lastHp = -1;
this.lastMaxHp = -1;
@ -75,11 +79,19 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.add(this.ownedIcon);
}
this.teraIcon = this.scene.add.sprite(0, 0, 'icon_tera');
this.teraIcon.setVisible(false);
this.teraIcon.setOrigin(0, 0);
this.teraIcon.setScale(0.5);
this.teraIcon.setPositionRelative(this.nameText, 0, 2);
this.teraIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 14, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.teraIcon);
this.splicedIcon = this.scene.add.sprite(0, 0, 'icon_spliced');
this.splicedIcon.setVisible(false);
this.splicedIcon.setOrigin(0, 0);
this.splicedIcon.setPositionRelative(this.nameText, 0, 2);
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 5, 7), Phaser.Geom.Rectangle.Contains);
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 5, 8), Phaser.Geom.Rectangle.Contains);
this.add(this.splicedIcon);
this.statusIndicator = this.scene.add.sprite(0, 0, 'statuses');
@ -130,7 +142,17 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.genderText.setColor(getGenderColor(pokemon.gender));
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 1);
this.lastTeraType = pokemon.getTeraType();
this.teraIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
this.teraIcon.setVisible(this.lastTeraType !== Type.UNKNOWN);
this.teraIcon.on('pointerover', () => {
if (this.lastTeraType !== Type.UNKNOWN)
(this.scene as BattleScene).ui.showTooltip(null, `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`);
});
this.teraIcon.on('pointerout', () => (this.scene as BattleScene).ui.hideTooltip());
this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0), 1);
this.splicedIcon.setVisible(!!pokemon.fusionSpecies);
if (this.splicedIcon.visible) {
this.splicedIcon.on('pointerover', () => (this.scene as BattleScene).ui.showTooltip(null, `${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies.getName(pokemon.fusionFormIndex)}`));
@ -197,7 +219,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
if (boss !== this.boss) {
this.boss = boss;
[ this.nameText, this.genderText, this.splicedIcon, this.ownedIcon, this.statusIndicator, this.levelContainer ].map(e => e.x += 48 * (boss ? -1 : 1));
[ this.nameText, this.genderText, this.teraIcon, this.splicedIcon, this.ownedIcon, this.statusIndicator, this.levelContainer ].map(e => e.x += 48 * (boss ? -1 : 1));
this.hpBar.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? '_boss' : ''}`);
@ -240,12 +262,25 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
if (!this.scene)
return resolve();
if (this.lastName !== pokemon.name) {
const nameUpdated = this.lastName !== pokemon.name;
if (nameUpdated) {
this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth;
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 1);
this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
}
const teraType = pokemon.getTeraType();
const teraTypeUpdated = this.lastTeraType !== teraType;
if (teraTypeUpdated) {
this.teraIcon.setVisible(teraType !== Type.UNKNOWN);
this.teraIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
this.teraIcon.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(teraType)));
this.lastTeraType = teraType;
}
if (nameUpdated || teraTypeUpdated)
this.splicedIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0), 1);
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;

View File

@ -192,7 +192,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
if (shownIvsCount < 6) {
let statsPool = stats.slice(0);
for (let i = 0; i < shownIvsCount; i++) {
const shownStat = Phaser.Math.RND.pick(statsPool);
const shownStat = Utils.randSeedItem(statsPool);
shownStats.push(shownStat);
statsPool.splice(statsPool.indexOf(shownStat), 1);
}

View File

@ -1,6 +1,6 @@
import BattleScene, { Button } from "../battle-scene";
import { Mode } from "./ui";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "../sprite/pokemon-icon-anim-handler";
import { TextStyle, addTextObject } from "./text";
import MessageUiHandler from "./message-ui-handler";
import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg";

View File

@ -11,7 +11,7 @@ import { allMoves } from "../data/move";
import { Moves } from "../data/enums/moves";
import { getGenderColor, getGenderSymbol } from "../data/gender";
import { StatusEffect } from "../data/status-effect";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "../sprite/pokemon-icon-anim-handler";
import { pokemonEvolutions } from "../data/pokemon-evolutions";
import { addWindow } from "./window";
import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
@ -102,7 +102,7 @@ export default class PartyUiHandler extends MessageUiHandler {
private static FilterAllMoves = (_pokemonMove: PokemonMove) => null;
public static FilterItemMaxStacks = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => {
const matchingModifier = pokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).matchType(modifier)) as PokemonHeldItemModifier;
const matchingModifier = pokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(modifier)) as PokemonHeldItemModifier;
if (matchingModifier && matchingModifier.stackCount === matchingModifier.getMaxStackCount(pokemon.scene))
return `${pokemon.name} has too many\nof this item!`;
return null;

View File

@ -11,13 +11,14 @@ import { Unlockables } from "../system/unlockables";
import { GrowthRate, getGrowthRateColor } from "../data/exp";
import { DexAttr, DexEntry } from "../system/game-data";
import * as Utils from "../utils";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "../sprite/pokemon-icon-anim-handler";
import { StatsContainer } from "./stats-container";
import { addWindow } from "./window";
import { Nature, getNatureName } from "../data/nature";
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
import { pokemonFormChanges } from "../data/pokemon-forms";
import { Tutorial, handleTutorial } from "../tutorial";
import PokemonSpriteSparkleHandler from "../sprite/pokemon-sprite-sparkle-handler";
export type StarterSelectCallback = (starters: Starter[]) => void;
@ -310,7 +311,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
let pokerusCursor: integer;
const generateSpecies = () => {
randomSpeciesId = Phaser.Math.RND.pick(starterSpecies);
randomSpeciesId = Utils.randSeedItem(starterSpecies);
species = getPokemonSpecies(randomSpeciesId);
pokerusCursor = this.genSpecies[species.generation - 1].indexOf(species);
};

View File

@ -3,7 +3,7 @@ import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import * as Utils from "../utils";
import { PlayerPokemon } from "../pokemon";
import { Type } from "../data/type";
import { Type, getTypeRgb } from "../data/type";
import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "./text";
import Move, { MoveCategory } from "../data/move";
import { getPokeballAtlasKey } from "../data/pokeball";
@ -98,8 +98,7 @@ export default class SummaryUiHandler extends UiHandler {
this.numberText.setOrigin(0, 1);
this.summaryContainer.add(this.numberText);
this.pokemonSprite = this.scene.add.sprite(56, -106, `pkmn__sub`);
this.pokemonSprite.setPipeline(this.scene.spritePipeline, { ignoreOverride: true, tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false });
this.pokemonSprite = this.scene.initPokemonSprite(this.scene.add.sprite(56, -106, `pkmn__sub`), null, false, true);
this.summaryContainer.add(this.pokemonSprite);
this.nameText = addTextObject(this.scene, 6, -54, '', TextStyle.SUMMARY);
@ -201,6 +200,7 @@ export default class SummaryUiHandler extends UiHandler {
this.numberText.setShadowColor(getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
this.pokemonSprite.play(this.pokemon.getSpriteKey(true));
this.pokemonSprite.pipelineData['teraColor'] = getTypeRgb(this.pokemon.getTeraType());
this.pokemonSprite.pipelineData['ignoreTimeTint'] = true;
[ 'spriteColors', 'fusionSpriteColors' ].map(k => {
delete this.pokemonSprite.pipelineData[`${k}Base`];
@ -489,16 +489,26 @@ export default class SummaryUiHandler extends UiHandler {
typeLabel.setOrigin(0, 0);
profileContainer.add(typeLabel);
const getTypeIcon = (index: integer, type: Type) => {
const typeIcon = this.scene.add.sprite(39 + 34 * index, 42, 'types', Type[type].toLowerCase());
const getTypeIcon = (index: integer, type: Type, tera: boolean = false) => {
const xCoord = 39 + 34 * index;
const typeIcon = !tera
? this.scene.add.sprite(xCoord, 42, 'types', Type[type].toLowerCase())
: this.scene.add.sprite(xCoord, 42, 'type_tera');
if (tera) {
typeIcon.setScale(0.5);
const typeRgb = getTypeRgb(type);
typeIcon.setTint(Phaser.Display.Color.GetColor(typeRgb[0], typeRgb[1], typeRgb[2]));
}
typeIcon.setOrigin(0, 1);
return typeIcon;
};
const types = this.pokemon.getTypes(true);
const types = this.pokemon.getTypes(false, true);
profileContainer.add(getTypeIcon(0, types[0]));
if (types.length > 1)
profileContainer.add(getTypeIcon(1, types[1]));
if (this.pokemon.isTerastallized())
profileContainer.add(getTypeIcon(types.length, this.pokemon.getTeraType(), true));
const ability = this.pokemon.getAbility();
@ -534,7 +544,7 @@ export default class SummaryUiHandler extends UiHandler {
});
}
let memoString = `${getBBCodeFrag(Utils.toReadableString(Nature[this.pokemon.nature]), TextStyle.SUMMARY_RED)} nature,\n${getBBCodeFrag(`${this.pokemon.metBiome === -1 ? 'apparently ' : ''}met at Lv`, TextStyle.WINDOW)}${getBBCodeFrag(this.pokemon.metLevel.toString(), TextStyle.SUMMARY_RED)}${getBBCodeFrag(',', TextStyle.WINDOW)}\n${getBBCodeFrag(getBiomeName(this.pokemon.metBiome), TextStyle.SUMMARY_RED)}${getBBCodeFrag('.', TextStyle.WINDOW)}`;
let memoString = `${getBBCodeFrag(Utils.toReadableString(Nature[this.pokemon.nature]), TextStyle.SUMMARY_RED)}${getBBCodeFrag(' nature,', TextStyle.WINDOW)}\n${getBBCodeFrag(`${this.pokemon.metBiome === -1 ? 'apparently ' : ''}met at Lv`, TextStyle.WINDOW)}${getBBCodeFrag(this.pokemon.metLevel.toString(), TextStyle.SUMMARY_RED)}${getBBCodeFrag(',', TextStyle.WINDOW)}\n${getBBCodeFrag(getBiomeName(this.pokemon.metBiome), TextStyle.SUMMARY_RED)}${getBBCodeFrag('.', TextStyle.WINDOW)}`;
const memoText = addBBCodeTextObject(this.scene, 7, 113, memoString, TextStyle.WINDOW);
memoText.setOrigin(0, 0);

View File

@ -72,6 +72,18 @@ export function randIntRange(min: integer, max: integer): integer {
return randInt(max - min, min);
}
export function randSeedItem<T>(items: T[]): T {
return items.length === 1
? items[0]
: Phaser.Math.RND.pick(items);
}
export function randSeedWeightedItem<T>(items: T[]): T {
return items.length === 1
? items[0]
: Phaser.Math.RND.weightedPick(items);
}
export function getSunday(date: Date): Date {
const day = date.getDay(),
diff = date.getDate() - day;