From 9005e32883f48ce957cf3b37b8dae40a0bf96f2e Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Thu, 22 Feb 2024 18:03:36 -0500 Subject: [PATCH] Add character sprite system with female rival --- .gitignore | 1 + public/images/character/rival_f.json | 209 +++++++++++++++++++++++++++ public/images/character/rival_f.png | Bin 0 -> 5044 bytes src/battle-scene.ts | 12 +- src/data/dialogue.ts | 80 +++++----- src/data/trainer-config.ts | 18 ++- src/phases.ts | 14 +- src/ui/char-sprite.ts | 106 ++++++++++++++ src/ui/message-ui-handler.ts | 9 +- 9 files changed, 398 insertions(+), 51 deletions(-) create mode 100644 public/images/character/rival_f.json create mode 100644 public/images/character/rival_f.png create mode 100644 src/ui/char-sprite.ts diff --git a/.gitignore b/.gitignore index 8c2a9f54f91..2b95adf2c86 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,6 @@ public/images/pokemon/input/*.png public/images/pokemon/input/output/* public/images/pokemon/icons/input/*.png public/images/pokemon/icons/input/output/* +public/images/character/*/ src/data/battle-anim-raw-data*.ts src/data/battle-anim-data.ts diff --git a/public/images/character/rival_f.json b/public/images/character/rival_f.json new file mode 100644 index 00000000000..2a9014b174f --- /dev/null +++ b/public/images/character/rival_f.json @@ -0,0 +1,209 @@ +{ + "textures": [ + { + "image": "rival_f.png", + "format": "RGBA8888", + "size": { + "w": 184, + "h": 675 + }, + "scale": 1, + "frames": [ + { + "filename": "angry", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 0, + "y": 0, + "w": 92, + "h": 135 + } + }, + { + "filename": "angry_mopen", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 92, + "y": 0, + "w": 92, + "h": 135 + } + }, + { + "filename": "neutral", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 0, + "y": 135, + "w": 92, + "h": 135 + } + }, + { + "filename": "shock", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 92, + "y": 135, + "w": 92, + "h": 135 + } + }, + { + "filename": "smile", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 0, + "y": 270, + "w": 92, + "h": 135 + } + }, + { + "filename": "smile_eclosed", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 92, + "y": 270, + "w": 92, + "h": 135 + } + }, + { + "filename": "smile_ehalf", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 9, + "y": 0, + "w": 92, + "h": 135 + }, + "frame": { + "x": 0, + "y": 405, + "w": 92, + "h": 135 + } + }, + { + "filename": "smile_wave", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 18, + "y": 0, + "w": 83, + "h": 135 + }, + "frame": { + "x": 0, + "y": 540, + "w": 83, + "h": 135 + } + }, + { + "filename": "smile_wave_wink", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 108, + "h": 135 + }, + "spriteSourceSize": { + "x": 18, + "y": 0, + "w": 83, + "h": 135 + }, + "frame": { + "x": 83, + "y": 540, + "w": 83, + "h": 135 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:1be996f9906a409496e360d67e49ccd0:db1900b6808dc32ed4746255b60aa557:0aaa288a75ecf87b6647cbb7fd0d2ecc$" + } +} diff --git a/public/images/character/rival_f.png b/public/images/character/rival_f.png new file mode 100644 index 0000000000000000000000000000000000000000..b165cd8a7601e089b43554ec30f805327c68de4c GIT binary patch literal 5044 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 modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE).setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.CHIKORITA, Species.CYNDAQUIL, Species.TOTODILE, Species.TREECKO, Species.TORCHIC, Species.MUDKIP, Species.TURTWIG, Species.CHIMCHAR, Species.PIPLUP, Species.SNIVY, Species.TEPIG, Species.OSHAWOTT, Species.CHESPIN, Species.FENNEKIN, Species.FROAKIE, Species.ROWLET, Species.LITTEN, Species.POPPLIO, Species.GROOKEY, Species.SCORBUNNY, Species.SOBBLE, Species.SPRIGATITO, Species.FUECOCO, Species.QUAXLY ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEY, Species.HOOTHOOT, Species.TAILLOW, Species.STARLY, Species.PIDOVE, Species.FLETCHLING, Species.PIKIPEK, Species.ROOKIDEE, Species.WATTREL ])), - [TrainerType.RIVAL_2]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setTitle('Rival').setStaticParty().setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival').setPartyTemplates(trainerPartyTemplates.RIVAL_2) + [TrainerType.RIVAL_2]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setHasCharSprite().setTitle('Rival').setStaticParty().setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival').setPartyTemplates(trainerPartyTemplates.RIVAL_2) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.IVYSAUR, Species.CHARMELEON, Species.WARTORTLE, Species.BAYLEEF, Species.QUILAVA, Species.CROCONAW, Species.GROVYLE, Species.COMBUSKEN, Species.MARSHTOMP, Species.GROTLE, Species.MONFERNO, Species.PRINPLUP, Species.SERVINE, Species.PIGNITE, Species.DEWOTT, Species.QUILLADIN, Species.BRAIXEN, Species.FROGADIER, Species.DARTRIX, Species.TORRACAT, Species.BRIONNE, Species.THWACKEY, Species.RABOOT, Species.DRIZZILE, Species.FLORAGATO, Species.CROCALOR, Species.QUAXWELL ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOTTO, Species.HOOTHOOT, Species.TAILLOW, Species.STARAVIA, Species.TRANQUILL, Species.FLETCHINDER, Species.TRUMBEAK, Species.CORVISQUIRE, Species.WATTREL ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)), - [TrainerType.RIVAL_3]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setTitle('Rival').setStaticParty().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival').setPartyTemplates(trainerPartyTemplates.RIVAL_3) + [TrainerType.RIVAL_3]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setHasCharSprite().setTitle('Rival').setStaticParty().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival').setPartyTemplates(trainerPartyTemplates.RIVAL_3) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setSpeciesFilter(species => species.baseTotal >= 540), - [TrainerType.RIVAL_4]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setTitle('Rival').setBoss().setStaticParty().setMoneyMultiplier(1.75).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_2').setPartyTemplates(trainerPartyTemplates.RIVAL_4) + [TrainerType.RIVAL_4]: new TrainerConfig(++t).setName('Finn').setHasGenders('Ivy').setHasCharSprite().setTitle('Rival').setBoss().setStaticParty().setMoneyMultiplier(1.75).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_2').setPartyTemplates(trainerPartyTemplates.RIVAL_4) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) @@ -784,7 +790,7 @@ export const trainerConfigs: TrainerConfigs = { 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').setHasCharSprite().setTitle('Rival').setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_5) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) @@ -797,7 +803,7 @@ export const trainerConfigs: TrainerConfigs = { 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').setHasCharSprite().setTitle('Rival').setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm('final').setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_6) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) diff --git a/src/phases.ts b/src/phases.ts index 12b60424f7e..37295d5dde4 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -620,9 +620,17 @@ export class EncounterPhase extends BattlePhase { if (!encounterMessages?.length) doSummon(); else { - let message: string; - this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); - this.scene.ui.showDialogue(message, trainer.getName(), null, doSummon); + const showDialogueAndSummon = () => { + let message: string; + this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); + this.scene.ui.showDialogue(message, trainer.getName(), null, () => { + this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); + }); + }; + if (this.scene.currentBattle.trainer.config.hasCharSprite) + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer.getKey(), 'smile').then(() => showDialogueAndSummon())); + else + showDialogueAndSummon(); } } } diff --git a/src/ui/char-sprite.ts b/src/ui/char-sprite.ts new file mode 100644 index 00000000000..872e8deac9f --- /dev/null +++ b/src/ui/char-sprite.ts @@ -0,0 +1,106 @@ +import BattleScene from "../battle-scene"; + +export default class CharSprite extends Phaser.GameObjects.Container { + private sprite: Phaser.GameObjects.Sprite; + private transitionSprite: Phaser.GameObjects.Sprite; + + public key: string; + public variant: string; + public shown: boolean; + + constructor(scene: BattleScene) { + super(scene, (scene.game.canvas.width / 6) + 32, -42); + } + + setup(): void { + [ this.sprite, this.transitionSprite ] = new Array(2).fill(null).map(() => { + const ret = this.scene.add.sprite(0, 0, '', ''); + ret.setOrigin(0.5, 1); + this.add(ret); + return ret; + }); + + this.transitionSprite.setVisible(false); + + this.setVisible(false); + this.shown = false; + } + + showCharacter(key: string, variant: string): Promise { + return new Promise(resolve => { + if (!key.startsWith('c_')) + key = `c_${key}`; + if (this.shown) { + if (key === this.key && variant === this.variant) + return resolve(); + if (key !== this.key) + return this.hide().then(() => this.showCharacter(key, variant)); + this.setVariant(variant).then(() => resolve()); + return; + } + + this.sprite.setTexture(key, variant); + + (this.scene as BattleScene).fieldUI.bringToTop(this); + + this.scene.tweens.add({ + targets: this, + x: (this.scene.game.canvas.width / 6) - 102, + duration: 750, + ease: 'Cubic.easeOut', + onComplete: () => { + resolve(); + } + }); + + this.setVisible(this.scene.textures.get(key).key !== '__MISSING'); + this.shown = true; + + this.key = key; + this.variant = variant; + }); + } + + setVariant(variant: string): Promise { + return new Promise(resolve => { + (this.scene as BattleScene).fieldUI.bringToTop(this); + + this.transitionSprite.setTexture(this.key, variant); + this.transitionSprite.setAlpha(0); + this.transitionSprite.setVisible(true); + this.scene.tweens.add({ + targets: this.transitionSprite, + alpha: 1, + duration: 250, + ease: 'Sine.easeIn', + onComplete: () => { + this.sprite.setTexture(this.key, variant); + this.transitionSprite.setVisible(false); + resolve(); + } + }); + this.variant = variant; + }); + } + + hide(): Promise { + return new Promise(resolve => { + if (!this.shown) + return resolve(); + + this.scene.tweens.add({ + targets: this, + x: (this.scene.game.canvas.width / 6) + 32, + duration: 750, + ease: 'Cubic.easeIn', + onComplete: () => { + if (!this.shown) + this.setVisible(false); + resolve(); + } + }); + + this.shown = false; + }); + }; +} \ No newline at end of file diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index faeeb90798b..7fd61c829e7 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -28,12 +28,16 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { private showTextInternal(text: string, delay: integer, callback: Function, callbackDelay: integer, prompt: boolean, promptDelay: integer) { if (delay === null || delay === undefined) delay = 20; + let charVarMap = new Map(); let delayMap = new Map(); let soundMap = new Map(); - const actionPattern = /@(d|s)\{(.*?)\}/; + const actionPattern = /@(c|d|s)\{(.*?)\}/; let actionMatch: RegExpExecArray; while ((actionMatch = actionPattern.exec(text))) { switch (actionMatch[1]) { + case 'c': + charVarMap.set(actionMatch.index, actionMatch[2]); + break; case 'd': delayMap.set(actionMatch.index, parseInt(actionMatch[2])); break; @@ -67,10 +71,13 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { delay: delay, callback: () => { const charIndex = text.length - this.textTimer.repeatCount; + const charVar = charVarMap.get(charIndex); const charSound = soundMap.get(charIndex); const charDelay = delayMap.get(charIndex); this.message.setText(text.slice(0, charIndex)); const advance = () => { + if (charVar) + this.scene.charSprite.setVariant(charVar); if (charSound) this.scene.playSound(charSound); if (callback && !this.textTimer.repeatCount) {