Add Terestallization mechanics
Add Terestallization mechanics; implement Stellar type; fix bug with held item weights
BIN
public/images/effects/tera.png
Normal file
After Width: | Height: | Size: 15 KiB |
293
public/images/effects/tera_sparkle.json
Normal 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$"
|
||||
}
|
||||
}
|
BIN
public/images/effects/tera_sparkle.png
Normal file
After Width: | Height: | Size: 191 B |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 40 KiB |
BIN
public/images/items/bug_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/dark_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/dragon_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/electric_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/fairy_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/fighting_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/fire_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/flying_tera_shard.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
public/images/items/ghost_tera_shard.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
public/images/items/grass_tera_shard.png
Normal file
After Width: | Height: | Size: 339 B |
BIN
public/images/items/ground_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/ice_tera_shard.png
Normal file
After Width: | Height: | Size: 343 B |
BIN
public/images/items/normal_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/poison_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/psychic_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/rock_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/steel_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
public/images/items/stellar_tera_shard.png
Normal file
After Width: | Height: | Size: 408 B |
BIN
public/images/items/tera_orb.png
Normal file
After Width: | Height: | Size: 391 B |
BIN
public/images/items/water_tera_shard.png
Normal file
After Width: | Height: | Size: 349 B |
@ -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$"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
public/images/types/stellar.png
Normal file
After Width: | Height: | Size: 813 B |
BIN
public/images/ui/icon_tera.png
Normal file
After Width: | Height: | Size: 162 B |
BIN
public/images/ui/type_tera.png
Normal file
After Width: | Height: | Size: 234 B |
@ -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--) {
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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!`));
|
||||
|
||||
|
@ -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)),
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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 ];
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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';
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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[] = [];
|
||||
|
@ -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)
|
||||
|
70
src/sprite/pokemon-sprite-sparkle-handler.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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++) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
12
src/utils.ts
@ -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;
|
||||
|