Add Terestallization mechanics

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

View File

@ -5,7 +5,7 @@
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 32, "w": 32,
"h": 266 "h": 280
}, },
"scale": 1, "scale": 1,
"frames": [ "frames": [
@ -407,6 +407,27 @@
"w": 32, "w": 32,
"h": 14 "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": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:194223c9662d1ecd87e27433f731e65a:63fb5df5cbaa3edbf6f88332a579bdac:5961efbfbf4c56b8745347e7a663a32f$" "smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

@ -6,7 +6,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler"; import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat"; 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 PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims"; import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
@ -585,7 +585,7 @@ export class EncounterPhase extends BattlePhase {
doSummon(); doSummon();
else { else {
let message: string; 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); this.scene.ui.showDialogue(message, trainer.getName(), null, doSummon);
} }
} }
@ -1699,9 +1699,12 @@ export class BattleEndPhase extends BattlePhase {
this.scene.clearEnemyHeldItemModifiers(); 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) { 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); this.scene.removeModifier(m);
} }
@ -2322,7 +2325,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
}; };
this.executeForAll((pokemon: Pokemon) => { 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) if (!immune)
inflictDamage(pokemon); inflictDamage(pokemon);
}); });
@ -2753,7 +2756,7 @@ export class TrainerVictoryPhase extends BattlePhase {
let showMessageAndEnd = () => this.end(); let showMessageAndEnd = () => this.end();
if (victoryMessages?.length) { if (victoryMessages?.length) {
let message: string; 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()); const messagePages = message.split(/\$/g).map(m => m.trim());
for (let p = messagePages.length - 1; p >= 0; p--) { for (let p = messagePages.length - 1; p >= 0; p--) {

View File

@ -4,7 +4,7 @@ import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies, speciesStarters } from './data/pokemon-species'; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies, speciesStarters } from './data/pokemon-species';
import * as Utils from './utils'; 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 { PokeballType } from './data/pokeball';
import { initAutoPlay } from './system/auto-play'; import { initAutoPlay } from './system/auto-play';
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from './data/battle-anims'; 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 { SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from './data/pokemon-forms';
import { FormChangePhase, QuietFormChangePhase } from './form-change-phase'; import { FormChangePhase, QuietFormChangePhase } from './form-change-phase';
import { BattleSpec } from './enums/battle-spec'; import { BattleSpec } from './enums/battle-spec';
import { getTypeRgb } from './data/type';
import PokemonSpriteSparkleHandler from './sprite/pokemon-sprite-sparkle-handler';
const enableAuto = true; const enableAuto = true;
const quickStart = false; const quickStart = false;
@ -141,6 +143,8 @@ export default class BattleScene extends Phaser.Scene {
public seed: string; public seed: string;
public waveSeed: string; public waveSeed: string;
private spriteSparkleHandler: PokemonSpriteSparkleHandler;
public fieldSpritePipeline: FieldSpritePipeline; public fieldSpritePipeline: FieldSpritePipeline;
public spritePipeline: SpritePipeline; public spritePipeline: SpritePipeline;
@ -235,6 +239,8 @@ export default class BattleScene extends Phaser.Scene {
this.loadImage('achv_bar_4', 'ui'); this.loadImage('achv_bar_4', 'ui');
this.loadImage('shiny_star', 'ui', 'shiny.png'); this.loadImage('shiny_star', 'ui', 'shiny.png');
this.loadImage('icon_spliced', 'ui'); 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_player', 'ui');
this.loadImage('pb_tray_overlay_enemy', '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.loadImage(`pkmn__sub`, 'pokemon', 'sub.png');
this.loadAtlas('battle_stats', 'effects'); this.loadAtlas('battle_stats', 'effects');
this.loadAtlas('shiny', 'effects'); this.loadAtlas('shiny', 'effects');
this.loadImage('tera', 'effects');
this.loadAtlas('pb_particles', 'effects'); this.loadAtlas('pb_particles', 'effects');
this.loadImage('evo_sparkle', 'effects'); this.loadImage('evo_sparkle', 'effects');
this.loadAtlas('tera_sparkle', 'effects');
this.load.video('evo_bg', 'images/effects/evo_bg.mp4', true); this.load.video('evo_bg', 'images/effects/evo_bg.mp4', true);
this.loadAtlas('pb', ''); this.loadAtlas('pb', '');
@ -501,6 +509,9 @@ export default class BattleScene extends Phaser.Scene {
this.updateUIPositions(); this.updateUIPositions();
this.spriteSparkleHandler = new PokemonSpriteSparkleHandler();
this.spriteSparkleHandler.setup(this);
this.party = []; this.party = [];
let loadPokemonAssets = []; let loadPokemonAssets = [];
@ -537,6 +548,15 @@ export default class BattleScene extends Phaser.Scene {
showOnStart: true 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(); this.reset();
if (this.quickStart) { if (this.quickStart) {
@ -1036,6 +1056,18 @@ export default class BattleScene extends Phaser.Scene {
return ret; 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> { showFieldOverlay(duration: integer): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
this.tweens.add({ this.tweens.add({
@ -1460,10 +1492,13 @@ export default class BattleScene extends Phaser.Scene {
return new Promise(resolve => { return new Promise(resolve => {
const soundName = modifier.type.soundName; const soundName = modifier.type.soundName;
this.validateAchvs(ModifierAchv, modifier); this.validateAchvs(ModifierAchv, modifier);
const modifiersToRemove: PersistentModifier[] = [];
if (modifier instanceof 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 as PersistentModifier).add(this.modifiers, !!virtual, this)) {
if (modifier instanceof PokemonFormChangeItemModifier) if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier)
modifier.apply([ this.getPokemonById(modifier.pokemonId) ]); modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]);
if (playSound && !this.sound.get(soundName)) if (playSound && !this.sound.get(soundName))
this.playSound(soundName); this.playSound(soundName);
} else if (!virtual) { } 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); 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()); return this.addModifier(defaultModifierType.newModifier(), ignoreUpdate, playSound, false, instant).then(() => resolve());
} }
for (let rm of modifiersToRemove)
this.removeModifier(rm);
if (!ignoreUpdate && !virtual) if (!ignoreUpdate && !virtual)
return this.updateModifiers(true, instant).then(() => resolve()); 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 => { 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) if (!ignoreUpdate)
this.updateModifiers(false, instant).then(() => resolve()); this.updateModifiers(false, instant).then(() => resolve());
else else
@ -1586,7 +1632,15 @@ export default class BattleScene extends Phaser.Scene {
else else
modifierChance = !isBoss ? 12 : 4; 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; let pokemonModifierChance = modifierChance;
if (this.currentBattle.battleType === BattleType.TRAINER) if (this.currentBattle.battleType === BattleType.TRAINER)
pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); 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); const modifierIndex = modifiers.indexOf(modifier);
if (modifierIndex > -1) { if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1); modifiers.splice(modifierIndex, 1);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier)
modifier.apply([ this.getPokemonById(modifier.pokemonId), false ]);
return true; return true;
} }
return false; return false;
} }
getModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean): PersistentModifier[] { getModifiers(modifierType: { new(...args: any[]): Modifier }, player: boolean = true): PersistentModifier[] {
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType); return (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType);
} }
findModifiers(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier[] { findModifiers(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier[] {
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m)); return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m));
} }
findModifier(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier { findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier {
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m)); return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m));
} }
applyModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean, ...args: any[]): void { applyModifiers(modifierType: { new(...args: any[]): Modifier }, player: boolean = true, ...args: any[]): void {
if (player === undefined)
player = true;
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
for (let modifier of modifiers) { for (let modifier of modifiers) {
if (modifier.apply(args)) 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 { applyModifier(modifierType: { new(...args: any[]): Modifier }, player: boolean = true, ...args: any[]): PersistentModifier {
if (player === undefined)
player = true;
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
for (let modifier of modifiers) { for (let modifier of modifiers) {
if (modifier.apply(args)) { if (modifier.apply(args)) {

View File

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

View File

@ -322,7 +322,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) { if (hitResult < HitResult.NO_EFFECT) {
const type = move.getMove().type; const type = move.getMove().type;
const pokemonTypes = pokemon.getTypes(); const pokemonTypes = pokemon.getTypes(true);
if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) { if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) {
pokemon.summonData.types = [ type ]; pokemon.summonData.types = [ type ];
return true; return true;
@ -333,7 +333,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
} }
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string { 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(); const targets = pokemon.getOpponents();
let target: Pokemon; let target: Pokemon;
if (targets.length > 1) 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 else
target = targets[0]; target = targets[0];

View File

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

View File

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

View File

@ -1813,7 +1813,7 @@ export class CopyTypeAttr extends MoveEffectAttr {
if (!super.apply(user, target, move, args)) if (!super.apply(user, target, move, args))
return false; 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!`)); user.scene.queueMessage(getPokemonMessage(user, `'s type\nchanged to match ${target.name}'s!`));

View File

@ -841,7 +841,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GOGOAT, 32, null, null) new SpeciesEvolution(Species.GOGOAT, 32, null, null)
], ],
[Species.PANCHAM]: [ [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]: [ [Species.ESPURR]: [
new SpeciesFormEvolution(Species.MEOWSTIC, '', '', 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)), new SpeciesFormEvolution(Species.MEOWSTIC, '', '', 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)),

View File

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

View File

@ -11,6 +11,7 @@ import { Species } from "./enums/species";
import { tmSpecies } from "./tms"; import { tmSpecies } from "./tms";
import { Type } from "./type"; import { Type } from "./type";
import { initTrainerTypeDialogue } from "./dialogue"; import { initTrainerTypeDialogue } from "./dialogue";
import { PersistentModifier, TerastallizeModifier } from "../modifier/modifier";
export enum TrainerPoolTier { export enum TrainerPoolTier {
COMMON, COMMON,
@ -163,6 +164,7 @@ export const trainerPartyTemplates = {
type PartyTemplateFunc = (scene: BattleScene) => TrainerPartyTemplate; type PartyTemplateFunc = (scene: BattleScene) => TrainerPartyTemplate;
type PartyMemberFunc = (scene: BattleScene, level: integer) => EnemyPokemon; type PartyMemberFunc = (scene: BattleScene, level: integer) => EnemyPokemon;
type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
export interface PartyMemberFuncs { export interface PartyMemberFuncs {
[key: integer]: PartyMemberFunc [key: integer]: PartyMemberFunc
@ -183,6 +185,7 @@ export class TrainerConfig {
public encounterBgm: string; public encounterBgm: string;
public femaleEncounterBgm: string; public femaleEncounterBgm: string;
public victoryBgm: string; public victoryBgm: string;
public genModifiersFunc: GenModifiersFunc;
public modifierRewardFuncs: ModifierTypeFunc[] = []; public modifierRewardFuncs: ModifierTypeFunc[] = [];
public partyTemplates: TrainerPartyTemplate[]; public partyTemplates: TrainerPartyTemplate[];
public partyTemplateFunc: PartyTemplateFunc; public partyTemplateFunc: PartyTemplateFunc;
@ -325,11 +328,16 @@ export class TrainerConfig {
return this; return this;
} }
setGenModifiersFunc(genModifiersFunc: GenModifiersFunc): TrainerConfig {
this.genModifiersFunc = genModifiersFunc;
return this;
}
setModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig { setModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig {
this.modifierRewardFuncs = modifierTypeFuncs.map(func => () => { this.modifierRewardFuncs = modifierTypeFuncs.map(func => () => {
const modifierTypeFunc = func(); const modifierTypeFunc = func();
const modifierType = modifierTypeFunc(); const modifierType = modifierTypeFunc();
modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc); modifierType.withIdFromFunc(modifierTypeFunc);
return modifierType; return modifierType;
}); });
return this; return this;
@ -352,6 +360,10 @@ export class TrainerConfig {
this.setStaticParty(); this.setStaticParty();
this.setBattleBgm('battle_gym'); this.setBattleBgm('battle_gym');
this.setVictoryBgm('victory_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; return this;
} }
@ -373,6 +385,7 @@ export class TrainerConfig {
this.setStaticParty(); this.setStaticParty();
this.setBattleBgm('battle_elite'); this.setBattleBgm('battle_elite');
this.setVictoryBgm('victory_gym'); this.setVictoryBgm('victory_gym');
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null));
return this; return this;
} }
@ -390,6 +403,7 @@ export class TrainerConfig {
this.setStaticParty(); this.setStaticParty();
this.setBattleBgm('battle_champion'); this.setBattleBgm('battle_champion');
this.setVictoryBgm('victory_champion'); this.setVictoryBgm('victory_champion');
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 3));
return this; return this;
} }
@ -447,7 +461,7 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
function getRandomPartyMemberFunc(speciesPool: Species[], postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc { function getRandomPartyMemberFunc(speciesPool: Species[], postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
return (scene: BattleScene, level: integer) => { 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); 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 = { export const trainerConfigs: TrainerConfigs = {
[TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(), [TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(),
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER) [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(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(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)) .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) [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(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(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 => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.RAYQUAZA ], p => {
p.setBoss(); p.setBoss();
p.pokeball = PokeballType.MASTER_BALL; 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) [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(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 ])) .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.setBoss();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
p.formIndex = 1; 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() { (function() {

View File

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

View File

@ -8,6 +8,7 @@ import { Mode } from "./ui/ui";
import { LearnMovePhase } from "./battle-phases"; import { LearnMovePhase } from "./battle-phases";
import { cos, sin } from "./anims"; import { cos, sin } from "./anims";
import { PlayerPokemon } from "./pokemon"; import { PlayerPokemon } from "./pokemon";
import { getTypeRgb } from "./data/type";
export class EvolutionPhase extends BattlePhase { export class EvolutionPhase extends BattlePhase {
protected pokemon: PlayerPokemon; protected pokemon: PlayerPokemon;
@ -70,7 +71,7 @@ export class EvolutionPhase extends BattlePhase {
this.evolutionBgOverlay.setAlpha(0); this.evolutionBgOverlay.setAlpha(0);
this.evolutionContainer.add(this.evolutionBgOverlay); 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.pokemonSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonTintSprite = 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 => { [ this.pokemonSprite, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite ].map(sprite => {
sprite.play(this.pokemon.getSpriteKey(true)); 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; sprite.pipelineData['ignoreTimeTint'] = true;
[ 'spriteColors', 'fusionSpriteColors' ].map(k => { [ 'spriteColors', 'fusionSpriteColors' ].map(k => {
if (this.pokemon.summonData?.speciesForm) if (this.pokemon.summonData?.speciesForm)

View File

@ -10,6 +10,7 @@ import PartyUiHandler from "./ui/party-ui-handler";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
import { BattleSpec } from "./enums/battle-spec"; import { BattleSpec } from "./enums/battle-spec";
import { MovePhase, PokemonHealPhase } from "./battle-phases"; import { MovePhase, PokemonHealPhase } from "./battle-phases";
import { getTypeRgb } from "./data/type";
export class FormChangePhase extends EvolutionPhase { export class FormChangePhase extends EvolutionPhase {
private formChange: SpeciesFormChange; private formChange: SpeciesFormChange;
@ -184,10 +185,10 @@ export class QuietFormChangePhase extends BattlePhase {
const preName = this.pokemon.name; const preName = this.pokemon.name;
const getPokemonSprite = () => { 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.setOrigin(0.5, 1);
sprite.play(this.pokemon.getSpriteKey()).stop(); 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 => { [ 'spriteColors', 'fusionSpriteColors' ].map(k => {
if (this.pokemon.summonData?.speciesForm) if (this.pokemon.summonData?.speciesForm)
k += 'Base'; k += 'Base';

View File

@ -59,6 +59,11 @@ export class ModifierType {
this.tier = tier; this.tier = tier;
} }
withIdFromFunc(func: ModifierTypeFunc): ModifierType {
this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func);
return this;
}
newModifier(...args: any[]): Modifier { newModifier(...args: any[]): Modifier {
return this.newModifierFunc(this, args); 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 { export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemModifierType {
constructor(name: string, chancePercent: integer, iconImage?: string, group?: string, soundName?: string) { 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); 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(), FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(),
MEGA_BRACELET: () => new ModifierType('Mega Bracelet', 'Mega stones become available', (type, _args) => new Modifiers.MegaEvolutionAccessModifier(type)), 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)), 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(), 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[]) => { BERRY: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) if (pregenArgs)
return new BerryModifierType(pregenArgs[0] as BerryType); return new BerryModifierType(pregenArgs[0] as BerryType);
@ -902,6 +936,7 @@ const modifierPool = {
return Math.min(Math.ceil(highestPartyLevel / 20), 4); return Math.min(Math.ceil(highestPartyLevel / 20), 4);
}), }),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), 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.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), 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; }), ].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
@ -934,6 +969,7 @@ const modifierPool = {
new WeightedModifierType(modifierTypes.OVAL_CHARM, 2), new WeightedModifierType(modifierTypes.OVAL_CHARM, 2),
new WeightedModifierType(modifierTypes.ABILITY_CHARM, 2), new WeightedModifierType(modifierTypes.ABILITY_CHARM, 2),
new WeightedModifierType(modifierTypes.IV_SCANNER, 2), new WeightedModifierType(modifierTypes.IV_SCANNER, 2),
new WeightedModifierType(modifierTypes.TERA_ORB, 3),
new WeightedModifierType(modifierTypes.EXP_BALANCE, 1), new WeightedModifierType(modifierTypes.EXP_BALANCE, 1),
new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, 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), 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) => { pool[t].reduce((total: integer, modifierType: WeightedModifierType) => {
const weightedModifierType = modifierType as WeightedModifierType; const weightedModifierType = modifierType as WeightedModifierType;
const existingModifiers = party[0].scene.findModifiers(m => (m.type.generatorId || m.type.id) === weightedModifierType.modifierType.id, player); 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 instanceof Function
? (weightedModifierType.weight as Function)(party) ? (weightedModifierType.weight as Function)(party)
: weightedModifierType.weight as integer : weightedModifierType.weight as integer

View File

@ -265,7 +265,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
this.battlesLeft = battlesLeft; this.battlesLeft = battlesLeft;
} }
lapse(): boolean { lapse(args: any[]): boolean {
return !!--this.battlesLeft; return !!--this.battlesLeft;
} }
@ -281,6 +281,10 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
return container; return container;
} }
getBattlesLeft(): integer {
return this.battlesLeft;
}
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number { getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number {
return 99; 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 { export abstract class PokemonHeldItemModifier extends PersistentModifier {
public pokemonId: integer; public pokemonId: integer;
@ -481,6 +503,85 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
abstract getMaxHeldItemCount(pokemon: Pokemon): integer 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 { export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
protected stat: Stat; protected stat: Stat;
@ -1323,8 +1424,19 @@ export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier {
apply(args: any[]): boolean { apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon; 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 { getMaxHeldItemCount(pokemon: Pokemon): integer {

View File

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

View File

@ -23,12 +23,16 @@ uniform int isOutside;
uniform vec3 dayTint; uniform vec3 dayTint;
uniform vec3 duskTint; uniform vec3 duskTint;
uniform vec3 nightTint; uniform vec3 nightTint;
uniform float teraTime;
uniform vec3 teraColor;
uniform int hasShadow; uniform int hasShadow;
uniform int yCenter; uniform int yCenter;
uniform float fieldScale; uniform float fieldScale;
uniform float vCutoff; uniform float vCutoff;
uniform vec2 relPosition; uniform vec2 relPosition;
uniform vec2 texFrameUv;
uniform vec2 size; uniform vec2 size;
uniform vec2 texSize;
uniform float yOffset; uniform float yOffset;
uniform vec4 tone; uniform vec4 tone;
uniform ivec4 spriteColors[32]; uniform ivec4 spriteColors[32];
@ -48,11 +52,107 @@ vec3 blendHardLight(vec3 base, vec3 blend) {
return blendOverlay(blend, base); return blendOverlay(blend, base);
} }
void main() float hue2rgb(float f1, float f2, float hue) {
{ if (hue < 0.0)
vec4 texture; 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++) { for (int i = 0; i < 32; i++) {
if (spriteColors[i][3] == 0) if (spriteColors[i][3] == 0)
@ -73,6 +173,24 @@ void main()
// Multiply texture tint // Multiply texture tint
vec4 color = texture * texel; 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) { if (outTintEffect == 1.0) {
// Solid color + texture alpha // Solid color + texture alpha
color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a); 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); 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) { if (hasShadow == 1) {
@ -144,13 +262,11 @@ void main()
`; `;
const spriteVertShader = ` const spriteVertShader = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float; precision mediump float;
#endif
uniform mat4 uProjectionMatrix; uniform mat4 uProjectionMatrix;
uniform int uRoundPixels;
uniform vec2 uResolution;
attribute vec2 inPosition; attribute vec2 inPosition;
attribute vec2 inTexCoord; attribute vec2 inTexCoord;
@ -159,6 +275,7 @@ attribute float inTintEffect;
attribute vec4 inTint; attribute vec4 inTint;
varying vec2 outTexCoord; varying vec2 outTexCoord;
varying vec2 outtexFrameUv;
varying float outTexId; varying float outTexId;
varying vec2 outPosition; varying vec2 outPosition;
varying float outTintEffect; varying float outTintEffect;
@ -167,7 +284,10 @@ varying vec4 outTint;
void main() void main()
{ {
gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0); 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; outTexCoord = inTexCoord;
outTexId = inTexId; outTexId = inTexId;
outPosition = inPosition; outPosition = inPosition;
@ -193,10 +313,14 @@ export default class SpritePipeline extends FieldSpritePipeline {
onPreRender(): void { onPreRender(): void {
super.onPreRender(); super.onPreRender();
this.set1f('teraTime', 0);
this.set3fv('teraColor', [ 0, 0, 0 ]);
this.set1i('hasShadow', 0); this.set1i('hasShadow', 0);
this.set1i('yCenter', 0); this.set1i('yCenter', 0);
this.set2f('relPosition', 0, 0); this.set2f('relPosition', 0, 0);
this.set2f('texFrameUv', 0, 0);
this.set2f('size', 0, 0); this.set2f('size', 0, 0);
this.set2f('texSize', 0, 0);
this.set1f('yOffset', 0); this.set1f('yOffset', 0);
this.set4fv('tone', this._tone); this.set4fv('tone', this._tone);
} }
@ -208,6 +332,7 @@ export default class SpritePipeline extends FieldSpritePipeline {
const data = sprite.pipelineData; const data = sprite.pipelineData;
const tone = data['tone'] as number[]; const tone = data['tone'] as number[];
const teraColor = data['teraColor'] as integer[] ?? [ 0, 0, 0 ];
const hasShadow = data['hasShadow'] as boolean; const hasShadow = data['hasShadow'] as boolean;
const ignoreOverride = data['ignoreOverride'] as boolean; const ignoreOverride = data['ignoreOverride'] as boolean;
const spriteColors = (ignoreOverride && data['spriteColorsBase']) || data['spriteColors'] || [] as number[][]; 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 isEntityObj = sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer;
const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer; const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer;
const fieldScaleRatio = field.scale / 6;
const position = isEntityObj const position = isEntityObj
? [ sprite.parentContainer.x, sprite.parentContainer.y ] ? [ sprite.parentContainer.x, sprite.parentContainer.y ]
: [ sprite.x, sprite.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; position[0] += -(sprite.width - (sprite.frame.width)) / 2 + sprite.frame.x;
if (sprite.originY === 0.5) if (sprite.originY === 0.5)
position[1] += (sprite.height / 2) * ((isEntityObj ? sprite.parentContainer : sprite).scale - 1); 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('hasShadow', hasShadow ? 1 : 0);
this.set1i('yCenter', sprite.originY === 0.5 ? 1 : 0); this.set1i('yCenter', sprite.originY === 0.5 ? 1 : 0);
this.set1f('fieldScale', field.scale); this.set1f('fieldScale', field.scale);
this.set2f('relPosition', position[0], position[1]); 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('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.set1f('yOffset', sprite.height - sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale));
this.set4fv('tone', tone); this.set4fv('tone', tone);
this.bindTexture(this.game.textures.get('tera').source[0].glTexture, 1);
const emptyColors = [ 0, 0, 0, 0 ]; const emptyColors = [ 0, 0, 0, 0 ];
const flatSpriteColors: integer[] = []; const flatSpriteColors: integer[] = [];
const flatFusionSpriteColors: integer[] = []; const flatFusionSpriteColors: integer[] = [];

View File

@ -2,13 +2,13 @@ import Phaser from 'phaser';
import BattleScene, { AnySound } from './battle-scene'; import BattleScene, { AnySound } from './battle-scene';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info';
import { Moves } from "./data/enums/moves"; 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 { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies } from './data/pokemon-species';
import * as Utils from './utils'; 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 { getLevelTotalExp } from './data/exp';
import { Stat } from './data/pokemon-stat'; 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 { PokeballType } from './data/pokeball';
import { Gender } from './data/gender'; import { Gender } from './data/gender';
import { initMoveAnim, loadMoveAnimAssets } from './data/battle-anims'; 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); this.scene.fieldUI.addAt(this.battleInfo, 0);
const getSprite = (hasShadow?: boolean) => { 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.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; return ret;
}; };
@ -406,6 +406,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return 1; return 1;
} }
updateSpritePipelineData(): void {
[ this.getSprite(), this.getTintSprite() ].map(s => s.pipelineData['teraColor'] = getTypeRgb(this.getTeraType()));
this.updateInfo(true);
}
initShinySparkle(): void { initShinySparkle(): void {
const shinySparkle = this.scene.addFieldSprite(0, 0, 'shiny'); const shinySparkle = this.scene.addFieldSprite(0, 0, 'shiny');
shinySparkle.setVisible(false); 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); 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 = []; const types = [];
if (!ignoreOverride && this.summonData?.types) if (includeTeraType) {
this.summonData.types.forEach(t => types.push(t)); const teraType = this.getTeraType();
else { if (teraType != Type.UNKNOWN)
const speciesForm = this.getSpeciesForm(); types.push(teraType);
} else {
types.push(speciesForm.type1); 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(); const fusionSpeciesForm = this.getFusionSpeciesForm();
if (fusionSpeciesForm) { if (fusionSpeciesForm) {
if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1) if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1)
types.push(fusionSpeciesForm.type2); types.push(fusionSpeciesForm.type2);
else if (fusionSpeciesForm.type1 !== speciesForm.type1) else if (fusionSpeciesForm.type1 !== speciesForm.type1)
types.push(fusionSpeciesForm.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)) { 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) { isOfType(type: Type) {
return this.getTypes().indexOf(type) > -1; return this.getTypes(true).indexOf(type) > -1;
} }
getAbility(): Ability { getAbility(): Ability {
@ -677,14 +688,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return weight.value; 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 { 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; return getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
} }
getMatchupScore(pokemon: Pokemon): number { getMatchupScore(pokemon: Pokemon): number {
const types = this.getTypes(); const types = this.getTypes(true);
const enemyTypes = pokemon.getTypes(); const enemyTypes = pokemon.getTypes(true);
let atkScore = pokemon.getAttackMoveEffectiveness(types[0]); let atkScore = pokemon.getAttackMoveEffectiveness(types[0]);
let defScore = 1 / this.getAttackMoveEffectiveness(enemyTypes[0]); let defScore = 1 / this.getAttackMoveEffectiveness(enemyTypes[0]);
if (types.length > 1) if (types.length > 1)
@ -965,7 +991,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
const typeless = !!move.getAttrs(TypelessAttr).length; const typeless = !!move.getAttrs(TypelessAttr).length;
const types = this.getTypes(); const types = this.getTypes(true);
const typeMultiplier = new Utils.NumberHolder(!typeless && moveCategory !== MoveCategory.STATUS const typeMultiplier = new Utils.NumberHolder(!typeless && moveCategory !== MoveCategory.STATUS
? getTypeDamageMultiplier(move.type, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(move.type, types[1]) : 1) ? getTypeDamageMultiplier(move.type, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(move.type, types[1]) : 1)
: 1); : 1);
@ -977,6 +1003,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
case MoveCategory.SPECIAL: case MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveCategory.PHYSICAL; const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const power = new Utils.NumberHolder(move.power); 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); applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power);
if (!typeless) 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 sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this);
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source); 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 criticalMultiplier = isCritical ? 2 : 1;
const isTypeImmune = (typeMultiplier.value * weatherTypeMultiplier) === 0; 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); applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType)
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
if (!isTypeImmune) { 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; 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) if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)

View File

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

View File

@ -125,6 +125,9 @@ export const achvs = {
TRANSFER_MAX_BATTLE_STAT: new Achv('Teamwork', 'Baton pass to another party member with at least one stat maxed out', 'stick', 20), 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), 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), 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), 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(), 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(), CATCH_MYTHICAL: new Achv('Mythical', 'Catch a mythical Pokémon', 'strange_ball', 50).setSecret(),

View File

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

View File

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

View File

@ -5,6 +5,7 @@ import { addTextObject, TextStyle } from './text';
import { getGenderSymbol, getGenderColor, Gender } from '../data/gender'; import { getGenderSymbol, getGenderColor, Gender } from '../data/gender';
import { StatusEffect } from '../data/status-effect'; import { StatusEffect } from '../data/status-effect';
import BattleScene from '../battle-scene'; import BattleScene from '../battle-scene';
import { Type, getTypeRgb } from '../data/type';
export default class BattleInfo extends Phaser.GameObjects.Container { export default class BattleInfo extends Phaser.GameObjects.Container {
private player: boolean; private player: boolean;
@ -13,6 +14,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
private bossSegments: integer; private bossSegments: integer;
private offset: boolean; private offset: boolean;
private lastName: string; private lastName: string;
private lastTeraType: Type;
private lastStatus: StatusEffect; private lastStatus: StatusEffect;
private lastHp: integer; private lastHp: integer;
private lastMaxHp: integer; private lastMaxHp: integer;
@ -26,6 +28,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
private nameText: Phaser.GameObjects.Text; private nameText: Phaser.GameObjects.Text;
private genderText: Phaser.GameObjects.Text; private genderText: Phaser.GameObjects.Text;
private ownedIcon: Phaser.GameObjects.Sprite; private ownedIcon: Phaser.GameObjects.Sprite;
private teraIcon: Phaser.GameObjects.Sprite;
private splicedIcon: Phaser.GameObjects.Sprite; private splicedIcon: Phaser.GameObjects.Sprite;
private shinyIcon: Phaser.GameObjects.Sprite; private shinyIcon: Phaser.GameObjects.Sprite;
private statusIndicator: Phaser.GameObjects.Sprite; private statusIndicator: Phaser.GameObjects.Sprite;
@ -43,6 +46,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.boss = false; this.boss = false;
this.offset = false; this.offset = false;
this.lastName = null; this.lastName = null;
this.lastTeraType = Type.UNKNOWN;
this.lastStatus = StatusEffect.NONE; this.lastStatus = StatusEffect.NONE;
this.lastHp = -1; this.lastHp = -1;
this.lastMaxHp = -1; this.lastMaxHp = -1;
@ -75,11 +79,19 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.add(this.ownedIcon); 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 = this.scene.add.sprite(0, 0, 'icon_spliced');
this.splicedIcon.setVisible(false); this.splicedIcon.setVisible(false);
this.splicedIcon.setOrigin(0, 0); this.splicedIcon.setOrigin(0, 0);
this.splicedIcon.setPositionRelative(this.nameText, 0, 2); 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.add(this.splicedIcon);
this.statusIndicator = this.scene.add.sprite(0, 0, 'statuses'); 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.setColor(getGenderColor(pokemon.gender));
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0); 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); this.splicedIcon.setVisible(!!pokemon.fusionSpecies);
if (this.splicedIcon.visible) { 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)}`)); 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) { if (boss !== this.boss) {
this.boss = 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.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1); this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? '_boss' : ''}`); this.hpBar.setTexture(`overlay_hp${boss ? '_boss' : ''}`);
@ -240,12 +262,25 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
if (!this.scene) if (!this.scene)
return resolve(); return resolve();
if (this.lastName !== pokemon.name) { const nameUpdated = this.lastName !== pokemon.name;
if (nameUpdated) {
this.updateNameText(pokemon); this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth; this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 1);
} }
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)) { if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE; this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;

View File

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

View File

@ -1,6 +1,6 @@
import BattleScene, { Button } from "../battle-scene"; import BattleScene, { Button } from "../battle-scene";
import { Mode } from "./ui"; 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 { TextStyle, addTextObject } from "./text";
import MessageUiHandler from "./message-ui-handler"; import MessageUiHandler from "./message-ui-handler";
import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg"; import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg";

View File

@ -11,7 +11,7 @@ import { allMoves } from "../data/move";
import { Moves } from "../data/enums/moves"; import { Moves } from "../data/enums/moves";
import { getGenderColor, getGenderSymbol } from "../data/gender"; import { getGenderColor, getGenderSymbol } from "../data/gender";
import { StatusEffect } from "../data/status-effect"; 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 { pokemonEvolutions } from "../data/pokemon-evolutions";
import { addWindow } from "./window"; import { addWindow } from "./window";
import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
@ -102,7 +102,7 @@ export default class PartyUiHandler extends MessageUiHandler {
private static FilterAllMoves = (_pokemonMove: PokemonMove) => null; private static FilterAllMoves = (_pokemonMove: PokemonMove) => null;
public static FilterItemMaxStacks = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => { 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)) if (matchingModifier && matchingModifier.stackCount === matchingModifier.getMaxStackCount(pokemon.scene))
return `${pokemon.name} has too many\nof this item!`; return `${pokemon.name} has too many\nof this item!`;
return null; return null;

View File

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

View File

@ -3,7 +3,7 @@ import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { PlayerPokemon } from "../pokemon"; import { PlayerPokemon } from "../pokemon";
import { Type } from "../data/type"; import { Type, getTypeRgb } from "../data/type";
import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "./text"; import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "./text";
import Move, { MoveCategory } from "../data/move"; import Move, { MoveCategory } from "../data/move";
import { getPokeballAtlasKey } from "../data/pokeball"; import { getPokeballAtlasKey } from "../data/pokeball";
@ -98,8 +98,7 @@ export default class SummaryUiHandler extends UiHandler {
this.numberText.setOrigin(0, 1); this.numberText.setOrigin(0, 1);
this.summaryContainer.add(this.numberText); this.summaryContainer.add(this.numberText);
this.pokemonSprite = this.scene.add.sprite(56, -106, `pkmn__sub`); this.pokemonSprite = this.scene.initPokemonSprite(this.scene.add.sprite(56, -106, `pkmn__sub`), null, false, true);
this.pokemonSprite.setPipeline(this.scene.spritePipeline, { ignoreOverride: true, tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false });
this.summaryContainer.add(this.pokemonSprite); this.summaryContainer.add(this.pokemonSprite);
this.nameText = addTextObject(this.scene, 6, -54, '', TextStyle.SUMMARY); 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.numberText.setShadowColor(getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
this.pokemonSprite.play(this.pokemon.getSpriteKey(true)); this.pokemonSprite.play(this.pokemon.getSpriteKey(true));
this.pokemonSprite.pipelineData['teraColor'] = getTypeRgb(this.pokemon.getTeraType());
this.pokemonSprite.pipelineData['ignoreTimeTint'] = true; this.pokemonSprite.pipelineData['ignoreTimeTint'] = true;
[ 'spriteColors', 'fusionSpriteColors' ].map(k => { [ 'spriteColors', 'fusionSpriteColors' ].map(k => {
delete this.pokemonSprite.pipelineData[`${k}Base`]; delete this.pokemonSprite.pipelineData[`${k}Base`];
@ -489,16 +489,26 @@ export default class SummaryUiHandler extends UiHandler {
typeLabel.setOrigin(0, 0); typeLabel.setOrigin(0, 0);
profileContainer.add(typeLabel); profileContainer.add(typeLabel);
const getTypeIcon = (index: integer, type: Type) => { const getTypeIcon = (index: integer, type: Type, tera: boolean = false) => {
const typeIcon = this.scene.add.sprite(39 + 34 * index, 42, 'types', Type[type].toLowerCase()); 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); typeIcon.setOrigin(0, 1);
return typeIcon; return typeIcon;
}; };
const types = this.pokemon.getTypes(true); const types = this.pokemon.getTypes(false, true);
profileContainer.add(getTypeIcon(0, types[0])); profileContainer.add(getTypeIcon(0, types[0]));
if (types.length > 1) if (types.length > 1)
profileContainer.add(getTypeIcon(1, types[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(); 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); const memoText = addBBCodeTextObject(this.scene, 7, 113, memoString, TextStyle.WINDOW);
memoText.setOrigin(0, 0); memoText.setOrigin(0, 0);

View File

@ -72,6 +72,18 @@ export function randIntRange(min: integer, max: integer): integer {
return randInt(max - min, min); 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 { export function getSunday(date: Date): Date {
const day = date.getDay(), const day = date.getDay(),
diff = date.getDate() - day; diff = date.getDate() - day;