[Feature] Add Mystery Encounters to the game (#3938)

* add .github/workflows/mystery-event.yml

* update mystery-event.yml

* mystery encounters: resolve review comments:

Lost at Sea:
-fix typo in handlePokemonGuidingYouPhase function

Mysterious Chest:
- remove obsolete commented code

mystery-encounter.ts
- remove unused `onDone` field from MysteryEncounterBuilder

* fix typo in CanLearnMoveRequirementOptions

* remove redundance from Pokemon.isAllowedInBattle()

* chore: jsdoc formatting

* fix lost-at-sea tests

* add fallback for biomeMysteryEncounters if empty

* lost-at-sea-encounter: fix and extend tests

* move "battle:fainted" into `koPlayerPokemon`

* add retries to quick-draw tests

* fix lost-at-sea-encounter tests

* clean up battle animation logic

* Update and rename mystery-event.yml to mystery-events.yml

* Update mystery-events.yml

* Fix typo

* Update mystery-events.yml

Fix debug runs

* clean up unit tests and utils

* attach github issues to all encounter jsdocs

* start dialogue refactor

* update sleeping snorlax encounter

* migrate encounters dialogue to new format

* cleanup and add jsdocs

* finish fiery fallout encounter

* fix unit test breaks

* add skeleton tests to fiery fallout

* commit latest test changes

* finish unit tests for fiery fallout

* bug fix for empty modifier shop

* stash working changes

* stash changes

* Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/data/battle-anims.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* nit updates and cleanup

* Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* add jsdocs and more cleanup

* add more jsdoc

* add the strong stuff encounter

* add the strong stuff encounter and more unit tests

* cleanup container length checks in ME ui

* add retries to tests

* add retries to tests

* fix trainer wave disable override

* add shuckle juice modifier

* add dialogue bug fixes

* add dialogue bug fixes

* add pokemon salesman encounter and affects pokedex UI display

* add unit tests for pokemon salesman

* temp stash

* add offer you can't refuse

* add unit tests for offer you can't refuse encounter

* remove unnecessary prompt handlers

* add tests for disabled encounter options

* add delibird-y encounter

* add delibird-y encounter

* add absolute avarice encounter

* finish absolute avarice encounter

* add unit tests and enhancements for item overrides in tests

* fix unit test

* cleanup absolute avarice PR

* small bug fixes with latest sync from main

* update visuals loading for safari and stat trainer visuals

* update visuals loading for safari and stat trainer visuals

* update a trainer's test encounter and add unit tests

* add Trash to Treasure encounter

* clean up trash to treasure encounter

* clean up trash to treasure encounter

* add berries abound encounter

* start clowning around encounter

* first implementation pass at clowning around

* add unit tests for clowning around

* add unit tests for clowning around

* clean up ME unit tests

* clean up unit tests

* update unit tests

* add part timer and dancing lessons encounters

* add unit tests for Dancing Lessons and Part-Timer

* reordered biome list and adjusted redirection for project and labels

* Add Weird Dream encounter and slight reworks to Berries Abound/Fight or Flight

* adjusting yml to match new labels

* fix yml whoopsie

* Expanded 'Weird Dream' banlist and fixed a bug with the BST bump range

* adds Winstrate Challenge mystery encounter

* small cleanup for winstrates

* add unit tests for Winstrate Challenge

* fix pokemon not returning after winstrate battle

* commit latest beta merge updates

* fix ME null checks and unit tests with beta update

* fix ME null checks and unit tests with beta update

* MEs to pokerogue beta branch

* test dialogue changes

* test patch fix

* test patch fix

* test patch fix

* adds teleporting hijinks encounter

* add unit tests for Teleporting Hijinks

* small change to teleporting hijinks dialogue

* migrate ME translations to json

* add retries to berries-abound.Option1: should reward the player with X berries based on wave

* add missing ME dialogue back in

* revert template changes

* add ME unique trainer dialogue to both dialogue jsons

* fix hanging comma in json

* fix broken imports

* resolve lint issues

* fix flaky test

* balance tweaks to a few MEs, updates to bug superfan

* add unit tests for Bug-Type Superfan and clean up dialogue

* Adds Fun and Games mystery encounter

* add unit tests for Fun and Games encounter

* update jsdoc

* small ME balance changes

* small ME balance changes

* Adds Uncommon Breed ME and misc. ME bug fixes

* Update getFinalSessionData() to collect Mystery Encounter data

* adds GTS encounter

* various ME bug fixes and balance changes

* latest ME bug fixes

* clean up GTS Encounter and add unit tests

* small cleanup to MEs branch

* add BGM music names for ME music

* bug fixes and balance changes for MEs

* ME data schema updates

* balance changes and bug fixes to MEs

* balance changes and bug fixes to MEs

* update tests for MEs

* add jsdoc to party exp function

* dialogue updates and test fixes for MEs

* dialogue updates and test fixes for MEs

* PR suggestions and fixees

* stash PR feedback and bugfixes

* fix all tests for MEs and cleanup

* PR feedback

* update flaky ME test

* update tests, bug fix MEs, and sprite assets

* remove unintentional console log

* re-enable stubbed function for Phaser text styling

* handle undefined introVisuals properly

* PR feedback from NightKev

* disable Uncommon Breed tests

* locales updates and bug fixes for safari zone

* more PR feedback and update field trip with Rarer Candy

* fix unit test

* Change how reroll button gets disabled in Modifier Shop Phase

* update continue button text logic

* Update src/ui/modifier-select-ui-handler.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* fix money formatting and some nits

* more nits

* more nits

* update ME tsdocs with links

* update ME tsdocs with links

---------

Co-authored-by: Felix Staud <felix.staud@headwire.com>
Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
Co-authored-by: ImperialSympathizer <imperialsympathizer@gmail.com>
Co-authored-by: InnocentGameDev <asdargmng@gmail.com>
Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com>
This commit is contained in:
ImperialSympathizer 2024-09-13 22:05:58 -04:00 committed by GitHub
parent a085d3d416
commit acb2b66be4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
263 changed files with 40297 additions and 3922 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,951 @@
{
"id": 686,
"graphic": "PRAS- Dragon Dance",
"frames": [
[
{
"x": 4,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 12,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -12,
"y": -0.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": 12,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 16,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -16,
"y": -0.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": 24,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 20,
"y": 0,
"zoomX": 108,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -20,
"y": -0.5,
"zoomX": 108,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": 32,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 24,
"y": 0,
"zoomX": 108,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -24,
"y": -0.5,
"zoomX": 108,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": 36,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 28,
"y": 0,
"zoomX": 108,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -28,
"y": -0.5,
"zoomX": 108,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": 36,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 36,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 32,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 24,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 12,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 4,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": -4,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 12,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -12,
"y": -0.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": -12,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 16,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -16,
"y": -0.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": -24,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 20,
"y": 0,
"zoomX": 108,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -20,
"y": -0.5,
"zoomX": 108,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": -32,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 24,
"y": 0,
"zoomX": 108,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -24,
"y": -0.5,
"zoomX": 108,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 155,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": -36,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
},
{
"x": 28,
"y": 0,
"zoomX": 108,
"zoomY": 100,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
},
{
"x": -28,
"y": -0.5,
"zoomX": 108,
"zoomY": 100,
"mirror": true,
"visible": true,
"blendType": 1,
"target": 2,
"graphicFrame": 0,
"opacity": 70,
"tone": [
0,
0,
0,
255
],
"priority": 1,
"focus": 3
}
],
[
{
"x": -36,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": -36,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": -32,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": -24,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": -12,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": -4,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 1,
"focus": 2
}
]
],
"frameTimedEvents": {
"0": [
{
"frameIndex": 0,
"resourceName": "PRSFX- Attract.wav",
"volume": 100,
"pitch": 100,
"eventType": "AnimTimedSoundEvent"
}
],
"1": [
{
"frameIndex": 0,
"resourceName": "PRSFX- Ally Switch.wav",
"volume": 80,
"pitch": 100,
"eventType": "AnimTimedSoundEvent"
}
]
},
"position": 4,
"hue": 0
}

View File

@ -0,0 +1,66 @@
{
"frames": [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
"frameTimedEvents": {
"0": [
{
"frameIndex": 0,
"resourceName": "PRAS- Fire BG",
"bgX": 0,
"bgY": 0,
"opacity": 0,
"duration": 35,
"eventType": "AnimTimedAddBgEvent"
},
{
"frameIndex": 0,
"resourceName": "",
"bgX": 0,
"bgY": 0,
"opacity": 255,
"duration": 12,
"eventType": "AnimTimedUpdateBgEvent"
}
],
"25": [
{
"frameIndex": 25,
"resourceName": "",
"bgX": 0,
"bgY": 0,
"opacity": 0,
"duration": 8,
"eventType": "AnimTimedUpdateBgEvent"
}
]
},
"position": 1,
"hue": 0
}

View File

@ -0,0 +1,902 @@
{
"graphic": "PRAS- Magma Storm",
"frames": [
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 120,
"y": -56,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 144,
"y": -84,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 100,
"y": -86.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 140,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 9,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 136,
"y": -92,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 9,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 108,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 9,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 152,
"y": -76,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 10,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 116,
"y": -88,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 10,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 128,
"y": -62.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 10,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 136,
"y": -96,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 100,
"y": -76,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 148,
"y": -66.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 108,
"y": -92,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 120,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 144,
"y": -86.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 100,
"y": -76,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 9,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 136,
"y": -68,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 9,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 128,
"y": -94.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 9,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 100.5,
"y": -70,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 10,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 144,
"y": -66,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 10,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 126,
"y": -86.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 10,
"opacity": 255,
"priority": 4,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 255,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 255,
"priority": 4,
"focus": 1
}
],
[
{
"x": 101,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 130,
"priority": 4,
"focus": 1
},
{
"x": 152,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 130,
"priority": 4,
"focus": 1
},
{
"x": 124.5,
"y": -78.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 140,
"priority": 4,
"focus": 1
}
]
],
"frameTimedEvents": {
"0": [
{
"frameIndex": 0,
"resourceName": "PRSFX- Magma Storm1.wav",
"volume": 100,
"pitch": 100,
"eventType": "AnimTimedSoundEvent"
}
],
"8": [
{
"frameIndex": 8,
"resourceName": "PRSFX- Magma Storm2.wav",
"volume": 100,
"pitch": 100,
"eventType": "AnimTimedSoundEvent"
}
]
},
"position": 1,
"hue": 0
}

View File

@ -0,0 +1,822 @@
{
"graphic": "PRAS- Smokescreen",
"frames": [
[
{
"x": 15.5,
"y": 12.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": 8.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 50,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": 0.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -4,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": -3.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -4,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -11,
"y": 21.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 50,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": -7.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -8,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -11,
"y": 17.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": -11.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -12,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -11,
"y": 13.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": 21,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 50,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": -15.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -16,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -11,
"y": 5.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": 17,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": -19.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 100,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -20,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -11,
"y": 0.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": 13,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -12.5,
"y": 8.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 50,
"priority": 4,
"focus": 2
}
],
[
{
"x": 15.5,
"y": -23.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 50,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -24,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -11,
"y": -2.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": 9,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -12.5,
"y": 4.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": -11,
"y": -6.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -28,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": 5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -12.5,
"y": 0.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 8,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": 23,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 50,
"priority": 4,
"focus": 2
}
],
[
{
"x": -11,
"y": -10.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -32,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 100,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": 1,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -12.5,
"y": -3.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 7,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": 19,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": -11,
"y": -14.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 100,
"priority": 4,
"focus": 2
},
{
"x": 0,
"y": -36,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 50,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": -3,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -12.5,
"y": -7.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": 15,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 0,
"opacity": 150,
"priority": 4,
"focus": 2
}
],
[
{
"x": -11,
"y": -18.5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 50,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": -7,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": -12.5,
"y": -11.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 6,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": 7,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 1,
"opacity": 150,
"priority": 4,
"focus": 2
}
],
[
{
"x": -12.5,
"y": -15.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 150,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": -11,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 100,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": 3,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 150,
"priority": 4,
"focus": 2
}
],
[
{
"x": -12.5,
"y": -19.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 100,
"priority": 4,
"focus": 2
},
{
"x": 11,
"y": -15,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 50,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": -1,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 2,
"opacity": 150,
"priority": 4,
"focus": 2
}
],
[
{
"x": -12.5,
"y": -23.5,
"zoomX": 100,
"zoomY": 100,
"mirror": true,
"visible": true,
"target": 2,
"graphicFrame": 5,
"opacity": 50,
"priority": 4,
"focus": 2
},
{
"x": 4.5,
"y": -5,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 3,
"opacity": 150,
"priority": 4,
"focus": 2
}
],
[
{
"x": 4.5,
"y": -9,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 150,
"priority": 4,
"focus": 2
}
],
[
{
"x": 4.5,
"y": -13,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 100,
"priority": 4,
"focus": 2
}
],
[
{
"x": 4.5,
"y": -17,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 2,
"graphicFrame": 4,
"opacity": 50,
"priority": 4,
"focus": 2
}
],
[
{
"x": 0,
"y": 0,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 0,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 4,
"focus": 3
},
{
"x": 128,
"y": -64,
"zoomX": 100,
"zoomY": 100,
"visible": true,
"target": 1,
"graphicFrame": 0,
"opacity": 255,
"locked": true,
"priority": 4,
"focus": 3
}
],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
"frameTimedEvents": {
"0": [
{
"frameIndex": 0,
"resourceName": "PRSFX- Haze.wav",
"volume": 100,
"pitch": 85,
"eventType": "AnimTimedSoundEvent"
},
{
"frameIndex": 0,
"resourceName": "Explosion1.m4a",
"volume": 100,
"pitch": 85,
"eventType": "AnimTimedSoundEvent"
}
]
},
"position": 2,
"hue": 0
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

View File

@ -0,0 +1,734 @@
{
"textures": [
{
"image": "b2w2_lady.png",
"format": "RGBA8888",
"size": {
"w": 399,
"h": 360
},
"scale": 1,
"frames": [
{
"filename": "0000.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 0,
"y": 0,
"w": 56,
"h": 72
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 57,
"y": 0,
"w": 56,
"h": 72
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 114,
"y": 0,
"w": 56,
"h": 72
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 9,
"y": 8,
"w": 55,
"h": 72
},
"frame": {
"x": 171,
"y": 0,
"w": 55,
"h": 72
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 54,
"h": 72
},
"frame": {
"x": 228,
"y": 0,
"w": 54,
"h": 72
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 54,
"h": 72
},
"frame": {
"x": 285,
"y": 0,
"w": 54,
"h": 72
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 14,
"y": 8,
"w": 52,
"h": 72
},
"frame": {
"x": 342,
"y": 0,
"w": 52,
"h": 72
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 20,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 0,
"y": 72,
"w": 48,
"h": 72
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 47,
"h": 72
},
"frame": {
"x": 57,
"y": 72,
"w": 47,
"h": 72
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 47,
"h": 72
},
"frame": {
"x": 114,
"y": 72,
"w": 47,
"h": 72
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 171,
"y": 72,
"w": 48,
"h": 72
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 228,
"y": 72,
"w": 48,
"h": 72
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 285,
"y": 72,
"w": 48,
"h": 72
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 342,
"y": 72,
"w": 48,
"h": 72
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 49,
"h": 72
},
"frame": {
"x": 0,
"y": 144,
"w": 49,
"h": 72
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 49,
"h": 72
},
"frame": {
"x": 57,
"y": 144,
"w": 49,
"h": 72
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 49,
"h": 72
},
"frame": {
"x": 114,
"y": 144,
"w": 49,
"h": 72
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 49,
"h": 72
},
"frame": {
"x": 171,
"y": 144,
"w": 49,
"h": 72
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 228,
"y": 144,
"w": 48,
"h": 72
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 285,
"y": 144,
"w": 48,
"h": 72
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 342,
"y": 144,
"w": 48,
"h": 72
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 22,
"y": 8,
"w": 48,
"h": 72
},
"frame": {
"x": 0,
"y": 216,
"w": 48,
"h": 72
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 20,
"y": 8,
"w": 50,
"h": 72
},
"frame": {
"x": 57,
"y": 216,
"w": 50,
"h": 72
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 18,
"y": 8,
"w": 51,
"h": 72
},
"frame": {
"x": 114,
"y": 216,
"w": 51,
"h": 72
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 18,
"y": 8,
"w": 51,
"h": 72
},
"frame": {
"x": 171,
"y": 216,
"w": 51,
"h": 72
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 15,
"y": 8,
"w": 53,
"h": 72
},
"frame": {
"x": 228,
"y": 216,
"w": 53,
"h": 72
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 10,
"y": 8,
"w": 57,
"h": 72
},
"frame": {
"x": 285,
"y": 216,
"w": 57,
"h": 72
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 10,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 342,
"y": 216,
"w": 56,
"h": 72
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 10,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 0,
"y": 288,
"w": 56,
"h": 72
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 9,
"y": 8,
"w": 55,
"h": 72
},
"frame": {
"x": 57,
"y": 288,
"w": 55,
"h": 72
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 114,
"y": 288,
"w": 56,
"h": 72
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 171,
"y": 288,
"w": 56,
"h": 72
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 228,
"y": 288,
"w": 56,
"h": 72
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 56,
"h": 72
},
"frame": {
"x": 285,
"y": 288,
"w": 56,
"h": 72
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:e7f062304401dbd7b3ec79512f0ff4cb:0136dac01331f88892a3df26aeab78f5:1ed1e22abb9b55d76337a5a599835c06$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,797 @@
{
"textures": [
{
"image": "b2w2_veteran_m.png",
"format": "RGBA8888",
"size": {
"w": 424,
"h": 390
},
"scale": 1,
"frames": [
{
"filename": "0000.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 0,
"y": 0,
"w": 43,
"h": 78
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 53,
"y": 0,
"w": 43,
"h": 78
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 106,
"y": 0,
"w": 43,
"h": 78
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 159,
"y": 0,
"w": 43,
"h": 78
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 212,
"y": 0,
"w": 44,
"h": 78
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 265,
"y": 0,
"w": 44,
"h": 78
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 318,
"y": 0,
"w": 44,
"h": 78
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 371,
"y": 0,
"w": 44,
"h": 78
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 0,
"y": 78,
"w": 44,
"h": 78
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 53,
"y": 78,
"w": 44,
"h": 78
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 48,
"h": 78
},
"frame": {
"x": 106,
"y": 78,
"w": 48,
"h": 78
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 50,
"h": 78
},
"frame": {
"x": 159,
"y": 78,
"w": 50,
"h": 78
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 53,
"h": 78
},
"frame": {
"x": 212,
"y": 78,
"w": 53,
"h": 78
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 53,
"h": 78
},
"frame": {
"x": 265,
"y": 78,
"w": 53,
"h": 78
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 52,
"h": 78
},
"frame": {
"x": 318,
"y": 78,
"w": 52,
"h": 78
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 51,
"h": 78
},
"frame": {
"x": 371,
"y": 78,
"w": 51,
"h": 78
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 52,
"h": 78
},
"frame": {
"x": 0,
"y": 156,
"w": 52,
"h": 78
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 52,
"h": 78
},
"frame": {
"x": 53,
"y": 156,
"w": 52,
"h": 78
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 53,
"h": 78
},
"frame": {
"x": 106,
"y": 156,
"w": 53,
"h": 78
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 53,
"h": 78
},
"frame": {
"x": 159,
"y": 156,
"w": 53,
"h": 78
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 53,
"h": 78
},
"frame": {
"x": 212,
"y": 156,
"w": 53,
"h": 78
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 52,
"h": 78
},
"frame": {
"x": 265,
"y": 156,
"w": 52,
"h": 78
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 51,
"h": 78
},
"frame": {
"x": 318,
"y": 156,
"w": 51,
"h": 78
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 51,
"h": 78
},
"frame": {
"x": 371,
"y": 156,
"w": 51,
"h": 78
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 51,
"h": 78
},
"frame": {
"x": 0,
"y": 234,
"w": 51,
"h": 78
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 50,
"h": 78
},
"frame": {
"x": 53,
"y": 234,
"w": 50,
"h": 78
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 48,
"h": 78
},
"frame": {
"x": 106,
"y": 234,
"w": 48,
"h": 78
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 46,
"h": 78
},
"frame": {
"x": 159,
"y": 234,
"w": 46,
"h": 78
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 46,
"h": 78
},
"frame": {
"x": 212,
"y": 234,
"w": 46,
"h": 78
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 265,
"y": 234,
"w": 44,
"h": 78
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 318,
"y": 234,
"w": 44,
"h": 78
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 371,
"y": 234,
"w": 44,
"h": 78
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 44,
"h": 78
},
"frame": {
"x": 0,
"y": 312,
"w": 44,
"h": 78
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 53,
"y": 312,
"w": 43,
"h": 78
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 106,
"y": 312,
"w": 43,
"h": 78
}
},
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 159,
"y": 312,
"w": 43,
"h": 78
}
},
{
"filename": "0036.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 2,
"w": 43,
"h": 78
},
"frame": {
"x": 212,
"y": 312,
"w": 43,
"h": 78
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:4deb068879a8ac195cb4f00c8b17b7f5:b32f0f90436649264b6f3c49b09ac06a:05e903aa75b8e50c28334d9b5e14c85a$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,83 @@
{
"textures": [
{
"image": "bait.png",
"format": "RGBA8888",
"size": {
"w": 14,
"h": 43
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 16
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 12,
"h": 13
},
"frame": {
"x": 1,
"y": 1,
"w": 12,
"h": 13
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 16
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 12,
"h": 13
},
"frame": {
"x": 1,
"y": 16,
"w": 12,
"h": 13
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 16
},
"spriteSourceSize": {
"x": 0,
"y": 5,
"w": 11,
"h": 11
},
"frame": {
"x": 1,
"y": 31,
"w": 11,
"h": 11
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f0ec04fcd67ac346dce973693711d032:b697e09191c4312b8faaa0a080a309b7:1af241a52e61fa01ca849aa03c112f85$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "berry_bush.png",
"format": "RGBA8888",
"size": {
"w": 49,
"h": 53
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 49,
"h": 53
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 49,
"h": 53
},
"frame": {
"x": 0,
"y": 0,
"w": 49,
"h": 53
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d5f83625477b5f98b726343f4a3a396f:f4665258986e97345cfeee041b4b8bcf:e7781fcc447e6d12deb2af78c9493c7f$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

View File

@ -0,0 +1,19 @@
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 46, "h": 60 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 46, "h": 60 },
"sourceSize": { "w": 46, "h": 60 }
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"image": "buoy-sheet.png",
"format": "RGBA8888",
"size": { "w": 46, "h": 60 },
"scale": "1"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "carnival_game.png",
"format": "RGBA8888",
"size": {
"w": 38,
"h": 82
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 38,
"h": 82
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 38,
"h": 82
},
"frame": {
"x": 0,
"y": 0,
"w": 38,
"h": 82
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d40b6742392c2fe8ca0735b3f561e319:5dcda5410b12f0aa75eb0dd1fbcbe4f9:d171fb17d3017d1f655cd8dd14c252b7$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "carnival_man.png",
"format": "RGBA8888",
"size": {
"w": 50,
"h": 77
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 15,
"y": 3,
"w": 50,
"h": 77
},
"frame": {
"x": 0,
"y": 0,
"w": 50,
"h": 77
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:e80aa9a809a7cca6d05992cb82f6dbd9:ea9962edd1cdc1e503deecf2ce1863c1:55647352b6547cf03212506309f2abf5$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "carnival_wobbuffet.png",
"format": "RGBA8888",
"size": {
"w": 45,
"h": 55
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 45,
"h": 55
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 45,
"h": 55
},
"frame": {
"x": 0,
"y": 0,
"w": 45,
"h": 55
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:879de17da906ea52e5a71afacb88fcf6:90f64e8eaac4ff1e67373f60c3d98d36:a090cb3294ca1218a4f90ecb97df81d7$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

View File

@ -0,0 +1,209 @@
{
"textures": [
{
"image": "chest_blue.png",
"format": "RGBA8888",
"size": {
"w": 54,
"h": 492
},
"scale": 1,
"frames": [
{
"filename": "0000.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 46,
"h": 39
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 46,
"h": 39
},
"frame": {
"x": 0,
"y": 0,
"w": 46,
"h": 39
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 47,
"h": 35
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 47,
"h": 35
},
"frame": {
"x": 0,
"y": 39,
"w": 47,
"h": 35
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 46,
"h": 39
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 46,
"h": 39
},
"frame": {
"x": 0,
"y": 74,
"w": 46,
"h": 39
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 46,
"h": 46
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 46,
"h": 46
},
"frame": {
"x": 0,
"y": 113,
"w": 46,
"h": 46
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 53,
"h": 65
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 53,
"h": 65
},
"frame": {
"x": 0,
"y": 159,
"w": 53,
"h": 65
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 224,
"w": 54,
"h": 67
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 291,
"w": 54,
"h": 67
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 358,
"w": 54,
"h": 67
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 425,
"w": 54,
"h": 67
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:017ecc2437e580a185f9843f97e80da5:f44ef1c27a4a17183a5bcf1f7fc8ce6a:f4f3c064e6c93b8d1290f93bee927f60$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,209 @@
{
"textures": [
{
"image": "chest_red.png",
"format": "RGBA8888",
"size": {
"w": 54,
"h": 492
},
"scale": 1,
"frames": [
{
"filename": "0000.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 46,
"h": 39
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 46,
"h": 39
},
"frame": {
"x": 0,
"y": 0,
"w": 46,
"h": 39
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 47,
"h": 35
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 47,
"h": 35
},
"frame": {
"x": 0,
"y": 39,
"w": 47,
"h": 35
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 46,
"h": 39
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 46,
"h": 39
},
"frame": {
"x": 0,
"y": 74,
"w": 46,
"h": 39
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 46,
"h": 46
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 46,
"h": 46
},
"frame": {
"x": 0,
"y": 113,
"w": 46,
"h": 46
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 53,
"h": 65
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 53,
"h": 65
},
"frame": {
"x": 0,
"y": 159,
"w": 53,
"h": 65
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 224,
"w": 54,
"h": 67
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 291,
"w": 54,
"h": 67
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 358,
"w": 54,
"h": 67
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 67
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 67
},
"frame": {
"x": 0,
"y": 425,
"w": 54,
"h": 67
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:2a0b6c93c5be115efa635d40780603f0:b5fde49f991c2ecc49afedd80cc8a544:a163d960e9966469ae4dde4b53c13496$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "dark_deal_porygon.png",
"format": "RGBA8888",
"size": {
"w": 36,
"h": 45
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 36,
"h": 45
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 44,
"h": 44
},
"frame": {
"x": 0,
"y": 0,
"w": 36,
"h": 45
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "global_trade_system.png",
"format": "RGBA8888",
"size": {
"w": 77,
"h": 78
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 77,
"h": 78
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 77,
"h": 78
},
"frame": {
"x": 0,
"y": 0,
"w": 77,
"h": 78
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8a51d7a17b3d8c32f0e5e4a0f15daeb4:6eba29c5345847f735d8b69a05fc49d1:98ad8b8b8d8c4865d7d23ec97b516594$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "mad_scientist_m.png",
"format": "RGBA8888",
"size": {
"w": 46,
"h": 76
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 44,
"h": 74
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 44,
"h": 74
},
"frame": {
"x": 1,
"y": 1,
"w": 44,
"h": 74
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:a7f8ff2bbb362868f51125c254eb6681:cf76e61ddd31a8f46af67ced168c44a2:4fc09abe16c0608828269e5da81d0744$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

View File

@ -0,0 +1,104 @@
{
"textures": [
{
"image": "mud.png",
"format": "RGBA8888",
"size": {
"w": 14,
"h": 68
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 20
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 12,
"h": 13
},
"frame": {
"x": 1,
"y": 1,
"w": 12,
"h": 13
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 20
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 12,
"h": 14
},
"frame": {
"x": 1,
"y": 16,
"w": 12,
"h": 14
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 20
},
"spriteSourceSize": {
"x": 0,
"y": 1,
"w": 12,
"h": 16
},
"frame": {
"x": 1,
"y": 32,
"w": 12,
"h": 16
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 12,
"h": 20
},
"spriteSourceSize": {
"x": 0,
"y": 3,
"w": 12,
"h": 17
},
"frame": {
"x": 1,
"y": 50,
"w": 12,
"h": 17
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:4f18a8effb8f01eb70f9f25b8294c1bf:ad663a73c51f780bbf45d00a52519553:c64f6b8befc3d5e9f836246d2b9536be$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "pokemon_salesman.png",
"format": "RGBA8888",
"size": {
"w": 40,
"h": 80
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 21,
"y": 2,
"w": 38,
"h": 78
},
"frame": {
"x": 1,
"y": 1,
"w": 38,
"h": 78
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:dd57e3db21f3933c15be65bec261f4c1:05c7ef32252a5c2d3ad007b7e26fabd7:ae82f52e471ed81e2558206f05476cd7$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "safari_zone.png",
"format": "RGBA8888",
"size": {
"w": 120,
"h": 84
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 118,
"h": 82
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 118,
"h": 82
},
"frame": {
"x": 1,
"y": 1,
"w": 118,
"h": 82
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:6fad7a61e47043b974153148b4fd3997:5ec4d0890f2f03446daf22c8ae8ba77b:87aa745cd95eef6cbf38935230f4e10f$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "teacher.png",
"format": "RGBA8888",
"size": {
"w": 43,
"h": 74
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 19,
"y": 8,
"w": 41,
"h": 72
},
"frame": {
"x": 1,
"y": 1,
"w": 41,
"h": 72
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:506e5a4ce79c134a7b4af90a90aef244:1b81d3d84bf12cedc419805eaff82548:59bc5dd000b5e72588320b473e31c312$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "teleporter.png",
"format": "RGBA8888",
"size": {
"w": 74,
"h": 79
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 74,
"h": 79
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 74,
"h": 79
},
"frame": {
"x": 0,
"y": 0,
"w": 74,
"h": 79
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:937d8502b98f79720118061b6021e108:2b4f9db00d5b0997b42a5466f808509b:ce1615396ce7b0a146766d50b319bb81$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "training_gear.png",
"format": "RGBA8888",
"size": {
"w": 76,
"h": 57
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 76,
"h": 57
},
"spriteSourceSize": {
"x": 10,
"y": 3,
"w": 56,
"h": 54
},
"frame": {
"x": 8,
"y": 0,
"w": 56,
"h": 54
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "warehouse_crate.png",
"format": "RGBA8888",
"size": {
"w": 71,
"h": 52
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 56
},
"spriteSourceSize": {
"x": 5,
"y": 4,
"w": 71,
"h": 52
},
"frame": {
"x": 0,
"y": 0,
"w": 71,
"h": 52
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:c8df5f0b35fb9c2a69b0e4aaa9fa9f91:f1d4643c26f2aed86ad77d354e669aaf:0c073e3c2048ea0779db9429e5e1d8bc$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "weird_dream_woman.png",
"format": "RGBA8888",
"size": {
"w": 78,
"h": 87
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 87
},
"spriteSourceSize": {
"x": 1,
"y": 0,
"w": 78,
"h": 87
},
"frame": {
"x": 0,
"y": 0,
"w": 78,
"h": 87
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d3cce87ee0e3a880d840bffe9373d5d4:7c776d33b75abad1fe36b14a5e5734af:56468b7a2883e66dadcd2af13ebd8010$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,524 @@
{
"textures": [
{
"image": "buck.png",
"format": "RGBA8888",
"size": {
"w": 120,
"h": 78
},
"scale": 1,
"frames": [
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 33,
"y": 4,
"w": 35,
"h": 76
},
"frame": {
"x": 1,
"y": 1,
"w": 35,
"h": 76
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 18,
"y": 8,
"w": 44,
"h": 72
},
"frame": {
"x": 38,
"y": 1,
"w": 44,
"h": 72
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 18,
"y": 8,
"w": 44,
"h": 72
},
"frame": {
"x": 38,
"y": 1,
"w": 44,
"h": 72
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 15,
"y": 8,
"w": 44,
"h": 72
},
"frame": {
"x": 38,
"y": 1,
"w": 44,
"h": 72
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 15,
"y": 8,
"w": 44,
"h": 72
},
"frame": {
"x": 38,
"y": 1,
"w": 44,
"h": 72
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 44,
"h": 72
},
"frame": {
"x": 38,
"y": 1,
"w": 44,
"h": 72
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 44,
"h": 72
},
"frame": {
"x": 38,
"y": 1,
"w": 44,
"h": 72
}
},
{
"filename": "0000.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 34,
"y": 5,
"w": 35,
"h": 75
},
"frame": {
"x": 84,
"y": 1,
"w": 35,
"h": 75
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:033f3d363b4192f64c92e02c19622c15:0d06141bef5af87ef82da967253207cb:3347efe478119141b0e3e6eccdecd0f5$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,398 @@
{
"textures": [
{
"image": "cheryl.png",
"format": "RGBA8888",
"size": {
"w": 154,
"h": 83
},
"scale": 1,
"frames": [
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 25,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 1,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 25,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 1,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 26,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 1,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 26,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 1,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 27,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 44,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 27,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 44,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 24,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 44,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 24,
"y": 0,
"w": 41,
"h": 81
},
"frame": {
"x": 44,
"y": 1,
"w": 41,
"h": 81
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 27,
"y": 0,
"w": 33,
"h": 81
},
"frame": {
"x": 87,
"y": 1,
"w": 33,
"h": 81
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 27,
"y": 0,
"w": 33,
"h": 81
},
"frame": {
"x": 87,
"y": 1,
"w": 33,
"h": 81
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 26,
"y": 0,
"w": 33,
"h": 81
},
"frame": {
"x": 87,
"y": 1,
"w": 33,
"h": 81
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 26,
"y": 0,
"w": 33,
"h": 81
},
"frame": {
"x": 87,
"y": 1,
"w": 33,
"h": 81
}
},
{
"filename": "0000.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 20,
"y": 0,
"w": 31,
"h": 81
},
"frame": {
"x": 122,
"y": 1,
"w": 31,
"h": 81
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 20,
"y": 0,
"w": 31,
"h": 81
},
"frame": {
"x": 122,
"y": 1,
"w": 31,
"h": 81
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 20,
"y": 0,
"w": 31,
"h": 81
},
"frame": {
"x": 122,
"y": 1,
"w": 31,
"h": 81
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 20,
"y": 0,
"w": 31,
"h": 81
},
"frame": {
"x": 122,
"y": 1,
"w": 31,
"h": 81
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 21,
"y": 0,
"w": 31,
"h": 81
},
"frame": {
"x": 122,
"y": 1,
"w": 31,
"h": 81
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 81
},
"spriteSourceSize": {
"x": 21,
"y": 0,
"w": 31,
"h": 81
},
"frame": {
"x": 122,
"y": 1,
"w": 31,
"h": 81
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:dfcf7aedbd588c4e42427a2e17c171bf:206549943a0e3325d20a017ef01eefee:a233cd27590422717866c66e366b68fb$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,83 @@
{ "frames": [
{
"filename": "0000.png",
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0002.png",
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0003.png",
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0004.png",
"frame": { "x": 32, "y": 0, "w": 28, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 28, "y": 1, "w": 28, "h": 78 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0005.png",
"frame": { "x": 32, "y": 0, "w": 28, "h": 78 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 28, "y": 1, "w": 28, "h": 78 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0006.png",
"frame": { "x": 0, "y": 78, "w": 31, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 28, "y": 2, "w": 31, "h": 77 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0007.png",
"frame": { "x": 0, "y": 78, "w": 31, "h": 77 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 28, "y": 2, "w": 31, "h": 77 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
}
],
"meta": {
"app": "https://www.pngprite.org/",
"version": "1.3.7-x64",
"image": "marley.png",
"format": "I8",
"size": { "w": 60, "h": 155 },
"scale": "1"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,209 @@
{ "frames": [
{
"filename": "0000.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 23, "y": 14, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0001.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 23, "y": 14, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0002.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 22, "y": 13, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0003.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 11, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0004.png",
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 11, "w": 53, "h": 63 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0005.png",
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 12, "w": 53, "h": 63 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0006.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 13, "y": 11, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0007.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 13, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0008.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 14, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0009.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 13, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0010.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 13, "y": 11, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0011.png",
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 11, "w": 53, "h": 63 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0012.png",
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 12, "w": 53, "h": 63 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0013.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 11, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0014.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 22, "y": 13, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0015.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 23, "y": 14, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0016.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 22, "y": 13, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0017.png",
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 11, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0018.png",
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 11, "w": 53, "h": 63 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0019.png",
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 21, "y": 12, "w": 53, "h": 63 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0020.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 13, "y": 11, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0021.png",
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 13, "w": 44, "h": 65 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"image": "mira.png",
"format": "I8",
"size": { "w": 97, "h": 128 },
"scale": "1"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,209 @@
{ "frames": [
{
"filename": "0000.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0002.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0003.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0004.png",
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 31, "y": 0, "w": 37, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0005.png",
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 31, "y": 0, "w": 37, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0006.png",
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 30, "y": 0, "w": 37, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0007.png",
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 30, "y": 0, "w": 37, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0008.png",
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 28, "y": 0, "w": 37, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0009.png",
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 28, "y": 0, "w": 37, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0010.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0011.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 10, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0012.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0013.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0014.png",
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0015.png",
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0016.png",
"frame": { "x": 0, "y": 80, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0017.png",
"frame": { "x": 0, "y": 80, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0018.png",
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0019.png",
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0020.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
},
{
"filename": "0021.png",
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
"rotated": false,
"trimmed": true,
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
"sourceSize": { "w": 80, "h": 80 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"image": "riley.png",
"format": "I8",
"size": { "w": 110, "h": 160 },
"scale": "1"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "vicky.png",
"format": "RGBA8888",
"size": {
"w": 52,
"h": 53
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 27,
"w": 52,
"h": 53
},
"frame": {
"x": 0,
"y": 0,
"w": 52,
"h": 53
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:bf9d2d417a1982282dd711456ac71206:101e07828e3d6e2a2a7a80aebfa802ad:cabe44a4410c334298b1984a219f8160$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "victor.png",
"format": "RGBA8888",
"size": {
"w": 55,
"h": 53
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 12,
"y": 27,
"w": 55,
"h": 53
},
"frame": {
"x": 0,
"y": 0,
"w": 55,
"h": 53
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:64eff0f697754cdf9552b46342c9292a:611e0e2cacbd90c1229ce5443b2414f0:0cc0f5a2c1b2eedb46dd8318e8feb1d8$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "victoria.png",
"format": "RGBA8888",
"size": {
"w": 52,
"h": 54
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 14,
"y": 26,
"w": 52,
"h": 54
},
"frame": {
"x": 0,
"y": 0,
"w": 52,
"h": 54
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:4dafeae3674d63b12cc4d8044f67b5a3:7834687d784c31169256927f419c7958:cf0eb39e0a3f2e42f23ca29747d73c40$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "vito.png",
"format": "RGBA8888",
"size": {
"w": 41,
"h": 78
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 20,
"y": 2,
"w": 41,
"h": 78
},
"frame": {
"x": 0,
"y": 0,
"w": 41,
"h": 78
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:cb988be58fcd5381174e9d120b051e38:4d4723dbbcd9713ee0ed3c2d84ef4bfb:1c7723b536b218346e3138016d865ce9$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "vivi.png",
"format": "RGBA8888",
"size": {
"w": 48,
"h": 69
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 80,
"h": 80
},
"spriteSourceSize": {
"x": 13,
"y": 11,
"w": 48,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 48,
"h": 69
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:0a51b4df0b2ed0fed7e3bdb5dffd9e28:af1f3b1480023b3e3761c49e49faf5f1:4fc6bf2bec74c4bb8809df38231deb01$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

View File

@ -2,18 +2,28 @@ import Phaser from "phaser";
import UI from "./ui/ui";
import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
import { Constructor } from "#app/utils";
import { Constructor, isNullOrUndefined } from "#app/utils";
import * as Utils from "./utils";
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems, PokemonIncrementingStatModifier, ExpShareModifier, ExpBalanceModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "./modifier/modifier";
import { PokeballType } from "./data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
import { Phase } from "./phase";
import { initGameSpeed } from "./system/game-speed";
import { Arena, ArenaBase } from "./field/arena";
import { GameData } from "./system/game-data";
import { TextStyle, addTextObject, getTextColor } from "./ui/text";
import { addTextObject, getTextColor, TextStyle } from "./ui/text";
import { allMoves } from "./data/move";
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, modifierTypes } from "./modifier/modifier-type";
import {
ModifierPoolType,
getDefaultModifierTypeForTier,
getEnemyModifierTypesForWave,
getLuckString,
getLuckTextTint,
getModifierPoolForType,
getModifierType,
getPartyLuckValue,
modifierTypes, PokemonHeldItemModifierType
} from "./modifier/modifier-type";
import AbilityBar from "./ui/ability-bar";
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, ChangeMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability";
import { allAbilities } from "./data/ability";
@ -22,14 +32,14 @@ import { GameMode, GameModes, getGameMode } from "./game-mode";
import FieldSpritePipeline from "./pipelines/field-sprite";
import SpritePipeline from "./pipelines/sprite";
import PartyExpBar from "./ui/party-exp-bar";
import { TrainerSlot, trainerConfigs } from "./data/trainer-config";
import { trainerConfigs, TrainerSlot } from "./data/trainer-config";
import Trainer, { TrainerVariant } from "./field/trainer";
import TrainerData from "./system/trainer-data";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
import PokeballTray from "./ui/pokeball-tray";
import InvertPostFX from "./pipelines/invert";
import { Achv, ModifierAchv, MoneyAchv, achvs } from "./system/achv";
import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv";
import { Voucher, vouchers } from "./system/voucher";
import { Gender } from "./data/gender";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
@ -86,6 +96,15 @@ import { TitlePhase } from "./phases/title-phase";
import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase";
import { TurnInitPhase } from "./phases/turn-init-phase";
import { ShopCursorTarget } from "./enums/shop-cursor-target";
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { ExpPhase } from "#app/phases/exp-phase";
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -246,6 +265,10 @@ export default class BattleScene extends SceneBase {
public money: integer;
public pokemonInfoContainer: PokemonInfoContainer;
private party: PlayerPokemon[];
/** Session save data that pertains to Mystery Encounters */
public mysteryEncounterSaveData: MysteryEncounterSaveData = new MysteryEncounterSaveData();
/** If the previous wave was a MysteryEncounter, tracks the object with this variable. Mostly used for visual object cleanup */
public lastMysteryEncounter?: MysteryEncounter;
/** Combined Biome and Wave count text */
private biomeWaveText: Phaser.GameObjects.Text;
private moneyText: Phaser.GameObjects.Text;
@ -884,6 +907,26 @@ export default class BattleScene extends SceneBase {
return pokemon;
}
/**
* Removes a {@linkcode PlayerPokemon} from the party, and clears modifiers for that Pokemon's id
* Useful for MEs/Challenges that remove Pokemon from the player party temporarily or permanently
* @param pokemon
* @param destroy Default true. If true, will destroy the {@linkcode PlayerPokemon} after removing
*/
removePokemonFromPlayerParty(pokemon: PlayerPokemon, destroy: boolean = true) {
if (!pokemon) {
return;
}
const partyIndex = this.party.indexOf(pokemon);
this.party.splice(partyIndex, 1);
if (destroy) {
this.field.remove(pokemon, true);
pokemon.destroy();
}
this.updateModifiers(true);
}
addPokemonIcon(pokemon: Pokemon, x: number, y: number, originX: number = 0.5, originY: number = 0.5, ignoreOverride: boolean = false): Phaser.GameObjects.Container {
const container = this.add.container(x, y);
container.setName(`${pokemon.name}-icon`);
@ -1086,7 +1129,7 @@ export default class BattleScene extends SceneBase {
}
}
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle | null {
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounterType?: MysteryEncounterType): Battle | null {
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
let newDouble: boolean | undefined;
@ -1135,6 +1178,36 @@ export default class BattleScene extends SceneBase {
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant);
this.field.add(newTrainer);
}
// Check for mystery encounter
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) {
const roll = Utils.randSeedInt(MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT);
// Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
const encounteredEvents = this.mysteryEncounterSaveData.encounteredEvents;
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well)
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (newWaveIndex - lowestMysteryEncounterWave);
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER;
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
// If the most recent ME was 3 or fewer waves ago, can never spawn a ME
const canSpawn = encounteredEvents.length === 0 || (newWaveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex) > 3 || !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE);
if (canSpawn && roll < successRate) {
newBattleType = BattleType.MYSTERY_ENCOUNTER;
// Reset base spawn weight
this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
} else {
this.mysteryEncounterSaveData.encounterSpawnChance = sessionEncounterRate + WEIGHT_INCREMENT_ON_SPAWN_MISS;
}
}
}
if (double === undefined && newWaveIndex > 1) {
@ -1167,12 +1240,21 @@ export default class BattleScene extends SceneBase {
const maxExpLevel = this.getMaxExpLevel();
this.lastEnemyTrainer = lastBattle?.trainer ?? null;
this.lastMysteryEncounter = lastBattle?.mysteryEncounter;
this.executeWithSeedOffset(() => {
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
}, newWaveIndex << 3, this.waveSeed);
this.currentBattle.incrementTurn(this);
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
this.currentBattle.double = false;
this.executeWithSeedOffset(() => {
this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounterType);
}, this.currentBattle.waveIndex << 4);
}
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
if (!waveIndex && lastBattle) {
@ -1181,7 +1263,7 @@ export default class BattleScene extends SceneBase {
const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0;
const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49;
const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne);
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
const resetArenaState = isNewBiome || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
this.trySpreadPokerus();
if (!isNewBiome && (newWaveIndex % 10) === 5) {
@ -1189,14 +1271,21 @@ export default class BattleScene extends SceneBase {
}
if (resetArenaState) {
this.arena.resetArenaEffects();
playerField.forEach((_, p) => this.pushPhase(new ReturnPhase(this, p)));
playerField.forEach((pokemon, p) => {
if (pokemon.isOnField()) {
this.pushPhase(new ReturnPhase(this, p));
}
});
for (const pokemon of this.getParty()) {
pokemon.resetBattleData();
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
}
this.pushPhase(new ShowTrainerPhase(this));
if (!this.trainer.visible) {
this.pushPhase(new ShowTrainerPhase(this));
}
}
for (const pokemon of this.getParty()) {
@ -1290,7 +1379,6 @@ export default class BattleScene extends SceneBase {
case Species.ZARUDE:
case Species.SQUAWKABILLY:
case Species.TATSUGIRI:
case Species.GIMMIGHOUL:
case Species.PALDEA_TAUROS:
return Utils.randSeedInt(species.forms.length);
case Species.PIKACHU:
@ -1316,6 +1404,13 @@ export default class BattleScene extends SceneBase {
return 1;
}
return 0;
case Species.GIMMIGHOUL:
// Chest form can only be found in Mysterious Chest Encounter, if this is a game mode with MEs
if (this.gameMode.hasMysteryEncounters) {
return 1; // Wandering form
} else {
return Utils.randSeedInt(species.forms.length);
}
}
if (ignoreArena) {
@ -2485,7 +2580,7 @@ export default class BattleScene extends SceneBase {
});
}
generateEnemyModifiers(): Promise<void> {
generateEnemyModifiers(heldModifiersConfigs?: HeldModifierConfig[][]): Promise<void> {
return new Promise(resolve => {
if (this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
return resolve();
@ -2507,29 +2602,47 @@ export default class BattleScene extends SceneBase {
}
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
let upgradeChance = 32;
if (isBoss) {
upgradeChance /= 2;
}
if (isFinalBoss) {
upgradeChance /= 8;
}
const modifierChance = this.gameMode.getEnemyModifierChance(isBoss);
let pokemonModifierChance = modifierChance;
if (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer)
pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line
let count = 0;
for (let c = 0; c < chances; c++) {
if (!Utils.randSeedInt(modifierChance)) {
count++;
if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i] && heldModifiersConfigs[i].length > 0) {
heldModifiersConfigs[i].forEach(mt => {
let modifier: PokemonHeldItemModifier;
if (mt.modifier instanceof PokemonHeldItemModifierType) {
modifier = mt.modifier.newModifier(enemyPokemon);
} else {
modifier = mt.modifier as PokemonHeldItemModifier;
modifier.pokemonId = enemyPokemon.id;
}
const stackCount = mt.stackCount ?? 1;
modifier.stackCount = stackCount;
// TODO: set isTransferable
// modifier.isTransferrable = mt.isTransferable ?? true;
this.addEnemyModifier(modifier, true);
});
} else {
const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
let upgradeChance = 32;
if (isBoss) {
upgradeChance /= 2;
}
if (isFinalBoss) {
upgradeChance /= 8;
}
const modifierChance = this.gameMode.getEnemyModifierChance(isBoss);
let pokemonModifierChance = modifierChance;
if (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer)
pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line
let count = 0;
for (let c = 0; c < chances; c++) {
if (!Utils.randSeedInt(modifierChance)) {
count++;
}
}
if (isBoss) {
count = Math.max(count, Math.floor(chances / 2));
}
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance)
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
}
if (isBoss) {
count = Math.max(count, Math.floor(chances / 2));
}
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance)
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
return true;
});
this.updateModifiers(false).then(() => resolve());
});
@ -2837,4 +2950,220 @@ export default class BattleScene extends SceneBase {
this.shiftPhase();
}
/**
* Updates Exp and level values for Player's party, adding new level up phases as required
* @param expValue raw value of exp to split among participants, OR the base multiplier to use with waveIndex
* @param pokemonDefeated If true, will increment Macho Brace stacks and give the party Pokemon friendship increases
* @param useWaveIndexMultiplier Default false. If true, will multiply expValue by a scaling waveIndex multiplier. Not needed if expValue is already scaled by level/wave
* @param pokemonParticipantIds Participants. If none are defined, no exp will be given. To spread evenly among the party, should pass all ids of party members.
*/
applyPartyExp(expValue: number, pokemonDefeated: boolean, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>): void {
const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds;
const party = this.getParty();
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
const multipleParticipantExpBonusModifier = this.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier;
const nonFaintedPartyMembers = party.filter(p => p.hp);
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.getMaxExpLevel());
const partyMemberExp: number[] = [];
// EXP value calculation is based off Pokemon.getExpValue
if (useWaveIndexMultiplier) {
expValue = Math.floor(expValue * this.currentBattle.waveIndex / 5 + 1);
}
if (participantIds.size > 0) {
if (this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
expValue = Math.floor(expValue * 1.5);
} else if (this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && this.currentBattle.mysteryEncounter) {
expValue = Math.floor(expValue * this.currentBattle.mysteryEncounter.expMultiplier);
}
for (const partyMember of nonFaintedPartyMembers) {
const pId = partyMember.id;
const participated = participantIds.has(pId);
if (participated && pokemonDefeated) {
partyMember.addFriendship(2);
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) {
machoBraceModifier.stackCount++;
this.updateModifiers(true, true);
partyMember.updateInfo();
}
}
if (!expPartyMembers.includes(partyMember)) {
continue;
}
if (!participated && !expShareModifier) {
partyMemberExp.push(0);
continue;
}
let expMultiplier = 0;
if (participated) {
expMultiplier += (1 / participantIds.size);
if (participantIds.size > 1 && multipleParticipantExpBonusModifier) {
expMultiplier += multipleParticipantExpBonusModifier.getStackCount() * 0.2;
}
} else if (expShareModifier) {
expMultiplier += (expShareModifier.getStackCount() * 0.2) / participantIds.size;
}
if (partyMember.pokerus) {
expMultiplier *= 1.5;
}
if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) {
expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE;
}
const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier);
this.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp);
partyMemberExp.push(Math.floor(pokemonExp.value));
}
if (expBalanceModifier) {
let totalLevel = 0;
let totalExp = 0;
expPartyMembers.forEach((expPartyMember, epm) => {
totalExp += partyMemberExp[epm];
totalLevel += expPartyMember.level;
});
const medianLevel = Math.floor(totalLevel / expPartyMembers.length);
const recipientExpPartyMemberIndexes: number[] = [];
expPartyMembers.forEach((expPartyMember, epm) => {
if (expPartyMember.level <= medianLevel) {
recipientExpPartyMemberIndexes.push(epm);
}
});
const splitExp = Math.floor(totalExp / recipientExpPartyMemberIndexes.length);
expPartyMembers.forEach((_partyMember, pm) => {
partyMemberExp[pm] = Phaser.Math.Linear(partyMemberExp[pm], recipientExpPartyMemberIndexes.indexOf(pm) > -1 ? splitExp : 0, 0.2 * expBalanceModifier.getStackCount());
});
}
for (let pm = 0; pm < expPartyMembers.length; pm++) {
const exp = partyMemberExp[pm];
if (exp) {
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
this.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(this, partyMemberIndex, exp) : new ShowPartyExpBarPhase(this, partyMemberIndex, exp));
}
}
}
}
/**
* Loads or generates a mystery encounter
* @param encounterType used to load session encounter when restarting game, etc.
* @returns
*/
getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter {
// Loading override or session encounter
let encounter: MysteryEncounter | null;
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE!)) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE!];
} else {
encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType!] : null;
}
// Check for queued encounters first
if (!encounter && this.mysteryEncounterSaveData?.queuedEncounters && this.mysteryEncounterSaveData.queuedEncounters.length > 0) {
let i = 0;
while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) {
const candidate = this.mysteryEncounterSaveData.queuedEncounters[i];
const forcedChance = candidate.spawnPercent;
if (Utils.randSeedInt(100) < forcedChance) {
encounter = allMysteryEncounters[candidate.type];
}
i++;
}
}
if (encounter) {
encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements(this);
return encounter;
}
// See Enum values for base tier weights
const tierWeights = [MysteryEncounterTier.COMMON, MysteryEncounterTier.GREAT, MysteryEncounterTier.ULTRA, MysteryEncounterTier.ROGUE];
// Adjust tier weights by previously encountered events to lower odds of only Common/Great in run
this.mysteryEncounterSaveData.encounteredEvents.forEach(seenEncounterData => {
if (seenEncounterData.tier === MysteryEncounterTier.COMMON) {
tierWeights[0] = tierWeights[0] - 6;
} else if (seenEncounterData.tier === MysteryEncounterTier.GREAT) {
tierWeights[1] = tierWeights[1] - 4;
}
});
const totalWeight = tierWeights.reduce((a, b) => a + b);
const tierValue = Utils.randSeedInt(totalWeight);
const commonThreshold = totalWeight - tierWeights[0];
const greatThreshold = totalWeight - tierWeights[0] - tierWeights[1];
const ultraThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2];
let tier: MysteryEncounterTier | null = tierValue > commonThreshold ? MysteryEncounterTier.COMMON : tierValue > greatThreshold ? MysteryEncounterTier.GREAT : tierValue > ultraThreshold ? MysteryEncounterTier.ULTRA : MysteryEncounterTier.ROGUE;
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE)) {
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE!;
}
let availableEncounters: MysteryEncounter[] = [];
// New encounter should never be the same as the most recent encounter
const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ? this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type : null;
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? [];
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
while (availableEncounters.length === 0 && tier !== null) {
availableEncounters = biomeMysteryEncounters
.filter((encounterType) => {
const encounterCandidate = allMysteryEncounters[encounterType];
if (!encounterCandidate) {
return false;
}
if (encounterCandidate.encounterTier !== tier) { // Encounter is in tier
return false;
}
const disabledModes = encounterCandidate.disabledGameModes;
if (disabledModes && disabledModes.length > 0
&& disabledModes.includes(this.gameMode.modeId)) { // Encounter is enabled for game mode
return false;
}
if (!encounterCandidate.meetsRequirements(this)) { // Meets encounter requirements
return false;
}
if (previousEncounter !== null && encounterType === previousEncounter) { // Previous encounter was not this one
return false;
}
if (this.mysteryEncounterSaveData.encounteredEvents.length > 0 && // Encounter has not exceeded max allowed encounters
(encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0)
&& this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
return false;
}
return true;
})
.map((m) => (allMysteryEncounters[m]));
// Decrement tier
if (tier === MysteryEncounterTier.ROGUE) {
tier = MysteryEncounterTier.ULTRA;
} else if (tier === MysteryEncounterTier.ULTRA) {
tier = MysteryEncounterTier.GREAT;
} else if (tier === MysteryEncounterTier.GREAT) {
tier = MysteryEncounterTier.COMMON;
} else {
tier = null; // Ends loop
}
}
// If absolutely no encounters are available, spawn 0th encounter
if (availableEncounters.length === 0) {
console.log("No Mystery Encounters found, falling back to Mysterious Challengers.");
return allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS];
}
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
// New encounter object to not dirty flags
encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements(this);
return encounter;
}
}

View File

@ -14,28 +14,31 @@ import { PlayerGender } from "#enums/player-gender";
import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type";
import i18next from "#app/plugins/i18n";
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
export enum BattleType {
WILD,
TRAINER,
CLEAR
WILD,
TRAINER,
CLEAR,
MYSTERY_ENCOUNTER
}
export enum BattlerIndex {
ATTACKER = -1,
PLAYER,
PLAYER_2,
ENEMY,
ENEMY_2
ATTACKER = -1,
PLAYER,
PLAYER_2,
ENEMY,
ENEMY_2
}
export interface TurnCommand {
command: Command;
cursor?: number;
move?: QueuedMove;
targets?: BattlerIndex[];
skip?: boolean;
args?: any[];
command: Command;
cursor?: number;
move?: QueuedMove;
targets?: BattlerIndex[];
skip?: boolean;
args?: any[];
}
export interface FaintLogEntry {
@ -44,7 +47,7 @@ export interface FaintLogEntry {
}
interface TurnCommands {
[key: number]: TurnCommand | null
[key: number]: TurnCommand | null
}
export default class Battle {
@ -77,6 +80,9 @@ export default class Battle {
public playerFaintsHistory: FaintLogEntry[] = [];
public enemyFaintsHistory: FaintLogEntry[] = [];
/** If the current battle is a Mystery Encounter, this will always be defined */
public mysteryEncounter?: MysteryEncounter;
private rngCounter: number = 0;
constructor(gameMode: GameMode, waveIndex: number, battleType: BattleType, trainer?: Trainer, double?: boolean) {
@ -99,7 +105,7 @@ export default class Battle {
this.battleSpec = spec;
}
private getLevelForWave(): number {
public getLevelForWave(): number {
const levelWaveIndex = this.gameMode.getWaveForDifficulty(this.waveIndex);
const baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2);
const bossMultiplier = 1.2;
@ -197,7 +203,11 @@ export default class Battle {
getBgmOverride(scene: BattleScene): string | null {
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
if (this.battleType === BattleType.TRAINER) {
if (this.battleType === BattleType.MYSTERY_ENCOUNTER && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
// Music is overridden for MEs during ME onInit()
// Should not use any BGM overrides before swapping from DEFAULT mode
return null;
} else if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
return `encounter_${this.trainer?.getEncounterBgm()}`;
}

View File

@ -7,6 +7,9 @@ import { BattlerIndex } from "../battle";
import { Element } from "json-stable-stringify";
import { Moves } from "#enums/moves";
import { SubstituteTag } from "./battler-tags";
import { isNullOrUndefined } from "../utils";
import Phaser from "phaser";
import { EncounterAnim } from "#enums/encounter-anims";
//import fs from 'vite-plugin-fs/browser';
export enum AnimFrameTarget {
@ -304,7 +307,7 @@ abstract class AnimTimedEvent {
this.resourceName = resourceName;
}
abstract execute(scene: BattleScene, battleAnim: BattleAnim): integer;
abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer;
abstract getEventType(): string;
}
@ -322,7 +325,7 @@ class AnimTimedSoundEvent extends AnimTimedEvent {
}
}
execute(scene: BattleScene, battleAnim: BattleAnim): integer {
execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer {
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
if (this.resourceName) {
try {
@ -384,7 +387,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source);
}
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
const tweenProps = {};
if (this.bgX !== undefined) {
tweenProps["x"] = (this.bgX * 0.5) - 320;
@ -414,7 +417,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source);
}
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
if (moveAnim.bgSprite) {
moveAnim.bgSprite.destroy();
}
@ -426,7 +429,9 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
moveAnim.bgSprite.setAlpha(this.opacity / 255);
scene.field.add(moveAnim.bgSprite);
const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon();
if (fieldPokemon?.isOnField()) {
if (!isNullOrUndefined(priority)) {
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!);
} else if (fieldPokemon?.isOnField()) {
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
}
@ -446,6 +451,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig] | null>();
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig] | null>();
export const commonAnims = new Map<CommonAnim, AnimConfig>();
export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
export function initCommonAnims(scene: BattleScene): Promise<void> {
return new Promise(resolve => {
@ -516,6 +522,26 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
});
}
/**
* Fetches animation configs to be used in a Mystery Encounter
* @param scene
* @param encounterAnim one or more animations to fetch
*/
export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim];
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
for (const anim of anims) {
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
continue;
}
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json())
.then(cas => encounterAnims.set(anim, new AnimConfig(cas))));
}
await Promise.allSettled(encounterAnimFetches);
}
export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise<void> {
return new Promise(resolve => {
if (chargeAnims.has(chargeAnim)) {
@ -570,6 +596,16 @@ export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): P
});
}
/**
* Loads encounter animation assets to scene
* MUST be called after {@linkcode initEncounterAnims()} to load all required animations properly
* @param scene
* @param startLoad
*/
export async function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> {
await loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad);
}
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> {
return new Promise(resolve => {
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
@ -679,14 +715,16 @@ export abstract class BattleAnim {
public target: Pokemon | null;
public sprites: Phaser.GameObjects.Sprite[];
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
public playOnEmptyField: boolean;
private srcLine: number[];
private dstLine: number[];
constructor(user?: Pokemon, target?: Pokemon) {
constructor(user?: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) {
this.user = user ?? null;
this.target = target ?? null;
this.sprites = [];
this.playOnEmptyField = playOnEmptyField;
}
abstract getAnim(): AnimConfig | null;
@ -761,9 +799,9 @@ export abstract class BattleAnim {
play(scene: BattleScene, onSubstitute?: boolean, callback?: Function) {
const isOppAnim = this.isOppAnim();
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
const target = !isOppAnim ? this.target : this.user;
const target = !isOppAnim ? this.target! : this.user!;
if (!target?.isOnField()) {
if (!target?.isOnField() && !this.playOnEmptyField) {
if (callback) {
callback();
}
@ -866,6 +904,8 @@ export abstract class BattleAnim {
const isUser = frame.target === AnimFrameTarget.USER;
if (isUser && target === user) {
continue;
} else if (this.playOnEmptyField && frame.target === AnimFrameTarget.TARGET && !target.isOnField()) {
continue;
}
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
const spriteSource = isUser ? userSprite : targetSprite;
@ -1017,13 +1057,175 @@ export abstract class BattleAnim {
}
});
}
private getGraphicFrameDataWithoutTarget(frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
[AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
]);
let g = 0;
let u = 0;
let t = 0;
for (const frame of frames) {
let { x, y } = frame;
const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1);
const scaleY = (frame.zoomY / 100);
x += targetInitialX;
y += targetInitialY;
const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
ret.get(frame.target)?.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle });
}
return ret;
}
/**
*
* @param scene
* @param targetInitialX
* @param targetInitialY
* @param frameTimeMult
* @param frameTimedEventPriority
* - 0 is behind all other sprites (except BG)
* - 1 on top of player field
* - 3 is on top of both fields
* - 5 is on top of player sprite
* @param callback
*/
playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) {
const spriteCache: SpriteCache = {
[AnimFrameTarget.GRAPHIC]: [],
[AnimFrameTarget.USER]: [],
[AnimFrameTarget.TARGET]: []
};
const cleanUpAndComplete = () => {
for (const ms of Object.values(spriteCache).flat()) {
if (ms) {
ms.destroy();
}
}
if (this.bgSprite) {
this.bgSprite.destroy();
}
if (callback) {
callback();
}
};
if (!scene.moveAnimations) {
return cleanUpAndComplete();
}
const anim = this.getAnim();
this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ];
this.dstLine = [ 150, 75, targetInitialX, targetInitialY ];
let totalFrames = anim!.frames.length;
let frameCount = 0;
let existingFieldSprites = scene.field.getAll().slice(0);
scene.tweens.addCounter({
duration: Utils.getFrameMs(3) * frameTimeMult,
repeat: anim!.frames.length,
onRepeat: () => {
existingFieldSprites = scene.field.getAll().slice(0);
const spriteFrames = anim!.frames[frameCount];
const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[frameCount], targetInitialX, targetInitialY);
let graphicFrameCount = 0;
for (const frame of spriteFrames) {
if (frame.target !== AnimFrameTarget.GRAPHIC) {
console.log("Encounter animations do not support targets");
continue;
}
const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
if (graphicFrameCount === sprites.length) {
const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1);
sprites.push(newSprite);
scene.field.add(newSprite);
}
const graphicIndex = graphicFrameCount++;
const moveSprite = sprites[graphicIndex];
if (!isNullOrUndefined(frame.priority)) {
const setSpritePriority = (priority: integer) => {
if (existingFieldSprites.length > priority) {
// Move to specified priority index
const index = scene.field.getIndex(existingFieldSprites[priority]);
scene.field.moveTo(moveSprite, index);
} else {
// Move to top of scene
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
}
};
setSpritePriority(frame.priority);
}
moveSprite.setFrame(frame.graphicFrame);
const graphicFrameData = frameData.get(frame.target)?.get(graphicIndex);
if (graphicFrameData) {
moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y);
moveSprite.setAngle(graphicFrameData.angle);
moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY);
moveSprite.setAlpha(frame.opacity / 255);
moveSprite.setVisible(frame.visible);
moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE);
}
}
if (anim?.frameTimedEvents.get(frameCount)) {
for (const event of anim.frameTimedEvents.get(frameCount)!) {
totalFrames = Math.max((anim.frames.length - frameCount) + event.execute(scene, this, frameTimedEventPriority), totalFrames);
}
}
const targets = Utils.getEnumValues(AnimFrameTarget);
for (const i of targets) {
const count = graphicFrameCount;
if (count < spriteCache[i].length) {
const spritesToRemove = spriteCache[i].slice(count, spriteCache[i].length);
for (const sprite of spritesToRemove) {
if (!sprite.getData("locked") as boolean) {
const spriteCacheIndex = spriteCache[i].indexOf(sprite);
spriteCache[i].splice(spriteCacheIndex, 1);
sprite.destroy();
}
}
}
}
frameCount++;
totalFrames--;
},
onComplete: () => {
for (const sprite of Object.values(spriteCache).flat()) {
if (sprite && !sprite.getData("locked")) {
sprite.destroy();
}
}
if (totalFrames) {
scene.tweens.addCounter({
duration: Utils.getFrameMs(totalFrames),
onComplete: () => cleanUpAndComplete()
});
} else {
cleanUpAndComplete();
}
}
});
}
}
export class CommonBattleAnim extends BattleAnim {
public commonAnim: CommonAnim | null;
constructor(commonAnim: CommonAnim | null, user: Pokemon, target?: Pokemon) {
super(user, target || user);
constructor(commonAnim: CommonAnim | null, user: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) {
super(user, target || user, playOnEmptyField);
this.commonAnim = commonAnim;
}
@ -1040,8 +1242,8 @@ export class CommonBattleAnim extends BattleAnim {
export class MoveAnim extends BattleAnim {
public move: Moves;
constructor(move: Moves, user: Pokemon, target: BattlerIndex) {
super(user, user.scene.getField()[target]);
constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField: boolean = false) {
super(user, user.scene.getField()[target], playOnEmptyField);
this.move = move;
}
@ -1085,6 +1287,26 @@ export class MoveChargeAnim extends MoveAnim {
}
}
export class EncounterBattleAnim extends BattleAnim {
public encounterAnim: EncounterAnim;
public oppAnim: boolean;
constructor(encounterAnim: EncounterAnim, user: Pokemon, target?: Pokemon, oppAnim?: boolean) {
super(user, target ?? user, true);
this.encounterAnim = encounterAnim;
this.oppAnim = oppAnim ?? false;
}
getAnim(): AnimConfig | null {
return encounterAnims.get(this.encounterAnim) ?? null;
}
isOppAnim(): boolean {
return this.oppAnim;
}
}
export async function populateAnims() {
const commonAnimNames = Utils.getEnumKeys(CommonAnim).map(k => k.toLowerCase());
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, ""));

View File

@ -5,7 +5,7 @@ import { StatusEffect } from "./status-effect";
import * as Utils from "../utils";
import { ChargeAttr, MoveFlags, allMoves } from "./move";
import { Type } from "./type";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, ProtectStatAbAttr } from "./ability";
import { TerrainType } from "./terrain";
import { WeatherType } from "./weather";
import { allAbilities } from "./ability";
@ -2304,6 +2304,45 @@ export class SubstituteTag extends BattlerTag {
}
}
/**
* Tag that adds extra post-summon effects to a battle for a specific Pokemon.
* These post-summon effects are performed through {@linkcode Pokemon.mysteryEncounterBattleEffects},
* and can be used to unshift special phases, etc.
* Currently used only in MysteryEncounters to provide start of fight stat buffs.
*/
export class MysteryEncounterPostSummonTag extends BattlerTag {
constructor() {
super(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, BattlerTagLapseType.CUSTOM, 1);
}
/** Event when tag is added */
onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon);
}
/** Performs post-summon effects through {@linkcode Pokemon.mysteryEncounterBattleEffects} */
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = super.lapse(pokemon, lapseType);
if (lapseType === BattlerTagLapseType.CUSTOM) {
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
if (!cancelled.value) {
if (pokemon.mysteryEncounterBattleEffects) {
pokemon.mysteryEncounterBattleEffects(pokemon);
}
}
}
return ret;
}
/** Event when tag is removed */
onRemove(pokemon: Pokemon): void {
super.onRemove(pokemon);
}
}
/**
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
*
@ -2465,6 +2504,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new GorillaTacticsTag();
case BattlerTagType.SUBSTITUTE:
return new SubstituteTag(sourceMove, sourceId);
case BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON:
return new MysteryEncounterPostSummonTag();
case BattlerTagType.NONE:
default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -1093,6 +1093,136 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
]
}
],
[TrainerType.BUCK]: [
{
encounter: [
"dialogue:stat_trainer_buck.encounter.1",
"dialogue:stat_trainer_buck.encounter.2"
],
victory: [
"dialogue:stat_trainer_buck.victory.1",
"dialogue:stat_trainer_buck.victory.2"
],
defeat: [
"dialogue:stat_trainer_buck.defeat.1",
"dialogue:stat_trainer_buck.defeat.2"
]
}
],
[TrainerType.CHERYL]: [
{
encounter: [
"dialogue:stat_trainer_cheryl.encounter.1",
"dialogue:stat_trainer_cheryl.encounter.2"
],
victory: [
"dialogue:stat_trainer_cheryl.victory.1",
"dialogue:stat_trainer_cheryl.victory.2"
],
defeat: [
"dialogue:stat_trainer_cheryl.defeat.1",
"dialogue:stat_trainer_cheryl.defeat.2"
]
}
],
[TrainerType.MARLEY]: [
{
encounter: [
"dialogue:stat_trainer_marley.encounter.1",
"dialogue:stat_trainer_marley.encounter.2"
],
victory: [
"dialogue:stat_trainer_marley.victory.1",
"dialogue:stat_trainer_marley.victory.2"
],
defeat: [
"dialogue:stat_trainer_marley.defeat.1",
"dialogue:stat_trainer_marley.defeat.2"
]
}
],
[TrainerType.MIRA]: [
{
encounter: [
"dialogue:stat_trainer_mira.encounter.1",
"dialogue:stat_trainer_mira.encounter.2"
],
victory: [
"dialogue:stat_trainer_mira.victory.1",
"dialogue:stat_trainer_mira.victory.2"
],
defeat: [
"dialogue:stat_trainer_mira.defeat.1",
"dialogue:stat_trainer_mira.defeat.2"
]
}
],
[TrainerType.RILEY]: [
{
encounter: [
"dialogue:stat_trainer_riley.encounter.1",
"dialogue:stat_trainer_riley.encounter.2"
],
victory: [
"dialogue:stat_trainer_riley.victory.1",
"dialogue:stat_trainer_riley.victory.2"
],
defeat: [
"dialogue:stat_trainer_riley.defeat.1",
"dialogue:stat_trainer_riley.defeat.2"
]
}
],
[TrainerType.VICTOR]: [
{
encounter: [
"dialogue:winstrates_victor.encounter.1",
],
victory: [
"dialogue:winstrates_victor.victory.1"
],
}
],
[TrainerType.VICTORIA]: [
{
encounter: [
"dialogue:winstrates_victoria.encounter.1",
],
victory: [
"dialogue:winstrates_victoria.victory.1"
],
}
],
[TrainerType.VIVI]: [
{
encounter: [
"dialogue:winstrates_vivi.encounter.1",
],
victory: [
"dialogue:winstrates_vivi.victory.1"
],
}
],
[TrainerType.VICKY]: [
{
encounter: [
"dialogue:winstrates_vicky.encounter.1",
],
victory: [
"dialogue:winstrates_vicky.victory.1"
],
}
],
[TrainerType.VITO]: [
{
encounter: [
"dialogue:winstrates_vito.encounter.1",
],
victory: [
"dialogue:winstrates_vito.victory.1"
],
}
],
[TrainerType.BROCK]: {
encounter: [
"dialogue:brock.encounter.1",

View File

@ -61,7 +61,10 @@ export interface IEggOptions {
/** Defines if the egg will hatch with the hidden ability of this species.
* If no hidden ability exist, a random one will get choosen.
*/
overrideHiddenAbility?: boolean
overrideHiddenAbility?: boolean,
/** Can customize the message displayed for where the egg was obtained */
eggDescriptor?: string;
}
export class Egg {
@ -83,6 +86,8 @@ export class Egg {
private _overrideHiddenAbility: boolean;
private _eggDescriptor?: string;
////
// #endregion
////
@ -191,6 +196,8 @@ export class Egg {
} else { // For legacy eggs without scene
generateEggProperties(eggOptions);
}
this._eggDescriptor = eggOptions?.eggDescriptor;
}
////
@ -292,13 +299,15 @@ export class Egg {
public getEggTypeDescriptor(scene: BattleScene): string {
switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG:
return i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName()});
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName()});
case EggSourceType.GACHA_LEGENDARY:
return `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`;
return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`;
case EggSourceType.GACHA_SHINY:
return i18next.t("egg:gachaTypeShiny");
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
case EggSourceType.GACHA_MOVE:
return i18next.t("egg:gachaTypeMove");
return this._eggDescriptor ?? i18next.t("egg:gachaTypeMove");
case EggSourceType.EVENT:
return this._eggDescriptor ?? i18next.t("egg:eventType");
default:
console.warn("getEggTypeDescriptor case not defined. Returning default empty string");
return "";

View File

@ -0,0 +1,186 @@
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs, } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species";
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { randSeedInt } from "#app/utils";
import i18next from "i18next";
import { IEggOptions } from "#app/data/egg";
import { EggSourceType } from "#enums/egg-source-types";
import { EggTier } from "#enums/egg-type";
import { PartyHealPhase } from "#app/phases/party-heal-phase";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { modifierTypes } from "#app/modifier/modifier-type";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:aTrainersTest";
/**
* A Trainer's Test encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3816 | GitHub Issue #3816}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const ATrainersTestEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.A_TRAINERS_TEST)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withSceneWaveRangeRequirement(100, 180)
.withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
])
.withAutoHideIntroVisuals(false)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Randomly pick from 1 of the 5 stat trainers to spawn
let trainerType: TrainerType;
let spriteKeys;
let trainerNameKey: string;
switch (randSeedInt(5)) {
default:
case 0:
trainerType = TrainerType.BUCK;
spriteKeys = getSpriteKeysFromSpecies(Species.CLAYDOL);
trainerNameKey = "buck";
break;
case 1:
trainerType = TrainerType.CHERYL;
spriteKeys = getSpriteKeysFromSpecies(Species.BLISSEY);
trainerNameKey = "cheryl";
break;
case 2:
trainerType = TrainerType.MARLEY;
spriteKeys = getSpriteKeysFromSpecies(Species.ARCANINE);
trainerNameKey = "marley";
break;
case 3:
trainerType = TrainerType.MIRA;
spriteKeys = getSpriteKeysFromSpecies(Species.ALAKAZAM, false, 1);
trainerNameKey = "mira";
break;
case 4:
trainerType = TrainerType.RILEY;
spriteKeys = getSpriteKeysFromSpecies(Species.LUCARIO, false, 1);
trainerNameKey = "riley";
break;
}
// Dialogue and tokens for trainer
encounter.dialogue.intro = [
{
speaker: `trainerNames:${trainerNameKey}`,
text: `${namespace}.${trainerNameKey}.intro_dialogue`
}
];
encounter.options[0].dialogue!.selected = [
{
speaker: `trainerNames:${trainerNameKey}`,
text: `${namespace}.${trainerNameKey}.accept`
}
];
encounter.options[1].dialogue!.selected = [
{
speaker: `trainerNames:${trainerNameKey}`,
text: `${namespace}.${trainerNameKey}.decline`
}
];
encounter.setDialogueToken("statTrainerName", i18next.t(`trainerNames:${trainerNameKey}`));
const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(`trainerNames:${trainerNameKey}`);
encounter.misc = { trainerType, trainerNameKey, trainerEggDescription: eggDescription };
// Trainer config
const trainerConfig = trainerConfigs[trainerType].clone();
const trainerSpriteKey = trainerConfig.getSpriteKey();
encounter.enemyPartyConfigs.push({
levelAdditiveMultiplier: 1,
trainerConfig: trainerConfig
});
encounter.spriteConfigs = [
{
spriteKey: spriteKeys.spriteKey,
fileRoot: spriteKeys.fileRoot,
hasShadow: true,
repeat: true,
isPokemon: true,
x: 22,
y: -2,
yShadow: -2
},
{
spriteKey: trainerSpriteKey,
fileRoot: "trainer",
hasShadow: true,
disableAnimation: true,
x: -24,
y: 4,
yShadow: 4
}
];
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withIntroDialogue()
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`
},
async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Battle the stat trainer for an Egg and great rewards
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
await transitionMysteryEncounterIntroVisuals(scene);
const eggOptions: IEggOptions = {
scene,
pulled: false,
sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.ULTRA
};
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]);
return initBattleWithEnemyConfig(scene, config);
}
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`
},
async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Full heal party
scene.unshiftPhase(new PartyHealPhase(scene, true));
const eggOptions: IEggOptions = {
scene,
pulled: false,
sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.GREAT
};
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.rare`));
setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [eggOptions]);
leaveEncounterWithoutBattle(scene);
}
)
.withOutroDialogue([
{
text: `${namespace}.outro`,
},
])
.build();

View File

@ -0,0 +1,521 @@
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { PersistentModifierRequirement } from "../mystery-encounter-requirements";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { BerryModifier } from "#app/modifier/modifier";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#enums/moves";
import { BattlerTagType } from "#enums/battler-tag-type";
import { randInt } from "#app/utils";
import { BattlerIndex } from "#app/battle";
import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { TrainerSlot } from "#app/data/trainer-config";
import { PokeballType } from "#app/data/pokeball";
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:absoluteAvarice";
/**
* Absolute Avarice encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3805 | GitHub Issue #3805}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.ABSOLUTE_AVARICE)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn
.withIntroSpriteConfigs([
{
// This sprite has the shadow
spriteKey: "",
fileRoot: "",
species: Species.GREEDENT,
hasShadow: true,
alpha: 0.001,
repeat: true,
x: -5
},
{
spriteKey: "",
fileRoot: "",
species: Species.GREEDENT,
hasShadow: false,
repeat: true,
x: -5
},
{
spriteKey: "lum_berry",
fileRoot: "items",
isItem: true,
x: 7,
y: -14,
hidden: true,
disableAnimation: true
},
{
spriteKey: "salac_berry",
fileRoot: "items",
isItem: true,
x: 2,
y: 4,
hidden: true,
disableAnimation: true
},
{
spriteKey: "lansat_berry",
fileRoot: "items",
isItem: true,
x: 32,
y: 5,
hidden: true,
disableAnimation: true
},
{
spriteKey: "liechi_berry",
fileRoot: "items",
isItem: true,
x: 6,
y: -5,
hidden: true,
disableAnimation: true
},
{
spriteKey: "sitrus_berry",
fileRoot: "items",
isItem: true,
x: 7,
y: 8,
hidden: true,
disableAnimation: true
},
{
spriteKey: "enigma_berry",
fileRoot: "items",
isItem: true,
x: 26,
y: -4,
hidden: true,
disableAnimation: true
},
{
spriteKey: "leppa_berry",
fileRoot: "items",
isItem: true,
x: 16,
y: -27,
hidden: true,
disableAnimation: true
},
{
spriteKey: "petaya_berry",
fileRoot: "items",
isItem: true,
x: 30,
y: -17,
hidden: true,
disableAnimation: true
},
{
spriteKey: "ganlon_berry",
fileRoot: "items",
isItem: true,
x: 16,
y: -11,
hidden: true,
disableAnimation: true
},
{
spriteKey: "apicot_berry",
fileRoot: "items",
isItem: true,
x: 14,
y: -2,
hidden: true,
disableAnimation: true
},
{
spriteKey: "starf_berry",
fileRoot: "items",
isItem: true,
x: 18,
y: 9,
hidden: true,
disableAnimation: true
},
])
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withOnVisualsStart((scene: BattleScene) => {
doGreedentSpriteSteal(scene);
doBerrySpritePile(scene);
return true;
})
.withIntroDialogue([
{
text: `${namespace}.intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
scene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
scene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
// Get all player berry items, remove from party, and store reference
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
// Sort berries by party member ID to more easily re-add later if necessary
const berryItemsMap = new Map<number, BerryModifier[]>();
scene.getParty().forEach(pokemon => {
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
if (pokemonBerries?.length > 0) {
berryItemsMap.set(pokemon.id, pokemonBerries);
}
});
encounter.misc = { berryItemsMap };
// Generates copies of the stolen berries to put on the Greedent
const bossModifierConfigs: HeldModifierConfig[] = [];
berryItems.forEach(berryMod => {
// Can't define stack count on a ModifierType, have to just create separate instances for each stack
// Overflow berries will be "lost" on the boss, but it's un-catchable anyway
for (let i = 0; i < berryMod.stackCount; i++) {
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [berryMod.berryType]) as PokemonHeldItemModifierType;
bossModifierConfigs.push({ modifier: modifierType });
}
scene.removeModifier(berryMod);
});
// Calculate boss mon
const config: EnemyPartyConfig = {
levelAdditiveMultiplier: 1,
pokemonConfigs: [
{
species: getPokemonSpecies(Species.GREEDENT),
isBoss: true,
bossSegments: 3,
moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF],
modifierConfigs: bossModifierConfigs,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1));
}
}
],
};
encounter.enemyPartyConfigs = [config];
encounter.setDialogueToken("greedentName", getPokemonSpecies(Species.GREEDENT).getName());
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
// Provides 1x Reviver Seed to each party member at end of battle
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED);
const givePartyPokemonReviverSeeds = () => {
const party = scene.getParty();
party.forEach(p => {
if (revSeed) {
const seedModifier = revSeed.newModifier(p);
if (seedModifier) {
encounter.setDialogueToken("foodReward", seedModifier.type.name);
}
scene.addModifier(seedModifier, false, false, false, true);
}
});
queueEncounterMessage(scene, `${namespace}.option.1.food_stash`);
};
setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds);
encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
move: new PokemonMove(Moves.STUFF_CHEEKS),
ignorePp: true
});
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const berryMap = encounter.misc.berryItemsMap;
// Returns 2/5 of the berries stolen from each Pokemon
const party = scene.getParty();
party.forEach(pokemon => {
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
const berryTypesAsArray: BerryType[] = [];
stolenBerries?.forEach(bMod => berryTypesAsArray.push(...new Array(bMod.stackCount).fill(bMod.berryType)));
const returnedBerryCount = Math.floor((berryTypesAsArray.length ?? 0) * 2 / 5);
if (returnedBerryCount > 0) {
for (let i = 0; i < returnedBerryCount; i++) {
// Shuffle remaining berry types and pop
Phaser.Math.RND.shuffle(berryTypesAsArray);
const randBerryType = berryTypesAsArray.pop();
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [randBerryType]) as BerryModifierType;
applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType);
}
}
});
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Animate berries being eaten
doGreedentEatBerries(scene);
doBerrySpritePile(scene, true);
return true;
})
.withOptionPhase(async (scene: BattleScene) => {
// Let it have the food
// Greedent joins the team, level equal to 2 below highest party member
const level = getHighestLevelPlayerPokemon(scene).level - 2;
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false);
greedent.moveset = [new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF)];
greedent.passive = true;
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.build();
function doGreedentSpriteSteal(scene: BattleScene) {
const shakeDelay = 50;
const slideDelay = 500;
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
scene.playSound("battle_anims/Follow Me");
scene.tweens.chain({
targets: greedentSprites,
tweens: [
{ // Slide Greedent diagonally
duration: slideDelay,
ease: "Cubic.easeOut",
y: "+=75",
x: "-=65",
scale: 1.1
},
{ // Shake
duration: shakeDelay,
ease: "Cubic.easeOut",
yoyo: true,
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
},
{ // Shake
duration: shakeDelay,
ease: "Cubic.easeOut",
yoyo: true,
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
},
{ // Shake
duration: shakeDelay,
ease: "Cubic.easeOut",
yoyo: true,
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
},
{ // Shake
duration: shakeDelay,
ease: "Cubic.easeOut",
yoyo: true,
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
},
{ // Shake
duration: shakeDelay,
ease: "Cubic.easeOut",
yoyo: true,
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
},
{ // Shake
duration: shakeDelay,
ease: "Cubic.easeOut",
yoyo: true,
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
},
{ // Slide Greedent diagonally
duration: slideDelay,
ease: "Cubic.easeOut",
y: "-=75",
x: "+=65",
scale: 1
},
{ // Bounce at the end
duration: 300,
ease: "Cubic.easeOut",
yoyo: true,
y: "-=20",
loop: 1,
}
]
});
}
function doGreedentEatBerries(scene: BattleScene) {
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
let index = 1;
scene.tweens.add({
targets: greedentSprites,
duration: 150,
ease: "Cubic.easeOut",
yoyo: true,
y: "-=8",
loop: 5,
onStart: () => {
scene.playSound("battle_anims/PRSFX- Bug Bite");
},
onLoop: () => {
if (index % 2 === 0) {
scene.playSound("battle_anims/PRSFX- Bug Bite");
}
index++;
}
});
}
/**
*
* @param scene
* @param isEat Default false. Will "create" pile when false, and remove pile when true.
*/
function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
const berryAddDelay = 150;
let animationOrder = ["starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa"];
if (isEat) {
animationOrder = animationOrder.reverse();
}
const encounter = scene.currentBattle.mysteryEncounter!;
animationOrder.forEach((berry, i) => {
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
const sprites = encounter.introVisuals?.getSpriteAtIndex(introVisualsIndex);
if (sprites) {
sprite = sprites[0];
tintSprite = sprites[1];
}
scene.time.delayedCall(berryAddDelay * i + 400, () => {
if (sprite) {
sprite.setVisible(!isEat);
}
if (tintSprite) {
tintSprite.setVisible(!isEat);
}
// Animate Petaya berry falling off the pile
if (berry === "petaya" && sprite && tintSprite && !isEat) {
scene.time.delayedCall(200, () => {
doBerryBounce(scene, [sprite, tintSprite], 30, 500);
});
}
});
});
}
function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: number) {
let bouncePower = 1;
let bounceYOffset = yd;
const doBounce = () => {
scene.tweens.add({
targets: berrySprites,
y: "+=" + bounceYOffset,
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
duration: bouncePower * baseBounceDuration,
ease: "Cubic.easeIn",
onComplete: () => {
bouncePower = bouncePower > 0.01 ? bouncePower * 0.5 : 0;
if (bouncePower) {
bounceYOffset = bounceYOffset * bouncePower;
scene.tweens.add({
targets: berrySprites,
y: "-=" + bounceYOffset,
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
duration: bouncePower * baseBounceDuration,
ease: "Cubic.easeOut",
onComplete: () => doBounce()
});
}
}
});
};
doBounce();
}

View File

@ -0,0 +1,165 @@
import { leaveEncounterWithoutBattle, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "../mystery-encounter-requirements";
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:offerYouCantRefuse";
/**
* An Offer You Can't Refuse encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3808 | GitHub Issue #3808}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
.withIntroSpriteConfigs([
{
spriteKey: Species.LIEPARD.toString(),
fileRoot: "pokemon",
hasShadow: true,
repeat: true,
x: 0,
y: -4,
yShadow: -4
},
{
spriteKey: "rich_kid_m",
fileRoot: "trainer",
hasShadow: true,
x: 2,
y: 5,
yShadow: 5
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
},
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, false);
const price = scene.getWaveMoneyAmount(10);
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString());
// Store pokemon and price
encounter.misc = {
pokemon: pokemon,
price: price
};
// If player meets the combo OR requirements for option 2, populate the token
const opt2Req = encounter.options[1].primaryPokemonRequirements[0];
if (opt2Req.meetsRequirement(scene)) {
const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"];
const moveToken = encounter.dialogueTokens["option2PrimaryMove"];
if (abilityToken) {
encounter.setDialogueToken("moveOrAbility", abilityToken);
} else if (moveToken) {
encounter.setDialogueToken("moveOrAbility", moveToken);
}
}
encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
speaker: `${namespace}.speaker`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Update money and remove pokemon from party
updatePlayerMoney(scene, encounter.misc.price);
scene.removePokemonFromPlayerParty(encounter.misc.pokemon);
return true;
})
.withOptionPhase(async (scene: BattleScene) => {
// Give the player a Shiny charm
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.SHINY_CHARM));
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement(
new MoveRequirement(EXTORTION_MOVES),
new AbilityRequirement(EXTORTION_ABILITIES))
)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.tooltip_disabled`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected`,
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
// Extort the rich kid for money
const encounter = scene.currentBattle.mysteryEncounter!;
// Update money and remove pokemon from party
updatePlayerMoney(scene, encounter.misc.price);
setEncounterExp(scene, encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true);
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.3.selected`,
},
],
},
async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true);
return true;
}
)
.build();

View File

@ -0,0 +1,273 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import {
EnemyPartyConfig, generateModifierType, generateModifierTypeOption,
initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, setEncounterExp,
setEncounterRewards
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import {
BerryModifierType,
getPartyLuckValue,
ModifierPoolType,
ModifierTypeOption, modifierTypes,
regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type";
import { randSeedInt } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { getPokemonNameWithAffix } from "#app/messages";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { TrainerSlot } from "#app/data/trainer-config";
import { applyModifierTypeToPlayerPokemon, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonData from "#app/system/pokemon-data";
import { BerryModifier } from "#app/modifier/modifier";
import i18next from "#app/plugins/i18n";
import { BerryType } from "#enums/berry-type";
import { PERMANENT_STATS, Stat } from "#enums/stat";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:berriesAbound";
/**
* Berries Abound encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3810 | GitHub Issue #3810}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const BerriesAboundEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.BERRIES_ABOUND)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Calculate boss mon
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
const config: EnemyPartyConfig = {
levelAdditiveMultiplier: 1,
pokemonConfigs: [{
level: level,
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true
}],
};
encounter.enemyPartyConfigs = [config];
// Calculate the number of extra berries that player receives
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
const numBerries =
scene.currentBattle.waveIndex > 160 ? 7
: scene.currentBattle.waveIndex > 120 ? 5
: scene.currentBattle.waveIndex > 40 ? 4 : 2;
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
encounter.misc = { numBerries };
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
encounter.spriteConfigs = [
{
spriteKey: "berry_bush",
fileRoot: "mystery-encounters",
x: 25,
y: -6,
yShadow: -7,
disableAnimation: true,
hasShadow: true
},
{
spriteKey: spriteKey,
fileRoot: fileRoot,
hasShadow: true,
tint: 0.25,
x: -5,
repeat: true,
isPokemon: true
}
];
// Get fastest party pokemon for option 2
const fastestPokemon = getHighestStatPlayerPokemon(scene, PERMANENT_STATS[Stat.SPD], true);
encounter.misc.fastestPokemon = fastestPokemon;
encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD);
encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender());
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
},
],
},
async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
const numBerries = encounter.misc.numBerries;
const doBerryRewards = async () => {
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
// Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) {
await tryGiveBerry(scene);
}
};
const shopOptions: ModifierTypeOption[] = [];
for (let i = 0; i < 5; i++) {
// Generate shop berries
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY);
if (mod) {
shopOptions.push(mod);
}
}
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
}
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`
})
.withOptionPhase(async (scene: BattleScene) => {
// Pick race for berries
const encounter = scene.currentBattle.mysteryEncounter!;
const fastestPokemon = encounter.misc.fastestPokemon;
const enemySpeed = encounter.misc.enemySpeed;
const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
const numBerries = encounter.misc.numBerries;
const shopOptions: ModifierTypeOption[] = [];
for (let i = 0; i < 5; i++) {
// Generate shop berries
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY);
if (mod) {
shopOptions.push(mod);
}
}
if (speedDiff < 1) {
// Caught and attacked by boss, gets +1 to all stats at start of fight
const doBerryRewards = async () => {
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
// Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) {
await tryGiveBerry(scene);
}
};
const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1));
};
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await showEncounterText(scene, `${namespace}.option.2.selected_bad`);
await initBattleWithEnemyConfig(scene, config);
return;
} else {
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1)/0.08), numBerries), 2);
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
const doFasterBerryRewards = async () => {
const berryText = numBerriesGrabbed + " " + i18next.t(`${namespace}.berries`);
scene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
// Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first)
for (let i = 0; i < numBerriesGrabbed; i++) {
await tryGiveBerry(scene, fastestPokemon);
}
};
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
await showEncounterText(scene, `${namespace}.option.2.selected`);
leaveEncounterWithoutBattle(scene);
}
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
],
},
async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true);
return true;
}
)
.build();
async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
const party = scene.getParty();
// Will try to apply to prioritized pokemon first, then do normal application method if it fails
if (prioritizedPokemon) {
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
await applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
return;
}
}
// Iterate over the party until berry was successfully given
for (const pokemon of party) {
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
await applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
return;
}
}
}

View File

@ -0,0 +1,670 @@
import {
EnemyPartyConfig, generateModifierType,
generateModifierTypeOption,
initBattleWithEnemyConfig,
leaveEncounterWithoutBattle,
selectOptionThenPokemon,
selectPokemonForOption,
setEncounterRewards,
transitionMysteryEncounterIntroVisuals,
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import {
trainerConfigs,
TrainerPartyCompoundTemplate,
TrainerPartyTemplate,
TrainerSlot,
} from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene";
import * as Utils from "#app/utils";
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { Moves } from "#enums/moves";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import {
AttackTypeBoosterHeldItemTypeRequirement,
CombinationPokemonRequirement,
HeldItemRequirement,
TypeRequirement
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { Type } from "#app/data/type";
import { AttackTypeBoosterModifierType, ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
import {
AttackTypeBoosterModifier,
BypassSpeedChanceModifier,
ContactHeldItemTransferChanceModifier,
PokemonHeldItemModifier
} from "#app/modifier/modifier";
import i18next from "i18next";
import MoveInfoOverlay from "#app/ui/move-info-overlay";
import { allMoves } from "#app/data/move";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:bugTypeSuperfan";
const POOL_1_POKEMON = [
Species.PARASECT,
Species.VENOMOTH,
Species.LEDIAN,
Species.ARIADOS,
Species.YANMA,
Species.BEAUTIFLY,
Species.DUSTOX,
Species.MASQUERAIN,
Species.NINJASK,
Species.VOLBEAT,
Species.ILLUMISE,
Species.ANORITH,
Species.KRICKETUNE,
Species.WORMADAM,
Species.MOTHIM,
Species.SKORUPI,
Species.JOLTIK,
Species.LARVESTA,
Species.VIVILLON,
Species.CHARJABUG,
Species.RIBOMBEE,
Species.SPIDOPS,
Species.LOKIX
];
const POOL_2_POKEMON = [
Species.SCYTHER,
Species.PINSIR,
Species.HERACROSS,
Species.FORRETRESS,
Species.SCIZOR,
Species.SHUCKLE,
Species.SHEDINJA,
Species.ARMALDO,
Species.VESPIQUEN,
Species.DRAPION,
Species.YANMEGA,
Species.LEAVANNY,
Species.SCOLIPEDE,
Species.CRUSTLE,
Species.ESCAVALIER,
Species.ACCELGOR,
Species.GALVANTULA,
Species.VIKAVOLT,
Species.ARAQUANID,
Species.ORBEETLE,
Species.CENTISKORCH,
Species.FROSMOTH,
Species.KLEAVOR,
];
const POOL_3_POKEMON: { species: Species, formIndex?: number }[] = [
{
species: Species.PINSIR,
formIndex: 1
},
{
species: Species.SCIZOR,
formIndex: 1
},
{
species: Species.HERACROSS,
formIndex: 1
},
{
species: Species.ORBEETLE,
formIndex: 1
},
{
species: Species.CENTISKORCH,
formIndex: 1
},
{
species: Species.DURANT,
},
{
species: Species.VOLCARONA,
},
{
species: Species.GOLISOPOD,
},
];
const POOL_4_POKEMON = [
Species.GENESECT,
Species.SLITHER_WING,
Species.BUZZWOLE,
Species.PHEROMOSA
];
const PHYSICAL_TUTOR_MOVES = [
Moves.MEGAHORN,
Moves.X_SCISSOR,
Moves.ATTACK_ORDER,
Moves.PIN_MISSILE,
Moves.FIRST_IMPRESSION
];
const SPECIAL_TUTOR_MOVES = [
Moves.SILVER_WIND,
Moves.BUG_BUZZ,
Moves.SIGNAL_BEAM,
Moves.POLLEN_PUFF
];
const STATUS_TUTOR_MOVES = [
Moves.STRING_SHOT,
Moves.STICKY_WEB,
Moves.SILK_TRAP,
Moves.RAGE_POWDER,
Moves.HEAL_ORDER
];
const MISC_TUTOR_MOVES = [
Moves.BUG_BITE,
Moves.LEECH_LIFE,
Moves.DEFEND_ORDER,
Moves.QUIVER_DANCE,
Moves.TAIL_GLOW,
Moves.INFESTATION,
Moves.U_TURN
];
/**
* Bug Type Superfan encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3810 | GitHub Issue #3810}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const BugTypeSuperfanEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.BUG_TYPE_SUPERFAN)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement(
// Must have at least 1 Bug type on team, OR have a bug item somewhere on the team
new HeldItemRequirement(["BypassSpeedChanceModifier", "ContactHeldItemTransferChanceModifier"], 1),
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1),
new TypeRequirement(Type.BUG, false, 1)
))
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([]) // These are set in onInit()
.withAutoHideIntroVisuals(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
},
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Calculates what trainers are available for battle in the encounter
// Bug type superfan trainer config
const config = getTrainerConfigForWave(scene.currentBattle.waveIndex);
const spriteKey = config.getSpriteKey();
encounter.enemyPartyConfigs.push({
trainerConfig: config,
female: true,
});
encounter.spriteConfigs = [
{
spriteKey: spriteKey,
fileRoot: "trainer",
hasShadow: true,
},
];
const requiredItems = [
generateModifierType(scene, modifierTypes.QUICK_CLAW),
generateModifierType(scene, modifierTypes.GRIP_CLAW),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.BUG]),
];
const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/");
encounter.setDialogueToken("requiredBugItems", requiredItemString);
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.1.selected`,
},
],
},
async (scene: BattleScene) => {
// Select battle the bug trainer
const encounter = scene.currentBattle.mysteryEncounter!;
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
// Init the moves available for tutor
const moveTutorOptions: PokemonMove[] = [];
moveTutorOptions.push(new PokemonMove(PHYSICAL_TUTOR_MOVES[randSeedInt(PHYSICAL_TUTOR_MOVES.length)]));
moveTutorOptions.push(new PokemonMove(SPECIAL_TUTOR_MOVES[randSeedInt(SPECIAL_TUTOR_MOVES.length)]));
moveTutorOptions.push(new PokemonMove(STATUS_TUTOR_MOVES[randSeedInt(STATUS_TUTOR_MOVES.length)]));
moveTutorOptions.push(new PokemonMove(MISC_TUTOR_MOVES[randSeedInt(MISC_TUTOR_MOVES.length)]));
encounter.misc = {
moveTutorOptions
};
// Assigns callback that teaches move before continuing to rewards
encounter.onRewards = doBugTypeMoveTutor;
setEncounterRewards(scene, { fillRemaining: true });
await transitionMysteryEncounterIntroVisuals(scene, true, true);
await initBattleWithEnemyConfig(scene, config);
}
)
.withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new TypeRequirement(Type.BUG, false, 1)) // Must have 1 Bug type on team
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Player shows off their bug types
const encounter = scene.currentBattle.mysteryEncounter!;
// Player gets different rewards depending on the number of bug types they have
const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length;
encounter.setDialogueToken("numBugTypes", numBugTypes.toString());
if (numBugTypes < 2) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_0_to_1`,
},
];
} else if (numBugTypes < 4) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_2_to_3`,
},
];
} else if (numBugTypes < 6) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_4_to_5`,
},
];
} else {
// If player has any evolution/form change items that are valid for their party, will spawn one of those items in addition to a Master Ball
const modifierOptions: ModifierTypeOption[] = [generateModifierTypeOption(scene, modifierTypes.MASTER_BALL)!, generateModifierTypeOption(scene, modifierTypes.MAX_LURE)!];
const specialOptions: ModifierTypeOption[] = [];
const nonRareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.EVOLUTION_ITEM);
if (nonRareEvolutionModifier) {
specialOptions.push(nonRareEvolutionModifier);
}
const rareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.RARE_EVOLUTION_ITEM);
if (rareEvolutionModifier) {
specialOptions.push(rareEvolutionModifier);
}
const formChangeModifier = generateModifierTypeOption(scene, modifierTypes.FORM_CHANGE_ITEM);
if (formChangeModifier) {
specialOptions.push(formChangeModifier);
}
if (specialOptions.length > 0) {
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
}
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected_6`,
},
];
}
})
.withOptionPhase(async (scene: BattleScene) => {
// Player shows off their bug types
leaveEncounterWithoutBattle(scene);
})
.build())
.withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement(
// Meets one or both of the below reqs
new HeldItemRequirement(["BypassSpeedChanceModifier", "ContactHeldItemTransferChanceModifier"], 1),
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1)
))
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.3.selected_dialogue`,
},
],
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter(item => {
return item instanceof BypassSpeedChanceModifier ||
item instanceof ContactHeldItemTransferChanceModifier ||
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
});
return validItems.map((modifier: PokemonHeldItemModifier) => {
const option: OptionSelectItem = {
label: modifier.type.name,
handler: () => {
// Pokemon and item selected
encounter.setDialogueToken("selectedItem", modifier.type.name);
encounter.misc = {
chosenPokemon: pokemon,
chosenModifier: modifier,
};
return true;
},
};
return option;
});
};
const selectableFilter = (pokemon: Pokemon) => {
// If pokemon has valid item, it can be selected
const hasValidItem = pokemon.getHeldItems().some(item => {
return item instanceof BypassSpeedChanceModifier ||
item instanceof ContactHeldItemTransferChanceModifier ||
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
});
if (!hasValidItem) {
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null;
}
return null;
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier;
// Remove the modifier if its stacks go to 0
modifier.stackCount -= 1;
if (modifier.stackCount === 0) {
scene.removeModifier(modifier);
}
scene.updateModifiers(true, true);
const bugNet = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!;
bugNet.type.tier = ModifierTier.ROGUE;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [bugNet], guaranteedModifierTypeFuncs: [modifierTypes.REVIVER_SEED], fillRemaining: false });
leaveEncounterWithoutBattle(scene, true);
})
.build())
.withOutroDialogue([
{
text: `${namespace}.outro`,
},
])
.build();
function getTrainerConfigForWave(waveIndex: number) {
// Bug type superfan trainer config
const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone();
config.name = i18next.t("trainerNames:bug_type_superfan");
const pool3Copy = POOL_3_POKEMON.slice(0);
randSeedShuffle(pool3Copy);
const pool3Mon = pool3Copy.pop()!;
if (waveIndex < 30) {
// Use default template (2 AVG)
config
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true));
} else if (waveIndex < 50) {
config
.setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 70) {
config
.setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 100) {
config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true));
} else if (waveIndex < 120) {
config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.generateAndPopulateMoveset();
p.generateName();
}
}));
} else if (waveIndex < 140) {
randSeedShuffle(pool3Copy);
const pool3Mon2 = pool3Copy.pop()!;
config
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon2.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon2.formIndex)) {
p.formIndex = pool3Mon2.formIndex!;
p.generateAndPopulateMoveset();
p.generateName();
}
}));
} else if (waveIndex < 160) {
config
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG)))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true));
} else {
config
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(1, PartyMemberStrength.STRONG)))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BEEDRILL ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BUTTERFREE ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true));
}
return config;
}
function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
let species = Utils.randSeedItem(speciesPool);
if (!ignoreEvolution) {
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength);
}
return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, undefined, postProcess);
};
}
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
return new Promise<void>(async resolve => {
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
await showEncounterDialogue(scene, `${namespace}.battle_won`, `${namespace}.speaker`);
const overlayScale = 1;
const moveInfoOverlay = new MoveInfoOverlay(scene, {
delayVisibility: false,
scale: overlayScale,
onSide: true,
right: true,
x: 1,
y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1,
width: (scene.game.canvas.width / 6) - 2,
});
scene.ui.add(moveInfoOverlay);
const optionSelectItems = moveOptions.map((move: PokemonMove) => {
const option: OptionSelectItem = {
label: move.getName(),
handler: () => {
moveInfoOverlay.active = false;
moveInfoOverlay.setVisible(false);
return true;
},
onHover: () => {
moveInfoOverlay.active = true;
moveInfoOverlay.show(allMoves[move.moveId]);
},
};
return option;
});
const onHoverOverCancel = () => {
moveInfoOverlay.active = false;
moveInfoOverlay.setVisible(false);
};
const result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}.teach_move_prompt`, undefined, onHoverOverCancel);
// let forceExit = !!result;
if (!result) {
moveInfoOverlay.active = false;
moveInfoOverlay.setVisible(false);
}
// TODO: add menu to confirm player doesn't want to teach a move
// while (!result && !forceExit) {
// // Didn't teach a move, ask the player to confirm they don't want to teach a move
// await showEncounterDialogue(scene, `${namespace}.confirm_no_teach`, `${namespace}.speaker`);
// const confirm = await new Promise<boolean>(confirmResolve => {
// scene.ui.setMode(Mode.CONFIRM, () => confirmResolve(true), () => confirmResolve(false));
// });
// scene.ui.clearText();
// await scene.ui.setMode(Mode.MESSAGE);
// if (confirm) {
// // No teach, break out of loop
// forceExit = true;
// } else {
// // Re-show learn menu
// result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}.teach_move_prompt`, undefined, onHoverOverCancel);
// if (!result) {
// moveInfoOverlay.active = false;
// moveInfoOverlay.setVisible(false);
// }
// }
// }
// Option select complete, handle if they are learning a move
if (result && result.selectedOptionIndex < moveOptions.length) {
scene.unshiftPhase(new LearnMovePhase(scene, result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId));
}
// Complete battle and go to rewards
resolve();
});
}

View File

@ -0,0 +1,496 @@
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Abilities } from "#enums/abilities";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Type } from "#app/data/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { randSeedInt, randSeedShuffle } from "#app/utils";
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { Ability } from "#app/data/ability";
import { BerryModifier } from "#app/modifier/modifier";
import { BerryType } from "#enums/berry-type";
import { BattlerIndex } from "#app/battle";
import { Moves } from "#enums/moves";
import { EncounterBattleAnim } from "#app/data/battle-anims";
import { MoveCategory } from "#app/data/move";
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, GameModes } from "#app/game-mode";
import { EncounterAnim } from "#enums/encounter-anims";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:clowningAround";
const RANDOM_ABILITY_POOL = [
Abilities.STURDY,
Abilities.PICKUP,
Abilities.INTIMIDATE,
Abilities.GUTS,
Abilities.DROUGHT,
Abilities.DRIZZLE,
Abilities.SNOW_WARNING,
Abilities.SAND_STREAM,
Abilities.ELECTRIC_SURGE,
Abilities.PSYCHIC_SURGE,
Abilities.GRASSY_SURGE,
Abilities.MISTY_SURGE,
Abilities.MAGICIAN,
Abilities.SHEER_FORCE,
Abilities.PRANKSTER
];
/**
* Clowning Around encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3807 | GitHub Issue #3807}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const ClowningAroundEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.CLOWNING_AROUND)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withDisabledGameModes(GameModes.CHALLENGE)
.withSceneWaveRangeRequirement(80, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withAnimations(EncounterAnim.SMOKESCREEN)
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([
{
spriteKey: Species.MR_MIME.toString(),
fileRoot: "pokemon",
hasShadow: true,
repeat: true,
x: -25,
tint: 0.3,
y: -3,
yShadow: -3
},
{
spriteKey: Species.BLACEPHALON.toString(),
fileRoot: "pokemon/exp",
hasShadow: true,
repeat: true,
x: 25,
tint: 0.3,
y: -3,
yShadow: -3
},
{
spriteKey: "harlequin",
fileRoot: "trainer",
hasShadow: true,
x: 0,
y: 2,
yShadow: 2
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`
},
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const clownTrainerType = TrainerType.HARLEQUIN;
const clownConfig = trainerConfigs[clownTrainerType].clone();
const clownPartyTemplate = new TrainerPartyCompoundTemplate(
new TrainerPartyTemplate(1, PartyMemberStrength.STRONG),
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER));
clownConfig.setPartyTemplates(clownPartyTemplate);
clownConfig.setDoubleOnly();
// @ts-ignore
clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists
// Generate random ability for Blacephalon from pool
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
encounter.setDialogueToken("ability", new Ability(ability, 3).name);
encounter.misc = { ability };
encounter.enemyPartyConfigs.push({
trainerConfig: clownConfig,
pokemonConfigs: [ // Overrides first 2 pokemon to be Mr. Mime and Blacephalon
{
species: getPokemonSpecies(Species.MR_MIME),
isBoss: true,
moveSet: [Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC]
},
{ // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
species: getPokemonSpecies(Species.BLACEPHALON),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ ability: ability, types: [randSeedInt(18), randSeedInt(18)] }),
isBoss: true,
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN]
},
],
doubleBattle: true
});
// Load animations/sfx for start of fight moves
loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]);
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
speaker: `${namespace}.speaker`
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Spawn battle
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards(scene, { fillRemaining: true });
// TODO: when Magic Room and Wonder Room are implemented, add those to start of battle
encounter.startOfBattleEffects.push(
{ // Mr. Mime copies the Blacephalon's random ability
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY_2],
move: new PokemonMove(Moves.ROLE_PLAY),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.TAUNT),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER_2],
move: new PokemonMove(Moves.TAUNT),
ignorePp: true
});
await transitionMysteryEncounterIntroVisuals(scene);
await initBattleWithEnemyConfig(scene, config);
})
.withPostOptionPhase(async (scene: BattleScene): Promise<boolean> => {
// After the battle, offer the player the opportunity to permanently swap ability
const abilityWasSwapped = await handleSwapAbility(scene);
if (abilityWasSwapped) {
await showEncounterText(scene, `${namespace}.option.1.ability_gained`);
}
// Play animations once ability swap is complete
// Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals
scene.tweens.add({
targets: scene.currentBattle.trainer,
x: "+=16",
y: "-=16",
alpha: 0,
ease: "Sine.easeInOut",
duration: 250
});
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2);
return true;
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
speaker: `${namespace}.speaker`
},
{
text: `${namespace}.option.2.selected_2`,
},
{
text: `${namespace}.option.2.selected_3`,
speaker: `${namespace}.speaker`
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Swap player's items on pokemon with the most items
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
// So Vitamins, form change items, etc. are not included
const encounter = scene.currentBattle.mysteryEncounter!;
const party = scene.getParty();
let mostHeldItemsPokemon = party[0];
let count = mostHeldItemsPokemon.getHeldItems()
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
.reduce((v, m) => v + m.stackCount, 0);
party.forEach(pokemon => {
const nextCount = pokemon.getHeldItems()
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
.reduce((v, m) => v + m.stackCount, 0);
if (nextCount > count) {
mostHeldItemsPokemon = pokemon;
count = nextCount;
}
});
encounter.setDialogueToken("switchPokemon", mostHeldItemsPokemon.getNameToRender());
const items = mostHeldItemsPokemon.getHeldItems();
// Shuffles Berries (if they have any)
let numBerries = 0;
items.filter(m => m instanceof BerryModifier)
.forEach(m => {
numBerries += m.stackCount;
scene.removeModifier(m);
});
generateItemsOfTier(scene, mostHeldItemsPokemon, numBerries, "Berries");
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
let numUltra = 0;
let numRogue = 0;
items.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
.forEach(m => {
const type = m.type.withTierFromPool();
const tier = type.tier ?? ModifierTier.ULTRA;
if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
numRogue += m.stackCount;
scene.removeModifier(m);
} else if (type.id === "LUCKY_EGG" || tier === ModifierTier.ULTRA) {
numUltra += m.stackCount;
scene.removeModifier(m);
}
});
generateItemsOfTier(scene, mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
generateItemsOfTier(scene, mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
})
.withOptionPhase(async (scene: BattleScene) => {
leaveEncounterWithoutBattle(scene, true);
})
.withPostOptionPhase(async (scene: BattleScene) => {
// Play animations
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
speaker: `${namespace}.speaker`
},
{
text: `${namespace}.option.3.selected_2`,
},
{
text: `${namespace}.option.3.selected_3`,
speaker: `${namespace}.speaker`
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Randomize the second type of all player's pokemon
// If the pokemon does not normally have a second type, it will gain 1
for (const pokemon of scene.getParty()) {
const originalTypes = pokemon.getTypes(false, false, true);
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
// Makes the "randomness" of the shuffle slightly less punishing
let priorityTypes = pokemon.moveset
.filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS)
.map(move => move!.getMove().type);
if (priorityTypes?.length > 0) {
priorityTypes = [...new Set(priorityTypes)];
randSeedShuffle(priorityTypes);
}
const newTypes = [originalTypes[0]];
let secondType: Type | null = null;
while (secondType === null || secondType === newTypes[0] || originalTypes.includes(secondType)) {
if (priorityTypes.length > 0) {
secondType = priorityTypes.pop() ?? null;
} else {
secondType = randSeedInt(18) as Type;
}
}
newTypes.push(secondType);
if (!pokemon.mysteryEncounterPokemonData) {
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
}
pokemon.mysteryEncounterPokemonData.types = newTypes;
}
})
.withOptionPhase(async (scene: BattleScene) => {
leaveEncounterWithoutBattle(scene, true);
})
.withPostOptionPhase(async (scene: BattleScene) => {
// Play animations
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200);
})
.build()
)
.withOutroDialogue([
{
text: `${namespace}.outro`,
},
])
.build();
async function handleSwapAbility(scene: BattleScene) {
return new Promise<boolean>(async resolve => {
await showEncounterDialogue(scene, `${namespace}.option.1.apply_ability_dialogue`, `${namespace}.speaker`);
await showEncounterText(scene, `${namespace}.option.1.apply_ability_message`);
scene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve);
});
});
}
function displayYesNoOptions(scene: BattleScene, resolve) {
showEncounterText(scene, `${namespace}.option.1.ability_prompt`, null, 500, false);
const fullOptions = [
{
label: i18next.t("menu:yes"),
handler: () => {
onYesAbilitySwap(scene, resolve);
return true;
}
},
{
label: i18next.t("menu:no"),
handler: () => {
resolve(false);
return true;
}
}
];
const config: OptionSelectConfig = {
options: fullOptions,
maxOptions: 7,
yOffset: 0
};
scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true);
}
function onYesAbilitySwap(scene: BattleScene, resolve) {
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Do ability swap
const encounter = scene.currentBattle.mysteryEncounter!;
if (!pokemon.mysteryEncounterPokemonData) {
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
}
pokemon.mysteryEncounterPokemonData.ability = encounter.misc.ability;
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
};
const onPokemonNotSelected = () => {
scene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve);
});
};
selectPokemonForOption(scene, onPokemonSelected, onPokemonNotSelected);
}
function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItems: number, tier: ModifierTier | "Berries") {
// These pools have to be defined at runtime so that modifierTypes exist
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
// This is to prevent "over-generating" a random item of a certain type during item swaps
const ultraPool = [
[modifierTypes.REVIVER_SEED, 1],
[modifierTypes.GOLDEN_PUNCH, 5],
[modifierTypes.ATTACK_TYPE_BOOSTER, 99],
[modifierTypes.QUICK_CLAW, 3],
[modifierTypes.WIDE_LENS, 3]
];
const roguePool = [
[modifierTypes.LEFTOVERS, 4],
[modifierTypes.SHELL_BELL, 4],
[modifierTypes.SOUL_DEW, 10],
[modifierTypes.SOOTHE_BELL, 3],
[modifierTypes.SCOPE_LENS, 1],
[modifierTypes.BATON, 1],
[modifierTypes.FOCUS_BAND, 5],
[modifierTypes.KINGS_ROCK, 3],
[modifierTypes.GRIP_CLAW, 5]
];
const berryPool = [
[BerryType.APICOT, 3],
[BerryType.ENIGMA, 2],
[BerryType.GANLON, 3],
[BerryType.LANSAT, 3],
[BerryType.LEPPA, 2],
[BerryType.LIECHI, 3],
[BerryType.LUM, 2],
[BerryType.PETAYA, 3],
[BerryType.SALAC, 2],
[BerryType.SITRUS, 2],
[BerryType.STARF, 3]
];
let pool: any[];
if (tier === "Berries") {
pool = berryPool;
} else {
pool = tier === ModifierTier.ULTRA ? ultraPool : roguePool;
}
for (let i = 0; i < numItems; i++) {
const randIndex = randSeedInt(pool.length);
const newItemType = pool[randIndex];
let newMod;
if (tier === "Berries") {
newMod = generateModifierType(scene, modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType;
} else {
newMod = generateModifierType(scene, newItemType[0]) as PokemonHeldItemModifierType;
}
applyModifierTypeToPlayerPokemon(scene, pokemon, newMod);
// Decrement max stacks and remove from pool if at max
newItemType[1]--;
if (newItemType[1] <= 0) {
pool.splice(randIndex, 1);
}
}
}

View File

@ -0,0 +1,325 @@
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#enums/moves";
import { TrainerSlot } from "#app/data/trainer-config";
import PokemonData from "#app/system/pokemon-data";
import { Biome } from "#enums/biome";
import { EncounterBattleAnim } from "#app/data/battle-anims";
import { BattlerTagType } from "#enums/battler-tag-type";
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { BattlerIndex } from "#app/battle";
import { catchPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { PokeballType } from "#enums/pokeball";
import { modifierTypes } from "#app/modifier/modifier-type";
import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { EncounterAnim } from "#enums/encounter-anims";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:dancingLessons";
// Fire form
const BAILE_STYLE_BIOMES = [
Biome.VOLCANO,
Biome.BEACH,
Biome.ISLAND,
Biome.WASTELAND,
Biome.MOUNTAIN,
Biome.BADLANDS,
Biome.DESERT
];
// Electric form
const POM_POM_STYLE_BIOMES = [
Biome.CONSTRUCTION_SITE,
Biome.POWER_PLANT,
Biome.FACTORY,
Biome.LABORATORY,
Biome.SLUM,
Biome.METROPOLIS,
Biome.DOJO
];
// Psychic form
const PAU_STYLE_BIOMES = [
Biome.JUNGLE,
Biome.FAIRY_CAVE,
Biome.MEADOW,
Biome.PLAINS,
Biome.GRASS,
Biome.TALL_GRASS,
Biome.FOREST
];
// Ghost form
const SENSU_STYLE_BIOMES = [
Biome.RUINS,
Biome.SWAMP,
Biome.CAVE,
Biome.ABYSS,
Biome.GRAVEYARD,
Biome.LAKE,
Biome.TEMPLE
];
/**
* Dancing Lessons encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3823 | GitHub Issue #3823}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const DancingLessonsEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DANCING_LESSONS)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([]) // Uses a real Pokemon sprite instead of ME Intro Visuals
.withAnimations(EncounterAnim.DANCE)
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withCatchAllowed(true)
.withOnVisualsStart((scene: BattleScene) => {
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]);
danceAnim.play(scene);
return true;
})
.withIntroDialogue([
{
text: `${namespace}.intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const species = getPokemonSpecies(Species.ORICORIO);
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false);
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
if (enemyPokemon.moveset.length < 4) {
enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE));
} else {
enemyPokemon.moveset[0] = new PokemonMove(Moves.REVELATION_DANCE);
}
}
// Set the form index based on the biome
// Defaults to Baile style if somehow nothing matches
const currentBiome = scene.arena.biomeType;
if (BAILE_STYLE_BIOMES.includes(currentBiome)) {
enemyPokemon.formIndex = 0;
} else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) {
enemyPokemon.formIndex = 1;
} else if (PAU_STYLE_BIOMES.includes(currentBiome)) {
enemyPokemon.formIndex = 2;
} else if (SENSU_STYLE_BIOMES.includes(currentBiome)) {
enemyPokemon.formIndex = 3;
} else {
enemyPokemon.formIndex = 0;
}
const oricorioData = new PokemonData(enemyPokemon);
const oricorio = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false, oricorioData);
// Adds a real Pokemon sprite to the field (required for the animation)
scene.getEnemyParty().forEach(enemyPokemon => {
scene.field.remove(enemyPokemon, true);
});
scene.currentBattle.enemyParty = [oricorio];
scene.field.add(oricorio);
// Spawns on offscreen field
oricorio.x -= 300;
encounter.loadAssets.push(oricorio.loadAssets());
const config: EnemyPartyConfig = {
levelAdditiveMultiplier: 1,
pokemonConfigs: [{
species: species,
dataSource: oricorioData,
isBoss: true,
// Gets +1 to all stats except SPD on battle start
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF], 1));
}
}],
};
encounter.enemyPartyConfigs = [config];
encounter.misc = {
oricorioData
};
encounter.setDialogueToken("oricorioName", getPokemonSpecies(Species.ORICORIO).getName());
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.REVELATION_DANCE),
ignorePp: true
});
await hideOricorioPokemon(scene);
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Learn its Dance
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
scene.unshiftPhase(new LearnMovePhase(scene, scene.getParty().indexOf(pokemon), Moves.REVELATION_DANCE));
// Play animation again to "learn" the dance
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon());
danceAnim.play(scene);
};
return selectPokemonForOption(scene, onPokemonSelected);
})
.withOptionPhase(async (scene: BattleScene) => {
// Learn its Dance
hideOricorioPokemon(scene);
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(DANCING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Open menu for selecting pokemon with a Dancing move
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for nature selection
return pokemon.moveset
.filter(move => move && DANCING_MOVES.includes(move.getMove().id))
.map((move: PokemonMove) => {
const option: OptionSelectItem = {
label: move.getName(),
handler: () => {
// Pokemon and second option selected
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("selectedMove", move.getName());
encounter.misc.selectedMove = move;
return true;
},
};
return option;
});
};
// Only Pokemon that have a Dancing move can be selected
const selectableFilter = (pokemon: Pokemon) => {
// If pokemon meets primary pokemon reqs, it can be selected
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
}
return null;
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
})
.withOptionPhase(async (scene: BattleScene) => {
// Show the Oricorio a dance, and recruit it
const encounter = scene.currentBattle.mysteryEncounter!;
const oricorio = encounter.misc.oricorioData.toPokemon(scene);
oricorio.passive = true;
// Ensure the Oricorio's moveset gains the Dance move the player used
const move = encounter.misc.selectedMove?.getMove().id;
if (!oricorio.moveset.some(m => m.getMove().id === move)) {
if (oricorio.moveset.length < 4) {
oricorio.moveset.push(new PokemonMove(move));
} else {
oricorio.moveset[3] = new PokemonMove(move);
}
}
hideOricorioPokemon(scene);
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.build();
function hideOricorioPokemon(scene: BattleScene) {
return new Promise<void>(resolve => {
const oricorioSprite = scene.getEnemyParty()[0];
scene.tweens.add({
targets: oricorioSprite,
x: "+=16",
y: "-=16",
alpha: 0,
ease: "Sine.easeInOut",
duration: 750,
onComplete: () => {
scene.field.remove(oricorioSprite, true);
resolve();
}
});
});
}

View File

@ -0,0 +1,197 @@
import { Type } from "#app/data/type";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene";
import { modifierTypes } from "#app/modifier/modifier-type";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:darkDeal";
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */
const excludedBosses = [
Species.NECROZMA,
Species.COSMOG,
Species.COSMOEM,
Species.SOLGALEO,
Species.LUNALA,
Species.ETERNATUS,
Species.NIHILEGO,
Species.BUZZWOLE,
Species.PHEROMOSA,
Species.XURKITREE,
Species.CELESTEELA,
Species.KARTANA,
Species.GUZZLORD,
Species.POIPOLE,
Species.NAGANADEL,
Species.STAKATAKA,
Species.BLACEPHALON,
Species.GREAT_TUSK,
Species.SCREAM_TAIL,
Species.BRUTE_BONNET,
Species.FLUTTER_MANE,
Species.SLITHER_WING,
Species.SANDY_SHOCKS,
Species.ROARING_MOON,
Species.KORAIDON,
Species.WALKING_WAKE,
Species.GOUGING_FIRE,
Species.RAGING_BOLT,
Species.IRON_TREADS,
Species.IRON_BUNDLE,
Species.IRON_HANDS,
Species.IRON_JUGULIS,
Species.IRON_MOTH,
Species.IRON_THORNS,
Species.IRON_VALIANT,
Species.MIRAIDON,
Species.IRON_LEAVES,
Species.IRON_BOULDER,
Species.IRON_CROWN,
Species.MEW,
Species.CELEBI,
Species.DEOXYS,
Species.JIRACHI,
Species.PHIONE,
Species.MANAPHY,
Species.ARCEUS,
Species.VICTINI,
Species.MELTAN,
Species.PECHARUNT,
];
/**
* Dark Deal encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3806 | GitHub Issue #3806}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const DarkDealEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withIntroSpriteConfigs([
{
spriteKey: "mad_scientist_m",
fileRoot: "mystery-encounters",
hasShadow: true,
},
{
spriteKey: "dark_deal_porygon",
fileRoot: "mystery-encounters",
hasShadow: true,
repeat: true,
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
speaker: `${namespace}.speaker`,
text: `${namespace}.intro_dialogue`,
},
])
.withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
.withCatchAllowed(true)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.1.selected_dialogue`,
},
{
text: `${namespace}.option.1.selected_message`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
// Will never return last battle able mon and instead pick fainted/unable to battle
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
// Get all the pokemon's held items
const modifiers = removedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
scene.removePokemonFromPlayerParty(removedPokemon);
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("pokeName", removedPokemon.getNameToRender());
// Store removed pokemon types
encounter.misc = {
removedTypes: removedPokemon.getTypes(),
modifiers
};
})
.withOptionPhase(async (scene: BattleScene) => {
// Give the player 5 Rogue Balls
const encounter = scene.currentBattle.mysteryEncounter!;
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ROGUE_BALL));
// Start encounter with random legendary (7-10 starter strength) that has level additive
const bossTypes: Type[] = encounter.misc.removedTypes;
const bossModifiers: PokemonHeldItemModifier[] = encounter.misc.modifiers;
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
const roll = randSeedInt(100);
const starterTier: number | [number, number] =
roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10];
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
const pokemonConfig: EnemyPokemonConfig = {
species: bossSpecies,
isBoss: true,
modifierConfigs: bossModifiers.map(m => {
return {
modifier: m
};
})
};
if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) {
pokemonConfig.formIndex = 0;
}
const config: EnemyPartyConfig = {
pokemonConfigs: [pokemonConfig],
};
return initBattleWithEnemyConfig(scene, config);
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
selected: [
{
speaker: `${namespace}.speaker`,
text: `${namespace}.option.2.selected`,
},
],
},
async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true);
return true;
}
)
.withOutroDialogue([
{
text: `${namespace}.outro`
}
])
.build();

View File

@ -0,0 +1,309 @@
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements";
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { HealingBoosterModifier, HiddenAbilityRateBoosterModifier, LevelIncrementBoosterModifier, PokemonHeldItemModifier, PreserveBerryModifier } from "#app/modifier/modifier";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import i18next from "#app/plugins/i18n";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:delibirdy";
/** Berries only */
const OPTION_2_ALLOWED_MODIFIERS = ["BerryModifier", "PokemonInstantReviveModifier"];
/** Disallowed items are berries, Reviver Seeds, and Vitamins (form change items and fusion items are not PokemonHeldItemModifiers) */
const OPTION_3_DISALLOWED_MODIFIERS = [
"BerryModifier",
"PokemonInstantReviveModifier",
"TerastallizeModifier",
"PokemonBaseStatModifier",
"PokemonBaseStatTotalModifier"
];
/**
* Delibird-y encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3804 | GitHub Issue #3804}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const DelibirdyEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(0, 2)) // Must have enough money for it to spawn at the very least
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
))
.withIntroSpriteConfigs([
{
spriteKey: "",
fileRoot: "",
species: Species.DELIBIRD,
hasShadow: true,
repeat: true,
startFrame: 38,
scale: 0.94
},
{
spriteKey: "",
fileRoot: "",
species: Species.DELIBIRD,
hasShadow: true,
repeat: true,
scale: 1.06
},
{
spriteKey: "",
fileRoot: "",
species: Species.DELIBIRD,
hasShadow: true,
repeat: true,
startFrame: 65,
x: 1,
y: 5,
yShadow: 5
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
}
])
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOutroDialogue([
{
text: `${namespace}.outro`,
}
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, 2) // Must have money to spawn
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
return true;
})
.withOptionPhase(async (scene: BattleScene) => {
// Give the player an Ability Charm
// Check if the player has max stacks of that item already
const existing = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
// At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
} else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM));
}
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS))
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
secondOptionPrompt: `${namespace}.option.2.select_prompt`,
selected: [
{
text: `${namespace}.option.2.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => {
return OPTION_2_ALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
});
return validItems.map((modifier: PokemonHeldItemModifier) => {
const option: OptionSelectItem = {
label: modifier.type.name,
handler: () => {
// Pokemon and item selected
encounter.setDialogueToken("chosenItem", modifier.type.name);
encounter.misc = {
chosenPokemon: pokemon,
chosenModifier: modifier,
};
return true;
},
};
return option;
});
};
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
const selectableFilter = (pokemon: Pokemon) => {
// If pokemon meets primary pokemon reqs, it can be selected
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
}
return null;
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier;
// Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed
if (modifier.type.name.includes("Berry")) {
// Check if the player has max stacks of that Candy Jar already
const existing = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
// At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
} else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
}
} else {
// Check if the player has max stacks of that Healing Charm already
const existing = scene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
// At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
} else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
}
}
// Remove the modifier if its stacks go to 0
modifier.stackCount -= 1;
if (modifier.stackCount === 0) {
scene.removeModifier(modifier);
}
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true))
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => {
return !OPTION_3_DISALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
});
return validItems.map((modifier: PokemonHeldItemModifier) => {
const option: OptionSelectItem = {
label: modifier.type.name,
handler: () => {
// Pokemon and item selected
encounter.setDialogueToken("chosenItem", modifier.type.name);
encounter.misc = {
chosenPokemon: pokemon,
chosenModifier: modifier,
};
return true;
},
};
return option;
});
};
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
const selectableFilter = (pokemon: Pokemon) => {
// If pokemon meets primary pokemon reqs, it can be selected
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
}
return null;
};
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier;
// Check if the player has max stacks of Berry Pouch already
const existing = scene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
// At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
} else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
}
// Remove the modifier if its stacks go to 0
modifier.stackCount -= 1;
if (modifier.stackCount === 0) {
scene.removeModifier(modifier);
}
leaveEncounterWithoutBattle(scene, true);
})
.build()
)
.build();

View File

@ -0,0 +1,164 @@
import {
leaveEncounterWithoutBattle,
setEncounterRewards,
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type";
import { randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, {
MysteryEncounterBuilder,
} from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:departmentStoreSale";
/**
* Department Store Sale encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3797 | GitHub Issue #3797}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const DepartmentStoreSaleEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[0], 100)
.withIntroSpriteConfigs([
{
spriteKey: "b2w2_lady",
fileRoot: "mystery-encounters",
hasShadow: true,
x: -20,
},
{
spriteKey: "",
fileRoot: "",
species: Species.FURFROU,
hasShadow: true,
repeat: true,
x: 30,
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
},
])
.withAutoHideIntroVisuals(false)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
},
async (scene: BattleScene) => {
// Choose TMs
const modifiers: ModifierTypeFunc[] = [];
let i = 0;
while (i < 4) {
// 2/2/1 weight on TM rarity
const roll = randSeedInt(5);
if (roll < 2) {
modifiers.push(modifierTypes.TM_COMMON);
} else if (roll < 4) {
modifiers.push(modifierTypes.TM_GREAT);
} else {
modifiers.push(modifierTypes.TM_ULTRA);
}
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene);
}
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
},
async (scene: BattleScene) => {
// Choose Vitamins
const modifiers: ModifierTypeFunc[] = [];
let i = 0;
while (i < 3) {
// 2/1 weight on base stat booster vs PP Up
const roll = randSeedInt(3);
if (roll === 0) {
modifiers.push(modifierTypes.PP_UP);
} else {
modifiers.push(modifierTypes.BASE_STAT_BOOSTER);
}
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene);
}
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
},
async (scene: BattleScene) => {
// Choose X Items
const modifiers: ModifierTypeFunc[] = [];
let i = 0;
while (i < 5) {
// 4/1 weight on base stat booster vs Dire Hit
const roll = randSeedInt(5);
if (roll === 0) {
modifiers.push(modifierTypes.DIRE_HIT);
} else {
modifiers.push(modifierTypes.TEMP_STAT_STAGE_BOOSTER);
}
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene);
}
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.4.label`,
buttonTooltip: `${namespace}.option.4.tooltip`,
},
async (scene: BattleScene) => {
// Choose Pokeballs
const modifiers: ModifierTypeFunc[] = [];
let i = 0;
while (i < 4) {
// 10/30/20/5 weight on pokeballs
const roll = randSeedInt(65);
if (roll < 10) {
modifiers.push(modifierTypes.POKEBALL);
} else if (roll < 40) {
modifiers.push(modifierTypes.GREAT_BALL);
} else if (roll < 60) {
modifiers.push(modifierTypes.ULTRA_BALL);
} else {
modifiers.push(modifierTypes.ROGUE_BALL);
}
i++;
}
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene);
}
)
.withOutroDialogue([
{
text: `${namespace}.outro`,
}
])
.build();

View File

@ -0,0 +1,235 @@
import { MoveCategory } from "#app/data/move";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { modifierTypes } from "#app/modifier/modifier-type";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieldTrip";
/**
* Field Trip encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3794 | GitHub Issue #3794}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const FieldTripEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
spriteKey: "preschooler_m",
fileRoot: "trainer",
hasShadow: true,
},
{
spriteKey: "teacher",
fileRoot: "mystery-encounters",
hasShadow: true,
},
{
spriteKey: "preschooler_f",
fileRoot: "trainer",
hasShadow: true,
},
])
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
{
text: `${namespace}.intro_dialogue`,
speaker: `${namespace}.speaker`,
},
])
.withAutoHideIntroVisuals(false)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => {
const option: OptionSelectItem = {
label: move.getName(),
handler: () => {
// Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.physical`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL);
return true;
},
};
return option;
});
};
return selectPokemonForOption(scene, onPokemonSelected);
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) {
const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
}
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => {
const option: OptionSelectItem = {
label: move.getName(),
handler: () => {
// Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.special`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL);
return true;
},
};
return option;
});
};
return selectPokemonForOption(scene, onPokemonSelected);
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) {
const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
}
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
secondOptionPrompt: `${namespace}.second_option_prompt`,
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => {
const option: OptionSelectItem = {
label: move.getName(),
handler: () => {
// Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}.status`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS);
return true;
},
};
return option;
});
};
return selectPokemonForOption(scene, onPokemonSelected);
})
.withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) {
const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL)!,
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
}
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
})
.build()
)
.build();
function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
const encounter = scene.currentBattle.mysteryEncounter!;
const correctMove = move.getMove().category === correctMoveCategory;
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
encounter.setDialogueToken("move", move.getName());
if (!correctMove) {
encounter.selectedOption!.dialogue!.selected = [
{
text: `${namespace}.option.selected`,
},
{
text: `${namespace}.incorrect`,
speaker: `${namespace}.speaker`,
},
{
text: `${namespace}.incorrect_exp`,
},
];
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
} else {
encounter.selectedOption!.dialogue!.selected = [
{
text: `${namespace}.option.selected`,
},
{
text: `${namespace}.correct`,
speaker: `${namespace}.speaker`,
},
{
text: `${namespace}.correct_exp`,
},
];
setEncounterExp(scene, [pokemon.id], 100);
}
encounter.misc = {
correctMove: correctMove,
};
}

View File

@ -0,0 +1,255 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { TypeRequirement } from "../mystery-encounter-requirements";
import { Species } from "#enums/species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Gender } from "#app/data/gender";
import { Type } from "#app/data/type";
import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon";
import { Moves } from "#enums/moves";
import { EncounterBattleAnim } from "#app/data/battle-anims";
import { WeatherType } from "#app/data/weather";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { StatusEffect } from "#app/data/status-effect";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { EncounterAnim } from "#enums/encounter-anims";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieryFallout";
/**
* Damage percentage taken when suffering the heat.
* Can be a number between `0` - `100`.
* The higher the more damage taken (100% = instant KO).
*/
const DAMAGE_PERCENTAGE: number = 20;
/**
* Fiery Fallout encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3814 | GitHub Issue #3814}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const FieryFalloutEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(40, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withCatchAllowed(true)
.withIntroSpriteConfigs([]) // Set in onInit()
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)
.withAutoHideIntroVisuals(false)
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Calculate boss mons
const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA);
const config: EnemyPartyConfig = {
pokemonConfigs: [
{
species: volcaronaSpecies,
isBoss: false,
gender: Gender.MALE
},
{
species: volcaronaSpecies,
isBoss: false,
gender: Gender.FEMALE
}
],
doubleBattle: true,
disableSwitch: true
};
encounter.enemyPartyConfigs = [config];
// Load hidden Volcarona sprites
encounter.spriteConfigs = [
{
spriteKey: "",
fileRoot: "",
species: Species.VOLCARONA,
repeat: true,
hidden: true,
hasShadow: true,
x: -20,
startFrame: 20
},
{
spriteKey: "",
fileRoot: "",
species: Species.VOLCARONA,
repeat: true,
hidden: true,
hasShadow: true,
x: 20
},
];
// Load animations/sfx for Volcarona moves
loadCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]);
scene.arena.trySetWeather(WeatherType.SUNNY, true);
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName());
return true;
})
.withOnVisualsStart((scene: BattleScene) => {
// Play animations
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
background.playWithoutTargets(scene, 200, 70, 2, 3);
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
animation.playWithoutTargets(scene, 80, 100, 2);
scene.time.delayedCall(600, () => {
animation.playWithoutTargets(scene, -20, 100, 2);
});
scene.time.delayedCall(1200, () => {
animation.playWithoutTargets(scene, 140, 150, 2);
});
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
},
],
},
async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { fillRemaining: true }, undefined, () => giveLeadPokemonCharcoal(scene));
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER_2],
move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
move: new PokemonMove(Moves.QUIVER_DANCE),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.ENEMY_2],
move: new PokemonMove(Moves.QUIVER_DANCE),
ignorePp: true
});
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
}
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`,
},
],
},
async (scene: BattleScene) => {
// Damage non-fire types and burn 1 random non-fire type member
const encounter = scene.currentBattle.mysteryEncounter!;
const nonFireTypes = scene.getParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
for (const pkm of nonFireTypes) {
const percentage = DAMAGE_PERCENTAGE / 100;
const damage = Math.floor(pkm.getMaxHp() * percentage);
applyDamageToPokemon(scene, pkm, damage);
}
// Burn random member
const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status!.effect) || p.status?.effect === StatusEffect.BURN);
if (burnable?.length > 0) {
const roll = randSeedInt(burnable.length);
const chosenPokemon = burnable[roll];
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
// Burn applied
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
queueEncounterMessage(scene, `${namespace}.option.2.target_burned`);
}
}
// No rewards
leaveEncounterWithoutBattle(scene, true);
}
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3PrimaryName dialogue token automatically
.withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3SecondaryName dialogue token automatically
.withDialogue({
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000);
})
.withOptionPhase(async (scene: BattleScene) => {
// Fire types help calm the Volcarona
const encounter = scene.currentBattle.mysteryEncounter!;
transitionMysteryEncounterIntroVisuals(scene);
setEncounterRewards(scene,
{ fillRemaining: true },
undefined,
() => {
giveLeadPokemonCharcoal(scene);
});
const primary = encounter.options[2].primaryPokemon!;
const secondary = encounter.options[2].secondaryPokemon![0];
setEncounterExp(scene, [primary.id, secondary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
leaveEncounterWithoutBattle(scene);
})
.build()
)
.build();
function giveLeadPokemonCharcoal(scene: BattleScene) {
// Give first party pokemon Charcoal for free at end of battle
const leadPokemon = scene.getParty()?.[0];
if (leadPokemon) {
const charcoal = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]) as AttackTypeBoosterModifierType;
applyModifierTypeToPlayerPokemon(scene, leadPokemon, charcoal);
scene.currentBattle.mysteryEncounter!.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
queueEncounterMessage(scene, `${namespace}.found_charcoal`);
}
}

View File

@ -0,0 +1,186 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import {
EnemyPartyConfig,
initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, setEncounterExp,
setEncounterRewards
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
import { ModifierTier } from "#app/modifier/modifier-tier";
import {
getPartyLuckValue,
getPlayerModifierTypeOptions,
ModifierPoolType,
ModifierTypeOption,
regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
import { MoveRequirement } from "../mystery-encounter-requirements";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { TrainerSlot } from "#app/data/trainer-config";
import { getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonData from "#app/system/pokemon-data";
import { BattlerTagType } from "#enums/battler-tag-type";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { randSeedInt } from "#app/utils";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fightOrFlight";
/**
* Fight or Flight encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3795 | GitHub Issue #3795}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const FightOrFlightEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIGHT_OR_FLIGHT)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}.intro`,
},
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Calculate boss mon
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
const config: EnemyPartyConfig = {
levelAdditiveMultiplier: 1,
pokemonConfigs: [{
level: level,
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`);
// Randomly boost 1 stat 2 stages
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(5)], 2));
}
}],
};
encounter.enemyPartyConfigs = [config];
// Calculate item
// Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
const tier =
scene.currentBattle.waveIndex > 160
? ModifierTier.MASTER
: scene.currentBattle.waveIndex > 120
? ModifierTier.ROGUE
: scene.currentBattle.waveIndex > 40
? ModifierTier.ULTRA
: ModifierTier.GREAT;
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
let item: ModifierTypeOption | null = null;
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
}
encounter.setDialogueToken("itemName", item.type.name);
encounter.misc = item;
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
encounter.spriteConfigs = [
{
spriteKey: item.type.iconImage,
fileRoot: "items",
hasShadow: false,
x: 35,
y: -5,
scale: 0.75,
isItem: true,
disableAnimation: true
},
{
spriteKey: spriteKey,
fileRoot: fileRoot,
hasShadow: true,
tint: 0.25,
x: -5,
repeat: true,
isPokemon: true
},
];
return true;
})
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
selected: [
{
text: `${namespace}.option.1.selected`,
},
],
},
async (scene: BattleScene) => {
// Pick battle
// Pokemon will randomly boost 1 stat by 2 stages
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
}
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
.withDialogue({
buttonLabel: `${namespace}.option.2.label`,
buttonTooltip: `${namespace}.option.2.tooltip`,
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`,
selected: [
{
text: `${namespace}.option.2.selected`
}
]
})
.withOptionPhase(async (scene: BattleScene) => {
// Pick steal
const encounter = scene.currentBattle.mysteryEncounter!;
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
// Use primaryPokemon to execute the thievery
const primaryPokemon = encounter.options[1].primaryPokemon!;
setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
leaveEncounterWithoutBattle(scene);
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}.option.3.label`,
buttonTooltip: `${namespace}.option.3.tooltip`,
selected: [
{
text: `${namespace}.option.3.selected`,
},
],
},
async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true);
return true;
}
)
.build();

Some files were not shown because too many files have changed in this diff Show More