Adds ribbon in starter screen if mon has beat classic mode (#370)
* adds the ribbon asset, hooking it up * works if override. need to add field on server side I imagine * moves count to starterData, increments on win * formatting * increment works properly * recursively check for prevolution * cleaned up to use getRootSpeciesId() * changes ribbon to gold medal version * adds Akuma's ribbon achievements * ribbons increment correctly * missed ui handler update * reorder achievements * ribbon correct, vouchers not. currently investigating * increments properly, but voucher reward phase not appearing * some cleanup * works great, need to better reflect who is getting ribbon in message and cry * plays level fanfare, tabling cry for now * reran items.bat * Minor fixes --------- Co-authored-by: Flashfyre <flashfireex@gmail.com>pull/381/merge
10817
public/images/items.json
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 400 B |
After Width: | Height: | Size: 408 B |
After Width: | Height: | Size: 408 B |
After Width: | Height: | Size: 407 B |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 285 B |
After Width: | Height: | Size: 285 B |
|
@ -1708,18 +1708,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
});
|
||||
}
|
||||
|
||||
cry(soundConfig?: Phaser.Types.Sound.SoundConfig): AnySound {
|
||||
const cry = this.getSpeciesForm().cry(this.scene, soundConfig);
|
||||
cry(soundConfig?: Phaser.Types.Sound.SoundConfig, sceneOverride?: BattleScene): AnySound {
|
||||
const scene = sceneOverride || this.scene;
|
||||
const cry = this.getSpeciesForm().cry(scene, soundConfig);
|
||||
let duration = cry.totalDuration * 1000;
|
||||
if (this.fusionSpecies) {
|
||||
let fusionCry = this.getFusionSpeciesForm().cry(this.scene, soundConfig, true);
|
||||
let fusionCry = this.getFusionSpeciesForm().cry(scene, soundConfig, true);
|
||||
duration = Math.min(duration, fusionCry.totalDuration * 1000);
|
||||
fusionCry.destroy();
|
||||
this.scene.time.delayedCall(Utils.fixedInt(Math.ceil(duration * 0.4)), () => {
|
||||
scene.time.delayedCall(Utils.fixedInt(Math.ceil(duration * 0.4)), () => {
|
||||
try {
|
||||
SoundFade.fadeOut(this.scene, cry, Utils.fixedInt(Math.ceil(duration * 0.2)));
|
||||
fusionCry = this.getFusionSpeciesForm().cry(this.scene, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig));
|
||||
SoundFade.fadeIn(this.scene, fusionCry, Utils.fixedInt(Math.ceil(duration * 0.2)), this.scene.masterVolume * this.scene.seVolume, 0);
|
||||
SoundFade.fadeOut(scene, cry, Utils.fixedInt(Math.ceil(duration * 0.2)));
|
||||
fusionCry = this.getFusionSpeciesForm().cry(scene, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig));
|
||||
SoundFade.fadeIn(scene, fusionCry, Utils.fixedInt(Math.ceil(duration * 0.2)), scene.masterVolume * scene.seVolume, 0);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ export class LoadingScene extends SceneBase {
|
|||
this.loadImage('shiny_star_small_1', 'ui', 'shiny_small_1.png');
|
||||
this.loadImage('shiny_star_small_2', 'ui', 'shiny_small_2.png');
|
||||
this.loadImage('ha_capsule', 'ui', 'ha_capsule.png');
|
||||
this.loadImage('champion_ribbon', 'ui', 'champion_ribbon.png');
|
||||
this.loadImage('icon_spliced', 'ui');
|
||||
this.loadImage('icon_tera', 'ui');
|
||||
this.loadImage('type_tera', 'ui');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import BattleScene, { bypassLogin, startingWave } from "./battle-scene";
|
||||
import BattleScene, { AnySound, bypassLogin, startingWave } from "./battle-scene";
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
|
||||
import * as Utils from './utils';
|
||||
import { Moves } from "./data/enums/moves";
|
||||
|
@ -55,7 +55,7 @@ import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select
|
|||
import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler";
|
||||
import { fetchDailyRunSeed, getDailyRunStarters } from "./data/daily-run";
|
||||
import { GameModes, gameModes } from "./game-mode";
|
||||
import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
|
||||
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "./data/pokemon-species";
|
||||
import i18next from './plugins/i18n';
|
||||
import { Abilities } from "./data/enums/abilities";
|
||||
import { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE } from './overrides';
|
||||
|
@ -3472,8 +3472,40 @@ export class GameOverModifierRewardPhase extends ModifierRewardPhase {
|
|||
}
|
||||
}
|
||||
|
||||
export class RibbonModifierRewardPhase extends ModifierRewardPhase {
|
||||
private species: PokemonSpecies;
|
||||
|
||||
constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) {
|
||||
super(scene, modifierTypeFunc);
|
||||
|
||||
this.species = species;
|
||||
}
|
||||
|
||||
doReward(): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
const newModifier = this.modifierType.newModifier();
|
||||
this.scene.addModifier(newModifier).then(() => {
|
||||
this.scene.gameData.saveSystem().then(success => {
|
||||
if (success) {
|
||||
this.scene.playSound('level_up_fanfare');
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.arenaBg.setVisible(false);
|
||||
this.scene.ui.fadeIn(250).then(() => {
|
||||
this.scene.ui.showText(`${this.species.name} beat classic for the first time!\nYou received ${newModifier.type.name}!`, null, () => {
|
||||
resolve();
|
||||
}, null, true, 1500);
|
||||
});
|
||||
} else
|
||||
this.scene.reset(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class GameOverPhase extends BattlePhase {
|
||||
private victory: boolean;
|
||||
private firstRibbons: PokemonSpecies[] = [];
|
||||
|
||||
constructor(scene: BattleScene, victory?: boolean) {
|
||||
super(scene);
|
||||
|
@ -3525,6 +3557,13 @@ export class GameOverPhase extends BattlePhase {
|
|||
if (this.scene.gameMode.isClassic) {
|
||||
firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY);
|
||||
this.scene.gameData.gameStats.sessionsWon++;
|
||||
for (let pokemon of this.scene.getParty()) {
|
||||
this.awardRibbon(pokemon);
|
||||
|
||||
if (pokemon.species.getRootSpeciesId() != pokemon.species.getRootSpeciesId(true)) {
|
||||
this.awardRibbon(pokemon, true);
|
||||
}
|
||||
}
|
||||
} else if (this.scene.gameMode.isDaily && success[1])
|
||||
this.scene.gameData.gameStats.dailyRunSessionsWon++;
|
||||
}
|
||||
|
@ -3536,8 +3575,11 @@ export class GameOverPhase extends BattlePhase {
|
|||
this.scene.clearPhaseQueue();
|
||||
this.scene.ui.clearText();
|
||||
this.handleUnlocks();
|
||||
if (this.victory && !firstClear && success[1])
|
||||
if (this.victory && !firstClear && success[1]) {
|
||||
for (let species of this.firstRibbons)
|
||||
this.scene.unshiftPhase(new RibbonModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PLUS, species));
|
||||
this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM));
|
||||
}
|
||||
this.scene.reset();
|
||||
this.scene.unshiftPhase(new TitlePhase(this.scene));
|
||||
this.end();
|
||||
|
@ -3556,6 +3598,15 @@ export class GameOverPhase extends BattlePhase {
|
|||
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE));
|
||||
}
|
||||
}
|
||||
|
||||
awardRibbon(pokemon: Pokemon, forStarter: boolean = false): void {
|
||||
const speciesId = getPokemonSpecies(pokemon.species.speciesId)
|
||||
const speciesRibbonCount = this.scene.gameData.incrementRibbonCount(speciesId, forStarter);
|
||||
// first time classic win, award voucher
|
||||
if (speciesRibbonCount === 1) {
|
||||
this.firstRibbons.push(getPokemonSpecies(pokemon.species.getRootSpeciesId(forStarter)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UnlockPhase extends Phase {
|
||||
|
|
|
@ -51,9 +51,9 @@ export class Achv {
|
|||
}
|
||||
|
||||
getTier(): AchvTier {
|
||||
if (this.score >= 150)
|
||||
return AchvTier.MASTER;
|
||||
if (this.score >= 100)
|
||||
return AchvTier.MASTER;
|
||||
if (this.score >= 75)
|
||||
return AchvTier.ROGUE;
|
||||
if (this.score >= 50)
|
||||
return AchvTier.ULTRA;
|
||||
|
@ -73,6 +73,16 @@ export class MoneyAchv extends Achv {
|
|||
}
|
||||
}
|
||||
|
||||
export class RibbonAchv extends Achv {
|
||||
private ribbonAmount: integer;
|
||||
|
||||
constructor(name: string, ribbonAmount: integer, iconImage: string, score: integer) {
|
||||
super(name, `Accumulate a total of ${ribbonAmount.toLocaleString('en-US')} Ribbons`, iconImage, score, (scene: BattleScene, _args: any[]) => scene.gameData.gameStats.ribbonsOwned >= this.ribbonAmount);
|
||||
|
||||
this.ribbonAmount = ribbonAmount;
|
||||
}
|
||||
}
|
||||
|
||||
export class DamageAchv extends Achv {
|
||||
private damageAmount: integer;
|
||||
|
||||
|
@ -125,6 +135,11 @@ export const achvs = {
|
|||
LV_100: new LevelAchv('But Wait, There\'s More!', 100, 'rare_candy', 25).setSecret(),
|
||||
LV_250: new LevelAchv('Elite', 250, 'rarer_candy', 50).setSecret(true),
|
||||
LV_1000: new LevelAchv('To Go Even Further Beyond', 1000, 'candy_jar', 100).setSecret(true),
|
||||
_10_RIBBONS: new RibbonAchv('Pokémon League Champion', 10, 'bronze_ribbon', 10),
|
||||
_25_RIBBONS: new RibbonAchv('Great League Champion', 25, 'great_ribbon', 25).setSecret(true),
|
||||
_50_RIBBONS: new RibbonAchv('Ultra League Champion', 50, 'ultra_ribbon', 50).setSecret(true),
|
||||
_75_RIBBONS: new RibbonAchv('Rogue League Champion', 75, 'rogue_ribbon', 75).setSecret(true),
|
||||
_100_RIBBONS: new RibbonAchv('Master League Champion', 100, 'master_ribbon', 100).setSecret(true),
|
||||
TRANSFER_MAX_BATTLE_STAT: new Achv('Teamwork', 'Baton pass to another party member with at least one stat maxed out', 'stick', 20),
|
||||
MAX_FRIENDSHIP: new Achv('Friendmaxxing', 'Reach max friendship on a Pokémon', 'soothe_bell', 25),
|
||||
MEGA_EVOLVE: new Achv('Megamorph', 'Mega evolve a Pokémon', 'mega_bracelet', 50),
|
||||
|
|
|
@ -173,6 +173,7 @@ export interface StarterDataEntry {
|
|||
abilityAttr: integer;
|
||||
passiveAttr: integer;
|
||||
valueReduction: integer;
|
||||
classicWinCount: integer;
|
||||
}
|
||||
|
||||
export interface StarterData {
|
||||
|
@ -194,7 +195,8 @@ const systemShortKeys = {
|
|||
eggMoves: '$em',
|
||||
candyCount: '$x',
|
||||
passive: '$p',
|
||||
valueReduction: '$vr'
|
||||
valueReduction: '$vr',
|
||||
classicWinCount: '$wc'
|
||||
};
|
||||
|
||||
export class GameData {
|
||||
|
@ -995,7 +997,8 @@ export class GameData {
|
|||
friendship: 0,
|
||||
abilityAttr: defaultStarterSpecies.includes(speciesId) ? AbilityAttr.ABILITY_1 : 0,
|
||||
passiveAttr: 0,
|
||||
valueReduction: 0
|
||||
valueReduction: 0,
|
||||
classicWinCount: 0
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1089,6 +1092,32 @@ export class GameData {
|
|||
});
|
||||
}
|
||||
|
||||
incrementRibbonCount(species: PokemonSpecies, forStarter: boolean = false): integer {
|
||||
const speciesIdToIncrement: Species = species.getRootSpeciesId(forStarter);
|
||||
|
||||
if (!this.starterData[speciesIdToIncrement].classicWinCount) {
|
||||
this.starterData[speciesIdToIncrement].classicWinCount = 0;
|
||||
}
|
||||
|
||||
if (!this.starterData[speciesIdToIncrement].classicWinCount)
|
||||
this.scene.gameData.gameStats.ribbonsOwned++;
|
||||
|
||||
const ribbonsInStats: integer = this.scene.gameData.gameStats.ribbonsOwned;
|
||||
|
||||
if (ribbonsInStats >= 100)
|
||||
this.scene.validateAchv(achvs._100_RIBBONS);
|
||||
if (ribbonsInStats >= 75)
|
||||
this.scene.validateAchv(achvs._75_RIBBONS);
|
||||
if (ribbonsInStats >= 50)
|
||||
this.scene.validateAchv(achvs._50_RIBBONS);
|
||||
if (ribbonsInStats >= 25)
|
||||
this.scene.validateAchv(achvs._25_RIBBONS);
|
||||
if (ribbonsInStats >= 10)
|
||||
this.scene.validateAchv(achvs._10_RIBBONS);
|
||||
|
||||
return ++this.starterData[speciesIdToIncrement].classicWinCount;
|
||||
}
|
||||
|
||||
addStarterCandy(species: PokemonSpecies, count: integer): void {
|
||||
this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count);
|
||||
this.starterData[species.speciesId].candyCount += count;
|
||||
|
|
|
@ -6,6 +6,7 @@ export class GameStats {
|
|||
public battles: integer;
|
||||
public classicSessionsPlayed: integer;
|
||||
public sessionsWon: integer;
|
||||
public ribbonsOwned: integer;
|
||||
public dailyRunSessionsPlayed: integer;
|
||||
public dailyRunSessionsWon: integer;
|
||||
public endlessSessionsPlayed: integer;
|
||||
|
@ -43,6 +44,7 @@ export class GameStats {
|
|||
this.battles = source?.battles || 0;
|
||||
this.classicSessionsPlayed = source?.classicSessionsPlayed || 0;
|
||||
this.sessionsWon = source?.sessionsWon || 0;
|
||||
this.ribbonsOwned = source?.ribbonsOwned || 0;
|
||||
this.dailyRunSessionsPlayed = source?.dailyRunSessionsPlayed || 0;
|
||||
this.dailyRunSessionsWon = source?.dailyRunSessionsWon || 0;
|
||||
this.endlessSessionsPlayed = source?.endlessSessionsPlayed || 0;
|
||||
|
|
|
@ -51,6 +51,7 @@ const displayStats: DisplayStats = {
|
|||
return `${caughtCount} (${Math.floor((caughtCount / Object.keys(gameData.dexData).length) * 1000) / 10}%)`;
|
||||
}
|
||||
},
|
||||
ribbonsOwned: 'Ribbons Owned',
|
||||
classicSessionsPlayed: 'Classic Runs',
|
||||
sessionsWon: 'Classic Wins',
|
||||
dailyRunSessionsPlayed: 'Daily Run Attempts',
|
||||
|
|
|
@ -174,6 +174,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
private starterValueLabels: Phaser.GameObjects.Text[];
|
||||
private shinyIcons: Phaser.GameObjects.Image[][];
|
||||
private hiddenAbilityIcons: Phaser.GameObjects.Image[];
|
||||
private classicWinIcons: Phaser.GameObjects.Image[];
|
||||
|
||||
private iconAnimHandler: PokemonIconAnimHandler;
|
||||
|
||||
|
@ -410,6 +411,17 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
return ret;
|
||||
});
|
||||
|
||||
this.classicWinIcons = new Array(81).fill(null).map((_, i) => {
|
||||
const x = (i % 9) * 18;
|
||||
const y = Math.floor(i / 9) * 18;
|
||||
const ret = this.scene.add.image(x + 152, y + 16, 'champion_ribbon');
|
||||
ret.setOrigin(0, 0);
|
||||
ret.setScale(0.5);
|
||||
ret.setVisible(false);
|
||||
this.starterSelectContainer.add(ret);
|
||||
return ret;
|
||||
});
|
||||
|
||||
this.pokemonSprite = this.scene.add.sprite(53, 63, `pkmn__sub`);
|
||||
this.pokemonSprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
|
||||
this.starterSelectContainer.add(this.pokemonSprite);
|
||||
|
@ -1192,6 +1204,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.shinyIcons[s][v].setTint(getVariantTint(speciesVariants[v] === DexAttr.DEFAULT_VARIANT ? 0 : speciesVariants[v] === DexAttr.VARIANT_2 ? 1 : 2));
|
||||
}
|
||||
this.hiddenAbilityIcons[s].setVisible(slotVisible && !!this.scene.gameData.dexData[speciesId].caughtAttr && !!(this.scene.gameData.starterData[speciesId].abilityAttr & 4));
|
||||
this.classicWinIcons[s].setVisible(slotVisible && this.scene.gameData.starterData[speciesId].classicWinCount > 0);
|
||||
}
|
||||
} else {
|
||||
changed = super.setCursor(cursor);
|
||||
|
|