Compare commits

...

41 Commits

Author SHA1 Message Date
Madmadness65
4cc3bfafd6 Update item atlas 2024-05-31 18:30:36 -05:00
Madmadness65
aa6cc95117 Merge branch 'main' into plates_memories 2024-05-31 18:23:20 -05:00
Benjamin Odom
19885e0558
Add Arena Events (#1627)
* Add Arena Events

* Update arena-events.ts
2024-05-31 17:52:16 -05:00
karl-police
f2a6fe5c6a
Autocomplete for modifiers in overrides (#1057)
* Autocomplete for modifiers

* Update overrides.ts

---------

Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
2024-05-31 17:41:09 -05:00
chaosgrimmon
218cf025a3
[Bug] Fix rare shiny Cleffa using default icon (#1642)
* [Bug] Fix rare shiny Cleffa using default icon

Rare shiny Cleffa's icon was showing up as the default colouration, or as shiny Lugia's icon.
This fixes that by adding it to the spritesheet pokemon_icons_2v and its .json, where the console log suggested it should belong.

* [Bug] Fix rare shiny Cleffa using default icon

Rare shiny Cleffa's icon was showing up as the default colouration, or as shiny Lugia's icon.
This fixes that by adding it to the spritesheet pokemon_icons_2v and its .json, where the console log suggested it should belong.
2024-05-31 17:15:41 -05:00
Jaime
7f1e94e8ca
[Bug] Fix status abilities not considering Comatose (fixes #1639) (#1641) 2024-05-31 16:50:03 -05:00
Tempoanon
cf9992f9b8
Add code (#1661) 2024-05-31 16:42:38 -05:00
innerthunder
2dff35c407
Fix Neutralizing Gas self-suppression (#1662) 2024-05-31 16:35:52 -05:00
Tempoanon
ff5b227a6b
[Documentation] Add documentation to egg-hatch-phase.ts (#1650)
* Add documentation

* Ben has made me take more sanity damage
2024-05-31 16:34:02 -05:00
Amani H
e0bc1d8781
[Item] Add Toxic Orb and Flame Orb (#1574)
* Add Toxic Orb and Flame Orb

* Change Weighting Logic & Functions

* Adjust Party Filter during Weighting

* Add Dynamic Weight Ceilings

* Refactor for Performance/Cleanliness

* Adjust Party Filter to Check Status Applicability

* Cover Same & Different Status Cases

* Adjust Full Heal & Restore Weighting

* Cover Unwanted Status Case

* Fix Wrong Status Effect on Reload
2024-05-31 14:31:11 -05:00
MadridPawmot
63a416a65b
[enhancement] Added Firebreather trainer class (#1409)
* Add files via upload

* Update trainer-type.ts

* Update biomes.ts

* Update trainer-config.ts

* Update trainer-names.ts

* Add Spanish localization

* Update trainers.ts

* Update trainers.ts

* Update trainers.ts

* Update trainers.ts

* Update trainers.ts

* Update trainers.ts

* Finished adding placeholder strings for each language

* Updated German translation

Thanks to CodeTappert

* Updated trainer.ts to resolve conflicts

* Update trainers.ts to resolve conflicts

* Update trainers.ts

* Fixed syntax error

* Update dialogue.ts

* Re-added dialogue back

* Added firebreather name string to Korean locale

* Added dialogue string to locale

* Added dialogue string to locale

* Update dialogue.ts

* Added dialogue string to locale

* Added dialogue string to locale

* Added dialogue string to locale

* Added dialogue string to locale

* Added dialogue string to locale

* Added requested changes

* Added requested changes

* Added requested changes

* Update dialogue.ts

* Update dialogue.ts

* Update dialogue.ts

* Update dialogue.ts

* Update dialogue.ts

* Finished adding requested changes to locales

* Added requested changes
2024-05-31 14:05:04 -05:00
Madmadness65
99e17c94eb Fix Zacian & Zamazenta form changes breaking
Forgot to update this, oops.
2024-05-31 11:33:16 -05:00
Madmadness65
fc61489d59 Hero Palafin, 10% PC Zygarde cries corrected for real this time
Also made sure Zacian and Zamazenta don't crash this time.
2024-05-31 11:14:55 -05:00
Tempoanon
0c862839d3
Revert "[Bug] Full Heals and Full Restore cure confusion (#1112)" (#1653)
This reverts commit 6c4b60a2fa.
2024-05-31 15:56:39 +01:00
Tempoanon
5eab016903
Revert "[Localization] modifier-select-ui-handler Locales (#1567)" (#1652)
This reverts commit 5c79e6258b.
2024-05-31 10:48:26 -04:00
Lugiad
5c79e6258b
[Localization] modifier-select-ui-handler Locales (#1567)
* Update i18n.ts

* Update modifier-select-ui-handler.ts

* Update config.ts

* Update config.ts

* Update config.ts

* Update config.ts

* Update config.ts

* Update config.ts

* Update config.ts

* Update config.ts

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update modifier-select-ui-handler.ts

* Update config.ts

* Add files via upload

* Update modifier-select-ui-handler.ts

* Update modifier-select-ui-handler.ts

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

Co-authored-by: returntoice <dieandbecome@gmail.com>

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

Co-authored-by: returntoice <dieandbecome@gmail.com>

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

Co-authored-by: returntoice <dieandbecome@gmail.com>

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

Co-authored-by: returntoice <dieandbecome@gmail.com>

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

Co-authored-by: returntoice <dieandbecome@gmail.com>

* Update modifier-select-ui-handler.ts

---------

Co-authored-by: returntoice <dieandbecome@gmail.com>
2024-05-31 10:39:13 -04:00
Lugiad
5182937040
[Localization] Add Korea type icons (#1649)
* Add files via upload

* Update utils.ts
2024-05-31 10:34:26 -04:00
MrWaterT
7b5510b055
[Localization] little edit in Korean locale text (#1636)
* Update tutorial.ts

statChange : 9b5c1cd
starterSelect : fccd546

* Edit a misspell
2024-05-31 10:33:25 -04:00
Ethan
6c4b60a2fa
[Bug] Full Heals and Full Restore cure confusion (#1112)
* Added Confusion to be healed with Full Heals and Full Restores

* Semi-Colon oversight

* Changed resetStatus to have a condition whether to include confusion or not, defaults to false so you manually have to add

* Fixed spacing and semicolon

* Refactored the Lum Berry case

* Fix berry conflicts

* Update {}

* Fix PP Conflict

* Build fix?

* Fix Modifier

* Build Fix

* Fix

* Fix StatuHeal from eslint
2024-05-31 10:31:15 -04:00
Adrian T
bb7af15c4a
[Move][Bug] Fix Enemy Fake Out and Force Switch Interaction (#1251)
* fix force switch move

* add check if switched out pokemon is not fainted with force switch move

* improve code documentation

* refactor if statement
2024-05-31 10:23:28 -04:00
Dakurei
92afcdea8f
Adds workflow to auto generate doc & make it accessible on Github (#1645)
* Adds workflow to auto generate doc & make it accessible on Github

* Adds a condition similar to deploy.yml

  + There's no particular reason to build the documentation for each fork, as it will simply be possible to synchronize the gh-pages branch via the interface or terminal
2024-05-31 09:36:59 -04:00
Adrian T
c102faf157
fix lowkey toxtricity evolution (#1640) 2024-05-31 14:12:01 +01:00
Dakurei
7b65c51931
Revert 2 last commits (#1646) 2024-05-31 13:29:54 +01:00
zaccie
f19cd37436
fix zacian/zamazenta crash (#1638)
* fix zacian/zamazenta crash

A quick fix by renaming Palafins forme change to hero-forme in code

* fixing bad english

i cant spell in my own language
2024-05-31 10:10:31 +02:00
Madmadness65
dfb089a2b9 Fix Hero Palafin and 10% PC Zygarde using wrong cries
Also marked Palafin as able to change forms, but this shouldn't affect it's functionality.
2024-05-31 02:06:40 -05:00
Aerochill
7a7971ec6c
[Ability] Queenly Majesty/Dazzling/Armor Tail Fix (#1631) 2024-05-30 22:37:47 -04:00
Benjamin Odom
378ce95636
[QoL] Add TypeDoc as a Valid Doc Generator (#1609)
* Adds TypeDoc as a Valid Doc Generator

Run `npm install typedoc --save-dev`
then
Run `npx typedoc`

* Rename Revert

* Add Script
2024-05-30 20:05:38 -05:00
Ethan
d2c5a283d1
[Ability] Implement Poison Heal (#1245)
* Implement Poison Heal Ability

* Removed unneeded import

* Fix some comments, as well as make Poison Heal only notify when healing

* Eslint fix

* Revert Phases

* Pushing for sake of reviewing; PR IS NOT DONE IT NEEDS TO BE TESTED AND COMMENTED AGAIN

* Changed the way healing is done, through a heal phase instead of heal(); Also added better documentation

* Changed healing, as well as making abilityTriggers updated
2024-05-30 18:58:40 -04:00
HighMans
af20712cb5
[Feature] Make 31IVs show up as Gold (#1380)
* Make 31IVs show as gold on summary & when caught.

* Add comments to change.
2024-05-30 18:39:25 -04:00
Ronn
3f6e43a12d
Starter select - Implement up and down offset navigation (#1480)
* Handle offset up and down navigation

* add comments
2024-05-30 17:27:53 -05:00
Jannik Tappert
7ac6016df3
[Localization] Fixed Bugsy dialogue. And some fixes in the german dialogue (#1616) 2024-05-30 18:22:28 -04:00
dielle000
bda3013c19
Allow selection of item quantity when transferring items (fixes #723) (#1394)
* Transferring item does not kick out of transfer menu

* Select simultaneously the item to transfer and the quantity

* eslint fix

* eslint fix

* Reset quantity on scroll

* Documentation

* eslint fix
2024-05-30 16:58:10 -05:00
Adrian T
31e3293c51
[Ability][Move] Finish wind rider/power + tailwind implementation (#1566)
* finish wind moves + tailwind implementation

* move code and add documentations

* remove partial tag of wind_power

* add translations, fix move bugs

* fix ability trigger message

* fix ability trigger message

* add es localization

* move out of else statement
2024-05-30 17:42:46 -04:00
Tempoanon
cb6a0b9973
Fix bug with variant rate (#1613) 2024-05-30 22:25:53 +01:00
Ethan
d70ab3eaf6
[Move] Implement Punishment (#1610)
* Implement Punishment and reopen pull request 373

* Fix console log and ()
2024-05-30 17:01:42 -04:00
Gianluca Fuoco
2fd87a3cad
[Bug] Add Giga Impact Opp BG img (#1584)
* fix: add missing image by rotating giga impact image 180 degrees

* fix: move over impact in image
2024-05-30 16:48:06 -04:00
MrWaterT
2c784c662f
[Localization] Update Korean rival dialogue (#1597)
* Update small Korean locales

* Translate Korean classic story conversation

* Minor translation edit in Korean

3: translate 'not the same back home'
  from 'atmosphere of home is not like before'
  to 'returning to previous state is impossible'
3_female: now 'home' clearly means their hometown, not only player's house
6, 6_female: simple nuance-of-sentence modification
2024-05-30 16:40:31 -04:00
Tempoanon
3e932ce2d0
Shinylock uncatchable Pokemon in endless mode (#1252)
* Shinylock uncatchable Pokemon

* Fix typos

* Pass the linter vibe check

* Remove 2 shinylock cases

* refactor
2024-05-30 16:20:27 -04:00
Dmitriy K
329c9619f6
Add unthaw logic to Steam Eruption, Scorching Sands, Matcha Gotcha and all Damaging Fire moves (#940)
* Add unthaw to moves that are missing it

* Add unthaw to all damaging fire moves

* Add Status Effect overrides for easier testing

* clean up comments and readd status cure prefaint

* use helper instead of accessing attrs directly

* remove status overrides
2024-05-30 15:04:50 -05:00
Benjamin Odom
1415b74682
[QoL] Fix Final VS Code Error (#1608)
* Fix i18next and mobile errors

* Move File and Fix Error
2024-05-30 19:37:59 +01:00
Madmadness65
13797fe322 Revert change to Minior forms
Apparently its Meteor forms couldn't be selected if you caught it while it was in its Core form, so the change to this Pokémon specifically had to be reverted. It still immediately transforms back into Meteor form when starting a run, so this change should be harmless.
2024-05-30 13:06:25 -05:00
74 changed files with 3565 additions and 2095 deletions

64
.github/workflows/github-pages.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: "Github Pages"
on:
push:
branches:
- main
jobs:
pages:
name: Github Pages
if: github.repository == 'pagefaultgames/pokerogue'
runs-on: ubuntu-latest
env:
api-dir: ./
strategy:
fail-fast: false
steps:
- name: Checkout repository for Typedoc
uses: actions/checkout@v3
with:
path: pokerogue_docs
- name: Install OS package
run: |
sudo apt update
sudo apt install -y git openssh-client
- name: Setup Node 20.13.1
uses: actions/setup-node@v1
with:
node-version: 20
- name: Checkout repository for Github Pages
uses: actions/checkout@v3
with:
path: pokerogue_gh
ref: gh-pages
- name: Install Node.js dependencies
working-directory: ${{env.api-dir}}
run: |
cd pokerogue_docs
npm ci
npm install typedoc --save-dev
- name: Generate Typedoc docs
working-directory: ${{env.api-dir}}
run: |
cd pokerogue_docs
npx typedoc --out /tmp/docs --githubPages false --entryPoints ./src/
- name: Commit & Push docs
run: |
cd pokerogue_gh
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
mkdir -p $GITHUB_REF_NAME
rm -rf $GITHUB_REF_NAME/*
cp -r /tmp/docs/. $GITHUB_REF_NAME
git add $GITHUB_REF_NAME
git commit --allow-empty -m "[skip ci] Deploy docs"
git push

3
.gitignore vendored
View File

@ -35,4 +35,5 @@ src/data/battle-anim-raw-data*.ts
src/data/battle-anim-data.ts
src/overrides.ts
coverage
coverage
/typedoc

100
package-lock.json generated
View File

@ -31,6 +31,7 @@
"lefthook": "^1.6.12",
"phaser3spectorjs": "^0.0.8",
"pokenode-ts": "^1.20.0",
"typedoc": "^0.25.13",
"typescript": "^5.4.5",
"typescript-eslint": "^7.10.0",
"vite": "^4.5.0",
@ -1663,6 +1664,12 @@
"node": ">=8"
}
},
"node_modules/ansi-sequence-parser": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz",
"integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==",
"dev": true
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@ -4246,6 +4253,12 @@
"node": ">=6"
}
},
"node_modules/jsonc-parser": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
"integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
"dev": true
},
"node_modules/jsonify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
@ -4557,6 +4570,12 @@
"yallist": "^3.0.2"
}
},
"node_modules/lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"node_modules/magic-string": {
"version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
@ -4592,6 +4611,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/marked": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
"integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
"dev": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -5676,6 +5707,18 @@
"node": ">=8"
}
},
"node_modules/shiki": {
"version": "0.14.7",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz",
"integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==",
"dev": true,
"dependencies": {
"ansi-sequence-parser": "^1.1.0",
"jsonc-parser": "^3.2.0",
"vscode-oniguruma": "^1.7.0",
"vscode-textmate": "^8.0.0"
}
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
@ -6151,6 +6194,51 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/typedoc": {
"version": "0.25.13",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz",
"integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==",
"dev": true,
"dependencies": {
"lunr": "^2.3.9",
"marked": "^4.3.0",
"minimatch": "^9.0.3",
"shiki": "^0.14.7"
},
"bin": {
"typedoc": "bin/typedoc"
},
"engines": {
"node": ">= 16"
},
"peerDependencies": {
"typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x"
}
},
"node_modules/typedoc/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/typedoc/node_modules/minimatch": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/typescript": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
@ -7482,6 +7570,18 @@
}
}
},
"node_modules/vscode-oniguruma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
"integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==",
"dev": true
},
"node_modules/vscode-textmate": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz",
"integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==",
"dev": true
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",

View File

@ -12,7 +12,8 @@
"test:cov": "vitest run --coverage",
"test:watch": "vitest watch --coverage",
"eslint": "eslint --fix .",
"eslint-ci": "eslint ."
"eslint-ci": "eslint .",
"docs": "typedoc"
},
"devDependencies": {
"@eslint/js": "^9.3.0",
@ -29,6 +30,7 @@
"lefthook": "^1.6.12",
"phaser3spectorjs": "^0.0.8",
"pokenode-ts": "^1.20.0",
"typedoc": "^0.25.13",
"typescript": "^5.4.5",
"typescript-eslint": "^7.10.0",
"vite": "^4.5.0",

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

View File

@ -1332,6 +1332,27 @@
"h": 24
}
},
{
"filename": "173_2",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 40,
"h": 30
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 18,
"h": 18
},
"frame": {
"x": 174,
"y": 253,
"w": 18,
"h": 18
}
},
{
"filename": "173_3",
"rotated": false,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

440
public/images/types_ko.json Normal file
View File

@ -0,0 +1,440 @@
{
"textures": [
{
"image": "types_ko.png",
"format": "RGBA8888",
"size": {
"w": 32,
"h": 280
},
"scale": 1,
"frames": [
{
"filename": "unknown",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
}
},
{
"filename": "bug",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 14,
"w": 32,
"h": 14
}
},
{
"filename": "dark",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 28,
"w": 32,
"h": 14
}
},
{
"filename": "dragon",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 42,
"w": 32,
"h": 14
}
},
{
"filename": "electric",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 56,
"w": 32,
"h": 14
}
},
{
"filename": "fairy",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 70,
"w": 32,
"h": 14
}
},
{
"filename": "fighting",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 84,
"w": 32,
"h": 14
}
},
{
"filename": "fire",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 98,
"w": 32,
"h": 14
}
},
{
"filename": "flying",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 112,
"w": 32,
"h": 14
}
},
{
"filename": "ghost",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 126,
"w": 32,
"h": 14
}
},
{
"filename": "grass",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 140,
"w": 32,
"h": 14
}
},
{
"filename": "ground",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 154,
"w": 32,
"h": 14
}
},
{
"filename": "ice",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 168,
"w": 32,
"h": 14
}
},
{
"filename": "normal",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 182,
"w": 32,
"h": 14
}
},
{
"filename": "poison",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 196,
"w": 32,
"h": 14
}
},
{
"filename": "psychic",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 210,
"w": 32,
"h": 14
}
},
{
"filename": "rock",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 224,
"w": 32,
"h": 14
}
},
{
"filename": "steel",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 238,
"w": 32,
"h": 14
}
},
{
"filename": "water",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 252,
"w": 32,
"h": 14
}
},
{
"filename": "stellar",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 14
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 14
},
"frame": {
"x": 0,
"y": 266,
"w": 32,
"h": 14
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
}
}

BIN
public/images/types_ko.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1876,7 +1876,20 @@ export default class BattleScene extends SceneBase {
});
}
tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferStack: boolean, playSound: boolean, instant?: boolean, ignoreUpdate?: boolean): Promise<boolean> {
/**
* Try to transfer a held item to another pokemon.
* If the recepient already has the maximum amount allowed for this item, the transfer is cancelled.
* The quantity to transfer is automatically capped at how much the recepient can take before reaching the maximum stack size for the item.
* A transfer that moves a quantity smaller than what is specified in the transferQuantity parameter is still considered successful.
* @param itemModifier {@linkcode PokemonHeldItemModifier} item to transfer (represents the whole stack)
* @param target {@linkcode Pokemon} pokemon recepient in this transfer
* @param playSound {boolean}
* @param transferQuantity {@linkcode integer} how many items of the stack to transfer. Optional, defaults to 1
* @param instant {boolean}
* @param ignoreUpdate {boolean}
* @returns true if the transfer was successful
*/
tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, playSound: boolean, transferQuantity: integer = 1, instant?: boolean, ignoreUpdate?: boolean): Promise<boolean> {
return new Promise(resolve => {
const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null;
const cancelled = new Utils.BooleanHolder(false);
@ -1894,14 +1907,16 @@ export default class BattleScene extends SceneBase {
if (matchingModifier.stackCount >= maxStackCount) {
return resolve(false);
}
const countTaken = transferStack ? Math.min(itemModifier.stackCount, maxStackCount - matchingModifier.stackCount) : 1;
const countTaken = Math.min(transferQuantity, itemModifier.stackCount, maxStackCount - matchingModifier.stackCount);
itemModifier.stackCount -= countTaken;
newItemModifier.stackCount = matchingModifier.stackCount + countTaken;
removeOld = !itemModifier.stackCount;
} else if (!transferStack) {
newItemModifier.stackCount = 1;
removeOld = !(--itemModifier.stackCount);
} else {
const countTaken = Math.min(transferQuantity, itemModifier.stackCount);
itemModifier.stackCount -= countTaken;
newItemModifier.stackCount = countTaken;
}
removeOld = !itemModifier.stackCount;
if (!removeOld || !source || this.removeModifier(itemModifier, !source.isPlayer())) {
const addModifier = () => {
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {

View File

@ -501,7 +501,7 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move.getMove(),attackPriority);
applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move.getMove(), attackPriority);
if (move.getMove().moveTarget===MoveTarget.USER) {
if (move.getMove().moveTarget===MoveTarget.USER || move.getMove().moveTarget===MoveTarget.NEAR_ALLY) {
return false;
}
@ -674,7 +674,10 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
if (!pokemon.getTag(this.tagType)) {
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: pokemon.name, moveName: move.getName() }));
}
return true;
}
return false;
@ -1226,7 +1229,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
const heldItems = this.getTargetHeldItems(defender).filter(i => i.getTransferrable(false));
if (heldItems.length) {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => {
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
if (success) {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${defender.name}'s ${stolenItem.type.name}!`));
}
@ -1315,7 +1318,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.getTransferrable(false));
if (heldItems.length) {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => {
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
if (success) {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${attacker.name}'s ${stolenItem.type.name}!`));
}
@ -2066,6 +2069,37 @@ export class BlockNonDirectDamageAbAttr extends AbAttr {
}
}
/**
* This attribute will block any status damage that you put in the parameter.
*/
export class BlockStatusDamageAbAttr extends BlockNonDirectDamageAbAttr {
private effects: StatusEffect[];
/**
* @param {StatusEffect[]} effects The status effect(s) that will be blocked from damaging the ability pokemon
*/
constructor(...effects: StatusEffect[]) {
super(false);
this.effects = effects;
}
/**
* @param {Pokemon} pokemon The pokemon with the ability
* @param {boolean} passive N/A
* @param {Utils.BooleanHolder} cancelled Whether to cancel the status damage
* @param {any[]} args N/A
* @returns Returns true if status damage is blocked
*/
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.effects.includes(pokemon.status.effect)) {
cancelled.value = true;
return true;
}
return false;
}
}
export class BlockOneHitKOAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
@ -2380,6 +2414,41 @@ export class PostTurnAbAttr extends AbAttr {
}
}
/**
* This attribute will heal 1/8th HP if the ability pokemon has the correct status.
*/
export class PostTurnStatusHealAbAttr extends PostTurnAbAttr {
private effects: StatusEffect[];
/**
* @param {StatusEffect[]} effects The status effect(s) that will qualify healing the ability pokemon
*/
constructor(...effects: StatusEffect[]) {
super(false);
this.effects = effects;
}
/**
* @param {Pokemon} pokemon The pokemon with the ability that will receive the healing
* @param {Boolean} passive N/A
* @param {any[]} args N/A
* @returns Returns true if healed from status, false if not
*/
applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
if (this.effects.includes(pokemon.status.effect)) {
if (pokemon.getMaxHp() !== pokemon.hp) {
const scene = pokemon.scene;
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.getMaxHp() / 8), 1), i18next.t("abilityTriggers:poisonHeal", { pokemonName: pokemon.name, abilityName: abilityName}), true));
return true;
}
}
return false;
}
}
/**
* After the turn ends, resets the status of either the ability holder or their ally
* @param {boolean} allyTarget Whether to target ally, defaults to false (self-target)
@ -2870,7 +2939,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot;
if (postBattleLoot.length) {
const randItem = Utils.randSeedItem(postBattleLoot);
if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false, true, true)) {
if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) {
postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` picked up\n${randItem.type.name}!`));
return true;
@ -3200,6 +3269,50 @@ export class MoneyAbAttr extends PostBattleAbAttr {
}
}
/**
* Applies a stat change after a Pokémon is summoned,
* conditioned on the presence of a specific arena tag.
*
* @extends {PostSummonStatChangeAbAttr}
*/
export class PostSummonStatChangeOnArenaAbAttr extends PostSummonStatChangeAbAttr {
/**
* The type of arena tag that conditions the stat change.
* @private
* @type {ArenaTagType}
*/
private tagType: ArenaTagType;
/**
* Creates an instance of PostSummonStatChangeOnArenaAbAttr.
* Initializes the stat change to increase Attack by 1 stage if the specified arena tag is present.
*
* @param {ArenaTagType} tagType - The type of arena tag to check for.
*/
constructor(tagType: ArenaTagType) {
super([BattleStat.ATK], 1, true, false);
this.tagType = tagType;
}
/**
* Applies the post-summon stat change if the specified arena tag is present on pokemon's side.
* This is used in Wind Rider ability.
*
* @param {Pokemon} pokemon - The Pokémon being summoned.
* @param {boolean} passive - Whether the effect is passive.
* @param {any[]} args - Additional arguments.
* @returns {boolean} - Returns true if the stat change was applied, otherwise false.
*/
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (pokemon.scene.arena.getTagOnSide(this.tagType, side)) {
return super.applyPostSummon(pokemon, passive, args);
}
return false;
}
}
function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any[]): TAttr },
pokemon: Pokemon, applyFunc: AbAttrApplyFunc<TAttr>, args: any[], isAsync: boolean = false, showAbilityInstant: boolean = false, quiet: boolean = false, passive: boolean = false): Promise<void> {
return new Promise(resolve => {
@ -3603,9 +3716,9 @@ export function initAbilities() {
.conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr),
new Ability(Abilities.GUTS, 3)
.attr(BypassBurnDamageReductionAbAttr)
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5),
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5),
new Ability(Abilities.MARVEL_SCALE, 3)
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5)
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5)
.ignorable(),
new Ability(Abilities.LIQUID_OOZE, 3)
.attr(ReverseDrainAbAttr),
@ -3685,7 +3798,8 @@ export function initAbilities() {
new Ability(Abilities.IRON_FIST, 4)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PUNCHING_MOVE), 1.2),
new Ability(Abilities.POISON_HEAL, 4)
.unimplemented(),
.attr(PostTurnStatusHealAbAttr, StatusEffect.TOXIC, StatusEffect.POISON)
.attr(BlockStatusDamageAbAttr, StatusEffect.TOXIC, StatusEffect.POISON),
new Ability(Abilities.ADAPTABILITY, 4)
.attr(StabBoostAbAttr),
new Ability(Abilities.SKILL_LINK, 4)
@ -3699,7 +3813,7 @@ export function initAbilities() {
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
new Ability(Abilities.QUICK_FEET, 4)
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.SPD, 1.5),
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.SPD, 1.5),
new Ability(Abilities.NORMALIZE, 4)
.attr(MoveTypeChangeAttr, Type.NORMAL, 1.2, (user, target, move) => move.id !== Moves.HIDDEN_POWER && move.id !== Moves.WEATHER_BALL &&
move.id !== Moves.NATURAL_GIFT && move.id !== Moves.JUDGMENT && move.id !== Moves.TECHNO_BLAST),
@ -4268,9 +4382,9 @@ export function initAbilities() {
.attr(TypeImmunityStatChangeAbAttr, Type.FIRE, BattleStat.DEF, 2)
.ignorable(),
new Ability(Abilities.WIND_RIDER, 9)
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.WIND_MOVE), BattleStat.ATK, 1)
.ignorable()
.partial(),
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.WIND_MOVE) && move.getMove().category !== MoveCategory.STATUS, BattleStat.ATK, 1)
.attr(PostSummonStatChangeOnArenaAbAttr, ArenaTagType.TAILWIND)
.ignorable(),
new Ability(Abilities.GUARD_DOG, 9)
.attr(PostIntimidateStatChangeAbAttr, [BattleStat.ATK], 1, true)
.attr(ForceSwitchOutImmunityAbAttr)
@ -4278,8 +4392,7 @@ export function initAbilities() {
new Ability(Abilities.ROCKY_PAYLOAD, 9)
.attr(MoveTypePowerBoostAbAttr, Type.ROCK),
new Ability(Abilities.WIND_POWER, 9)
.attr(PostDefendApplyBattlerTagAbAttr, (target, user, move) => move.hasFlag(MoveFlags.WIND_MOVE), BattlerTagType.CHARGED)
.partial(),
.attr(PostDefendApplyBattlerTagAbAttr, (target, user, move) => move.hasFlag(MoveFlags.WIND_MOVE), BattlerTagType.CHARGED),
new Ability(Abilities.ZERO_TO_HERO, 9)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)

View File

@ -4,7 +4,7 @@ import * as Utils from "../utils";
import { MoveCategory, allMoves, MoveTarget } from "./move";
import { getPokemonMessage } from "../messages";
import Pokemon, { HitResult, PokemonMove } from "../field/pokemon";
import { MoveEffectPhase, PokemonHealPhase, StatChangePhase} from "../phases";
import { MoveEffectPhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase} from "../phases";
import { StatusEffect } from "./status-effect";
import { BattlerIndex } from "../battle";
import { Moves } from "./enums/moves";
@ -12,6 +12,9 @@ import { ArenaTagType } from "./enums/arena-tag-type";
import { BlockNonDirectDamageAbAttr, ProtectStatAbAttr, applyAbAttrs } from "./ability";
import { BattleStat } from "./battle-stat";
import { CommonAnim, CommonBattleAnim } from "./battle-anims";
import { Abilities } from "./enums/abilities";
import { BattlerTagType } from "./enums/battler-tag-type";
import i18next from "i18next";
export enum ArenaTagSide {
BOTH,
@ -625,6 +628,22 @@ class TailwindTag extends ArenaTag {
onAdd(arena: Arena): void {
arena.scene.queueMessage(`The Tailwind blew from behind${this.side === ArenaTagSide.PLAYER ? "\nyour" : this.side === ArenaTagSide.ENEMY ? "\nthe opposing" : ""} team!`);
const source = arena.scene.getPokemonById(this.sourceId);
const party = source.isPlayer() ? source.scene.getPlayerField() : source.scene.getEnemyField();
for (const pokemon of party) {
// Apply the CHARGED tag to party members with the WIND_POWER ability
if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
pokemon.addTag(BattlerTagType.CHARGED);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: pokemon.name, moveName: this.getMoveName() }));
}
// Raise attack by one stage if party member has WIND_RIDER ability
if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.getBattlerIndex()));
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK], 1, true));
}
}
}
onRemove(arena: Arena): void {

View File

@ -1823,7 +1823,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.VOLCANO]: {
[BiomePoolTier.COMMON]: [],
[BiomePoolTier.COMMON]: [ TrainerType.FIREBREATHER ],
[BiomePoolTier.UNCOMMON]: [],
[BiomePoolTier.RARE]: [],
[BiomePoolTier.SUPER_RARE]: [],
@ -7304,6 +7304,10 @@ export function initBiomes() {
[ Biome.GRAVEYARD, BiomePoolTier.UNCOMMON ]
]
],
[ TrainerType.FIREBREATHER, [
[ Biome.VOLCANO, BiomePoolTier.COMMON ]
]
],
[ TrainerType.BROCK, [
[ Biome.CAVE, BiomePoolTier.BOSS ]
]

View File

@ -421,6 +421,20 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
]
}
],
[TrainerType.FIREBREATHER]: [
{
encounter: [
"dialogue:firebreather.encounter.1",
"dialogue:firebreather.encounter.2",
"dialogue:firebreather.encounter.3",
],
victory: [
"dialogue:firebreather.victory.1",
"dialogue:firebreather.victory.2",
"dialogue:firebreather.victory.3",
]
}
],
[TrainerType.BROCK]: {
encounter: [
"dialogue:brock.encounter.1",

View File

@ -16,6 +16,7 @@ export enum TrainerType {
DANCER,
DEPOT_AGENT,
DOCTOR,
FIREBREATHER,
FISHERMAN,
GUITARIST,
HARLEQUIN,

View File

@ -423,6 +423,14 @@ export default class Move implements Localizable {
export class AttackMove extends Move {
constructor(id: Moves, type: Type, category: MoveCategory, power: integer, accuracy: integer, pp: integer, chance: integer, priority: integer, generation: integer) {
super(id, type, category, MoveTarget.NEAR_OTHER, power, accuracy, pp, chance, priority, generation);
/**
* {@link https://bulbapedia.bulbagarden.net/wiki/Freeze_(status_condition)}
* > All damaging Fire-type moves can now thaw a frozen target, regardless of whether or not they have a chance to burn;
*/
if (this.type === Type.FIRE) {
this.addAttr(new HealStatusEffectAttr(false, StatusEffect.FREEZE));
}
}
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
@ -1437,7 +1445,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0);
const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)];
user.scene.tryTransferHeldItemModifier(stolenItem, user, false, false).then(success => {
user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => {
if (success) {
user.scene.queueMessage(getPokemonMessage(user, ` stole\n${target.name}'s ${stolenItem.type.name}!`));
}
@ -1486,7 +1494,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0);
const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)];
user.scene.tryTransferHeldItemModifier(stolenItem, user, false, false).then(success => {
user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => {
if (success) {
user.scene.queueMessage(getPokemonMessage(user, ` knocked off\n${target.name}'s ${stolenItem.type.name}!`));
}
@ -1612,15 +1620,31 @@ export class StealEatBerryAttr extends EatBerryAttr {
}
}
/**
* Move attribute that signals that the move should cure a status effect
* @extends MoveEffectAttr
* @see {@linkcode apply()}
*/
export class HealStatusEffectAttr extends MoveEffectAttr {
/** List of Status Effects to cure */
private effects: StatusEffect[];
/**
* @param selfTarget - Whether this move targets the user
* @param ...effects - List of status effects to cure
*/
constructor(selfTarget: boolean, ...effects: StatusEffect[]) {
super(selfTarget);
this.effects = effects;
}
/**
* @param user {@linkcode Pokemon} source of the move
* @param target {@linkcode Pokemon} target of the move
* @param move the {@linkcode Move} being used
* @returns true if the status is cured
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
@ -2680,12 +2704,57 @@ export class HitCountPowerAttr extends VariablePowerAttr {
}
}
/**
* Turning a once was (StatChangeCountPowerAttr) statement and making it available to call for any attribute.
* @param {Pokemon} pokemon The pokemon that is being used to calculate the count of positive stats
* @returns {number} Returns the amount of positive stats
*/
const countPositiveStats = (pokemon: Pokemon): number => {
return pokemon.summonData.battleStats.reduce((total, stat) => (stat && stat > 0) ? total + stat : total, 0);
};
/**
* Attribute that increases power based on the amount of positive stat increases.
*/
export class StatChangeCountPowerAttr extends VariablePowerAttr {
/**
* @param {Pokemon} user The pokemon that is being used to calculate the amount of positive stats
* @param {Pokemon} target N/A
* @param {Move} move N/A
* @param {any[]} args The argument for VariablePowerAttr, accumulates and sets the amount of power multiplied by stats
* @returns {boolean} Returns true if attribute is applied
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const positiveStats: number = user.summonData.battleStats.reduce((total, stat) => stat > 0 && stat ? total + stat : total, 0);
const positiveStats: number = countPositiveStats(user);
(args[0] as Utils.NumberHolder).value += positiveStats * 20;
return true;
}
}
/**
* Punishment normally has a base power of 60,
* but gains 20 power for every increased stat stage the target has,
* up to a maximum of 200 base power in total.
*/
export class PunishmentPowerAttr extends VariablePowerAttr {
private PUNISHMENT_MIN_BASE_POWER = 60;
private PUNISHMENT_MAX_BASE_POWER = 200;
/**
* @param {Pokemon} user N/A
* @param {Pokemon} target The pokemon that the move is being used against, as well as calculating the stats for the min/max base power
* @param {Move} move N/A
* @param {any[]} args The value that is being changed due to VariablePowerAttr
* @returns Returns true if attribute is applied
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const positiveStats: number = countPositiveStats(target);
(args[0] as Utils.NumberHolder).value = Math.min(
this.PUNISHMENT_MAX_BASE_POWER,
this.PUNISHMENT_MIN_BASE_POWER + positiveStats * 20
);
return true;
}
}
@ -6110,7 +6179,8 @@ export function initMoves() {
new StatusMove(Moves.GUARD_SWAP, Type.PSYCHIC, -1, 10, -1, 0, 4)
.unimplemented(),
new AttackMove(Moves.PUNISHMENT, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4)
.unimplemented(),
.makesContact(true)
.attr(PunishmentPowerAttr),
new AttackMove(Moves.LAST_RESORT, Type.NORMAL, MoveCategory.PHYSICAL, 140, 100, 5, -1, 0, 4)
.attr(LastResortAttr),
new StatusMove(Moves.WORRY_SEED, Type.GRASS, 100, 10, -1, 0, 4)
@ -6681,6 +6751,7 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.STEAM_ERUPTION, Type.WATER, MoveCategory.SPECIAL, 110, 95, 5, 30, 0, 6)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
.attr(HealStatusEffectAttr, false, StatusEffect.FREEZE)
.attr(StatusEffectAttr, StatusEffect.BURN),
new AttackMove(Moves.HYPERSPACE_HOLE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, -1, 5, -1, 0, 6)
.ignoresProtect(),
@ -7357,6 +7428,7 @@ export function initMoves() {
.attr(MultiHitAttr, MultiHitType._2),
new AttackMove(Moves.SCORCHING_SANDS, Type.GROUND, MoveCategory.SPECIAL, 70, 100, 10, 30, 0, 8)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
.attr(HealStatusEffectAttr, false, StatusEffect.FREEZE)
.attr(StatusEffectAttr, StatusEffect.BURN),
new StatusMove(Moves.JUNGLE_HEALING, Type.GRASS, -1, 10, -1, 0, 8)
.attr(HealAttr, 0.25, true, false)
@ -7736,6 +7808,7 @@ export function initMoves() {
new AttackMove(Moves.MATCHA_GOTCHA, Type.GRASS, MoveCategory.SPECIAL, 80, 90, 15, 20, 0, 9)
.attr(HitHealAttr)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
.attr(HealStatusEffectAttr, false, StatusEffect.FREEZE)
.attr(StatusEffectAttr, StatusEffect.BURN)
.target(MoveTarget.ALL_NEAR_ENEMIES)
.triageMove()

View File

@ -765,10 +765,10 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.DURALUDON, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
],
[Species.ZACIAN]: [
new SpeciesFormChange(Species.ZACIAN, "hero", "crowned", new SpeciesFormChangeItemTrigger(FormChangeItem.RUSTED_SWORD))
new SpeciesFormChange(Species.ZACIAN, "hero-of-many-battles", "crowned", new SpeciesFormChangeItemTrigger(FormChangeItem.RUSTED_SWORD))
],
[Species.ZAMAZENTA]: [
new SpeciesFormChange(Species.ZAMAZENTA, "hero", "crowned", new SpeciesFormChangeItemTrigger(FormChangeItem.RUSTED_SHIELD))
new SpeciesFormChange(Species.ZAMAZENTA, "hero-of-many-battles", "crowned", new SpeciesFormChangeItemTrigger(FormChangeItem.RUSTED_SHIELD))
],
[Species.ETERNATUS]: [
new SpeciesFormChange(Species.ETERNATUS, "", SpeciesFormKey.ETERNAMAX, new SpeciesFormChangeManualTrigger()),

View File

@ -387,9 +387,11 @@ export abstract class PokemonSpeciesForm {
case "four":
case "droopy":
case "stretchy":
case "hero":
case "roaming":
case "complete":
case "10":
case "10-pc":
case "super":
case "unbound":
case "pau":
@ -2137,13 +2139,13 @@ export function initSpecies() {
new PokemonForm("Blue Meteor Form", "blue-meteor", Type.ROCK, Type.FLYING, 0.3, 40, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 440, 60, 60, 100, 60, 100, 60, 30, 70, 154, false, "", true),
new PokemonForm("Indigo Meteor Form", "indigo-meteor", Type.ROCK, Type.FLYING, 0.3, 40, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 440, 60, 60, 100, 60, 100, 60, 30, 70, 154, false, "", true),
new PokemonForm("Violet Meteor Form", "violet-meteor", Type.ROCK, Type.FLYING, 0.3, 40, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 440, 60, 60, 100, 60, 100, 60, 30, 70, 154, false, "", true),
new PokemonForm("Red Core Form", "red", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Orange Core Form", "orange", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Yellow Core Form", "yellow", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Green Core Form", "green", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Blue Core Form", "blue", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Indigo Core Form", "indigo", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Violet Core Form", "violet", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154),
new PokemonForm("Red Core Form", "red", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
new PokemonForm("Orange Core Form", "orange", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
new PokemonForm("Yellow Core Form", "yellow", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
new PokemonForm("Green Core Form", "green", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
new PokemonForm("Blue Core Form", "blue", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
new PokemonForm("Indigo Core Form", "indigo", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
new PokemonForm("Violet Core Form", "violet", Type.ROCK, Type.FLYING, 0.3, 0.3, Abilities.SHIELDS_DOWN, Abilities.NONE, Abilities.NONE, 500, 60, 100, 60, 100, 60, 120, 30, 70, 154, false, null, true),
),
new PokemonSpecies(Species.KOMALA, 7, false, false, false, "Drowsing Pokémon", Type.NORMAL, null, 0.4, 19.9, Abilities.COMATOSE, Abilities.NONE, Abilities.NONE, 480, 65, 115, 65, 75, 95, 65, 45, 70, 168, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.TURTONATOR, 7, false, false, false, "Blast Turtle Pokémon", Type.FIRE, Type.DRAGON, 2, 212, Abilities.SHELL_ARMOR, Abilities.NONE, Abilities.NONE, 485, 60, 78, 135, 91, 85, 36, 70, 50, 170, GrowthRate.MEDIUM_FAST, 50, false),
@ -2272,7 +2274,7 @@ export function initSpecies() {
new PokemonSpecies(Species.TOXEL, 8, false, false, false, "Baby Pokémon", Type.ELECTRIC, Type.POISON, 0.4, 11, Abilities.RATTLED, Abilities.STATIC, Abilities.KLUTZ, 242, 40, 38, 35, 54, 35, 40, 75, 50, 48, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(Species.TOXTRICITY, 8, false, false, false, "Punk Pokémon", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.PLUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, GrowthRate.MEDIUM_SLOW, 50, false, true,
new PokemonForm("Amped Form", "amped", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.PLUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, false, "", true),
new PokemonForm("Low-Key Form", "lowkey", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.MINUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, true),
new PokemonForm("Low-Key Form", "lowkey", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.MINUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, false, "lowkey", true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, Type.POISON, 24, 40, Abilities.PUNK_ROCK, Abilities.MINUS, Abilities.TECHNICIAN, 602, 95, 118, 80, 144, 80, 85, 45, 50, 176),
),
new PokemonSpecies(Species.SIZZLIPEDE, 8, false, false, false, "Radiator Pokémon", Type.FIRE, Type.BUG, 0.7, 1, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 305, 50, 65, 45, 50, 50, 45, 190, 50, 61, GrowthRate.MEDIUM_FAST, 50, false),
@ -2355,11 +2357,11 @@ export function initSpecies() {
new PokemonSpecies(Species.DRAKLOAK, 8, false, false, false, "Caretaker Pokémon", Type.DRAGON, Type.GHOST, 1.4, 11, Abilities.CLEAR_BODY, Abilities.INFILTRATOR, Abilities.CURSED_BODY, 410, 68, 80, 50, 60, 50, 102, 45, 50, 144, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.DRAGAPULT, 8, false, false, false, "Stealth Pokémon", Type.DRAGON, Type.GHOST, 3, 50, Abilities.CLEAR_BODY, Abilities.INFILTRATOR, Abilities.CURSED_BODY, 600, 88, 120, 75, 100, 75, 142, 45, 50, 300, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.ZACIAN, 8, false, true, false, "Warrior Pokémon", Type.FAIRY, null, 2.8, 110, Abilities.INTREPID_SWORD, Abilities.NONE, Abilities.NONE, 660, 92, 120, 115, 80, 115, 138, 10, 0, 335, GrowthRate.SLOW, null, false, false,
new PokemonForm("Hero of Many Battles", "hero", Type.FAIRY, null, 2.8, 110, Abilities.INTREPID_SWORD, Abilities.NONE, Abilities.NONE, 660, 92, 120, 115, 80, 115, 138, 10, 0, 335, false, "", true),
new PokemonForm("Hero of Many Battles", "hero-of-many-battles", Type.FAIRY, null, 2.8, 110, Abilities.INTREPID_SWORD, Abilities.NONE, Abilities.NONE, 660, 92, 120, 115, 80, 115, 138, 10, 0, 335, false, "", true),
new PokemonForm("Crowned", "crowned", Type.FAIRY, Type.STEEL, 2.8, 355, Abilities.INTREPID_SWORD, Abilities.NONE, Abilities.NONE, 700, 92, 150, 115, 80, 115, 148, 10, 0, 335),
),
new PokemonSpecies(Species.ZAMAZENTA, 8, false, true, false, "Warrior Pokémon", Type.FIGHTING, null, 2.9, 210, Abilities.DAUNTLESS_SHIELD, Abilities.NONE, Abilities.NONE, 660, 92, 120, 115, 80, 115, 138, 10, 0, 335, GrowthRate.SLOW, null, false, false,
new PokemonForm("Hero of Many Battles", "hero", Type.FIGHTING, null, 2.9, 210, Abilities.DAUNTLESS_SHIELD, Abilities.NONE, Abilities.NONE, 660, 92, 120, 115, 80, 115, 138, 10, 0, 335, false, "", true),
new PokemonForm("Hero of Many Battles", "hero-of-many-battles", Type.FIGHTING, null, 2.9, 210, Abilities.DAUNTLESS_SHIELD, Abilities.NONE, Abilities.NONE, 660, 92, 120, 115, 80, 115, 138, 10, 0, 335, false, "", true),
new PokemonForm("Crowned", "crowned", Type.FIGHTING, Type.STEEL, 2.9, 785, Abilities.DAUNTLESS_SHIELD, Abilities.NONE, Abilities.NONE, 700, 92, 120, 140, 80, 140, 128, 10, 0, 335),
),
new PokemonSpecies(Species.ETERNATUS, 8, false, true, false, "Gigantic Pokémon", Type.POISON, Type.DRAGON, 20, 950, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 690, 140, 85, 95, 145, 95, 130, 255, 0, 345, GrowthRate.SLOW, null, false, true,
@ -2468,9 +2470,9 @@ export function initSpecies() {
new PokemonSpecies(Species.WUGTRIO, 9, false, false, false, "Garden Eel Pokémon", Type.WATER, null, 1.2, 5.4, Abilities.GOOEY, Abilities.RATTLED, Abilities.SAND_VEIL, 425, 35, 100, 50, 50, 70, 120, 50, 50, 149, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.BOMBIRDIER, 9, false, false, false, "Item Drop Pokémon", Type.FLYING, Type.DARK, 1.5, 42.9, Abilities.BIG_PECKS, Abilities.KEEN_EYE, Abilities.ROCKY_PAYLOAD, 485, 70, 103, 85, 60, 85, 82, 25, 50, 243, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.FINIZEN, 9, false, false, false, "Dolphin Pokémon", Type.WATER, null, 1.3, 60.2, Abilities.WATER_VEIL, Abilities.NONE, Abilities.NONE, 315, 70, 45, 40, 45, 40, 75, 200, 50, 63, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.PALAFIN, 9, false, false, false, "Dolphin Pokémon", Type.WATER, null, 1.3, 60.2, Abilities.ZERO_TO_HERO, Abilities.NONE, Abilities.NONE, 457, 100, 70, 72, 53, 62, 100, 45, 50, 160, GrowthRate.SLOW, 50, false, false,
new PokemonSpecies(Species.PALAFIN, 9, false, false, false, "Dolphin Pokémon", Type.WATER, null, 1.3, 60.2, Abilities.ZERO_TO_HERO, Abilities.NONE, Abilities.NONE, 457, 100, 70, 72, 53, 62, 100, 45, 50, 160, GrowthRate.SLOW, 50, false, true,
new PokemonForm("Zero Form", "zero", Type.WATER, null, 1.3, 60.2, Abilities.ZERO_TO_HERO, Abilities.NONE, Abilities.ZERO_TO_HERO, 457, 100, 70, 72, 53, 62, 100, 45, 50, 160, false, null, true),
new PokemonForm("Hero Form", "hero", Type.WATER, null, 1.8, 97.4, Abilities.ZERO_TO_HERO, Abilities.NONE, Abilities.ZERO_TO_HERO, 650, 100, 160, 97, 106, 87, 100, 45, 50, 160, false, null),
new PokemonForm("Hero Form", "hero", Type.WATER, null, 1.8, 97.4, Abilities.ZERO_TO_HERO, Abilities.NONE, Abilities.ZERO_TO_HERO, 650, 100, 160, 97, 106, 87, 100, 45, 50, 160),
),
new PokemonSpecies(Species.VAROOM, 9, false, false, false, "Single-Cyl Pokémon", Type.STEEL, Type.POISON, 1, 35, Abilities.OVERCOAT, Abilities.NONE, Abilities.SLOW_START, 300, 45, 70, 63, 30, 45, 47, 190, 50, 60, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.REVAVROOM, 9, false, false, false, "Multi-Cyl Pokémon", Type.STEEL, Type.POISON, 1.8, 120, Abilities.OVERCOAT, Abilities.NONE, Abilities.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, GrowthRate.MEDIUM_FAST, 50, false),

View File

@ -989,6 +989,8 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.DEPOT_AGENT]: new TrainerConfig(++t).setMoneyMultiplier(1.45).setEncounterBgm(TrainerType.CLERK),
[TrainerType.DOCTOR]: new TrainerConfig(++t).setHasGenders("Nurse", "lass").setHasDouble("Medical Team").setMoneyMultiplier(3).setEncounterBgm(TrainerType.CLERK)
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.HEAL_PULSE)),
[TrainerType.FIREBREATHER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK)
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.SMOG) || s.isOfType(Type.FIRE)),
[TrainerType.FISHERMAN]: new TrainerConfig(++t).setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.BACKPACKER).setSpecialtyTypes(Type.WATER)
.setPartyTemplates(trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.THREE_WEAK_SAME, trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.SIX_WEAKER)
.setSpeciesPools({

View File

@ -36,6 +36,7 @@ const trainerNameConfigs: TrainerNameConfigs = {
[TrainerType.DANCER]: new TrainerNameConfig(TrainerType.DANCER),
[TrainerType.DEPOT_AGENT]: new TrainerNameConfig(TrainerType.DEPOT_AGENT),
[TrainerType.DOCTOR]: new TrainerNameConfig(TrainerType.DOCTOR).hasGenderVariant("Nurse"),
[TrainerType.FIREBREATHER]: new TrainerNameConfig(TrainerType.FIREBREATHER),
[TrainerType.FISHERMAN]: new TrainerNameConfig(TrainerType.FISHERMAN),
[TrainerType.GUITARIST]: new TrainerNameConfig(TrainerType.GUITARIST),
[TrainerType.HARLEQUIN]: new TrainerNameConfig(TrainerType.HARLEQUIN),
@ -87,6 +88,7 @@ export const trainerNamePools = {
[TrainerType.DANCER]: ["Brian","Davey","Dirk","Edmond","Mickey","Raymond","Cara","Julia","Maika","Mireille","Ronda","Zoe"],
[TrainerType.DEPOT_AGENT]: ["Josh","Hank","Vincent"],
[TrainerType.DOCTOR]: [["Hank","Jerry","Jules","Logan","Wayne","Braid","Derek","Heath","Julius","Kit","Graham"],["Kirsten","Sachiko","Shery","Carol","Dixie","Mariah"]],
[TrainerType.FIREBREATHER]: ["Bill","Burt","Cliff","Dick","Lyle","Ned","Otis","Ray","Richard","Walt"],
[TrainerType.FISHERMAN]: ["Andre","Arnold","Barney","Chris","Edgar","Henry","Jonah","Justin","Kyle","Martin","Marvin","Ralph","Raymond","Scott","Stephen","Wilton","Tully","Andrew","Barny","Carter","Claude","Dale","Elliot","Eugene","Ivan","Ned","Nolan","Roger","Ronald","Wade","Wayne","Darian","Kai","Chip","Hank","Kaden","Tommy","Tylor","Alec","Brett","Cameron","Cody","Cole","Cory","Erick","George","Joseph","Juan","Kenneth","Luc","Miguel","Travis","Walter","Zachary","Josh","Gideon","Kyler","Liam","Murphy","Bruce","Damon","Devon","Hubert","Jones","Lydon","Mick","Pete","Sean","Sid","Vince","Bucky","Dean","Eustace","Kenzo","Leroy","Mack","Ryder","Ewan","Finn","Murray","Seward","Shad","Wharton","Finley","Fisher","Fisk","River","Sheaffer","Timin","Carl","Ernest","Hal","Herbert","Hisato","Mike","Vernon","Harriet","Marina","Chase"],
[TrainerType.GUITARIST]: ["Anna","Beverly","January","Tina","Alicia","Claudia","Julia","Lidia","Mireia","Noelia","Sara","Sheila","Tatiana"],
[TrainerType.HARLEQUIN]: ["Charley","Ian","Jack","Kerry","Louis","Pat","Paul","Rick","Anders","Clarence","Gary"],

View File

@ -14,30 +14,51 @@ import { EggTier } from "./data/enums/egg-type";
import PokemonInfoContainer from "./ui/pokemon-info-container";
import EggsToHatchCountContainer from "./ui/eggs-to-hatch-count-container";
/**
* Class that represents egg hatching
*/
export class EggHatchPhase extends Phase {
/** The egg that is hatching */
private egg: Egg;
/** The number of eggs that are hatching */
private eggsToHatchCount: integer;
/** The container that lists how many eggs are hatching */
private eggsToHatchCountContainer: EggsToHatchCountContainer;
/** The scene handler for egg hatching */
private eggHatchHandler: EggHatchSceneHandler;
/** The phaser gameobject container that holds everything */
private eggHatchContainer: Phaser.GameObjects.Container;
/** The phaser image that is the background */
private eggHatchBg: Phaser.GameObjects.Image;
/** The phaser rectangle that overlays during the scene */
private eggHatchOverlay: Phaser.GameObjects.Rectangle;
/** The phaser container that holds the egg */
private eggContainer: Phaser.GameObjects.Container;
/** The phaser sprite of the egg */
private eggSprite: Phaser.GameObjects.Sprite;
/** The phaser sprite of the cracks in an egg */
private eggCrackSprite: Phaser.GameObjects.Sprite;
/** The phaser sprite that represents the overlaid light rays */
private eggLightraysOverlay: Phaser.GameObjects.Sprite;
/** The phaser sprite of the hatched Pokemon */
private pokemonSprite: Phaser.GameObjects.Sprite;
/** The phaser sprite for shiny sparkles */
private pokemonShinySparkle: Phaser.GameObjects.Sprite;
/** The {@link PokemonInfoContainer} of the newly hatched Pokemon */
private infoContainer: PokemonInfoContainer;
/** The newly hatched {@link PlayerPokemon} */
private pokemon: PlayerPokemon;
/** The index of which egg move is unlocked. 0-2 is common, 3 is rare */
private eggMoveIndex: integer;
/** Internal booleans representing if the egg is hatched, able to be skipped, or skipped */
private hatched: boolean;
private canSkip: boolean;
private skipped: boolean;
/** The sound effect being played when the egg is hatched */
private evolutionBgm: AnySound;
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
@ -117,6 +138,7 @@ export class EggHatchPhase extends Phase {
this.eggHatchContainer.add(this.infoContainer);
// The game will try to unfuse any Pokemon even though eggs should not generate fused Pokemon in the first place
const pokemon = this.generatePokemon();
if (pokemon.fusionSpecies) {
pokemon.clearFusionSpecies();
@ -187,6 +209,13 @@ export class EggHatchPhase extends Phase {
super.end();
}
/**
* Function that animates egg shaking
* @param intensity of horizontal shaking. Doubled on the first call (where count is 0)
* @param repeatCount the number of times this function should be called (asynchronous recursion?!?)
* @param count the current number of times this function has been called.
* @returns nothing since it's a Promise<void>
*/
doEggShake(intensity: number, repeatCount?: integer, count?: integer): Promise<void> {
return new Promise(resolve => {
if (repeatCount === undefined) {
@ -226,6 +255,10 @@ export class EggHatchPhase extends Phase {
});
}
/**
* Tries to skip the hatching animation
* @returns false if cannot be skipped or already skipped. True otherwise
*/
trySkip(): boolean {
if (!this.canSkip || this.skipped) {
return false;
@ -239,6 +272,9 @@ export class EggHatchPhase extends Phase {
return true;
}
/**
* Plays the animation of an egg hatch
*/
doHatch(): void {
this.canSkip = false;
this.hatched = true;
@ -268,6 +304,9 @@ export class EggHatchPhase extends Phase {
});
}
/**
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
*/
doReveal(): void {
// Update/reduce count of hatching eggs when revealed if count is at least 1
// If count is 0, hide eggsToHatchCountContainer instead
@ -333,10 +372,21 @@ export class EggHatchPhase extends Phase {
});
}
/**
* Helper function to generate sine. (Why is this not a Utils?!?)
* @param index random number from 0-7 being passed in to scale pi/128
* @param amplitude Scaling
* @returns a number
*/
sin(index: integer, amplitude: integer): number {
return amplitude * Math.sin(index * (Math.PI / 128));
}
/**
* Animates spraying
* @param intensity number of times this is repeated (this is a badly named variable)
* @param offsetY how much to offset the Y coordinates
*/
doSpray(intensity: integer, offsetY?: number) {
this.scene.tweens.addCounter({
repeat: intensity,
@ -347,6 +397,11 @@ export class EggHatchPhase extends Phase {
});
}
/**
* Animates a particle used in the spray animation
* @param trigIndex Used to modify the particle's vertical speed, is a random number from 0-7
* @param offsetY how much to offset the Y coordinate
*/
doSprayParticle(trigIndex: integer, offsetY: number) {
const initialX = this.eggHatchBg.displayWidth / 2;
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY;
@ -387,12 +442,22 @@ export class EggHatchPhase extends Phase {
updateParticle();
}
/**
* Generates a Pokemon to be hatched by the egg
* @returns the hatched PlayerPokemon
*/
generatePokemon(): PlayerPokemon {
let ret: PlayerPokemon;
let speciesOverride: Species;
let speciesOverride: Species; // SpeciesOverride should probably be a passed in parameter for future species-eggs
this.scene.executeWithSeedOffset(() => {
/**
* Manaphy eggs have a 1/8 chance of being Manaphy and 7/8 chance of being Phione
* Legendary eggs pulled from the legendary gacha have a 50% of being converted into
* the species that was the legendary focus at the time
*/
if (this.egg.isManaphyEgg()) {
const rand = Utils.randSeedInt(8);
@ -437,6 +502,18 @@ export class EggHatchPhase extends Phase {
.map(s => parseInt(s) as Species)
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
/**
* Pokemon that are cheaper in their tier get a weight boost. Regionals get a weight penalty
* 1 cost mons get 2x
* 2 cost mons get 1.5x
* 4, 6, 8 cost mons get 1.75x
* 3, 5, 7, 9 cost mons get 1x
* Alolan, Galarian, and Paldean mons get 0.5x
* Hisui mons get 0.125x
*
* The total weight is also being calculated EACH time there is an egg hatch instead of being generated once
* and being the same each time
*/
let totalWeight = 0;
const speciesWeights = [];
for (const speciesId of speciesPool) {
@ -464,6 +541,16 @@ export class EggHatchPhase extends Phase {
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
}
/**
* Non Shiny gacha Pokemon have a 1/128 chance of being shiny
* Shiny gacha Pokemon have a 1/64 chance of being shiny
* IVs are rolled twice and the higher of each stat's IV is taken
* The egg move gacha doubles the rate of rare egg moves but the base rates are
* Common: 1/48
* Rare: 1/24
* Epic: 1/12
* Legendary: 1/6
*/
ret.trySetShiny(this.egg.gachaType === GachaType.SHINY ? 1024 : 512);
ret.variant = ret.shiny ? ret.generateVariant() : 0;

77
src/field/arena-events.ts Normal file
View File

@ -0,0 +1,77 @@
import { ArenaTagSide } from "#app/data/arena-tag.js";
import { ArenaTagType } from "#app/data/enums/arena-tag-type.js";
import { TerrainType } from "#app/data/terrain.js";
import { WeatherType } from "#app/data/weather.js";
/** Alias for all {@linkcode ArenaEvent} type strings */
export enum ArenaEventType {
/** Triggers when a {@linkcode WeatherType} is added, overlapped, or removed */
WEATHER_CHANGED = "onWeatherChanged",
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
TERRAIN_CHANGED = "onTerrainChanged",
/** Triggers when a {@linkcode ArenaTagType} is added or removed */
TAG_CHANGED = "onTagChanged",
}
/**
* Base container class for all {@linkcode ArenaEventType} events
* @extends Event
*/
export class ArenaEvent extends Event {
/** The total duration of the {@linkcode ArenaEventType} */
public duration: number;
constructor(eventType: ArenaEventType, duration: number) {
super(eventType);
this.duration = duration;
}
}
/**
* Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events
* @extends ArenaEvent
*/
export class WeatherChangedEvent extends ArenaEvent {
/** The {@linkcode WeatherType} being overridden */
public oldWeatherType: WeatherType;
/** The {@linkcode WeatherType} being set */
public newWeatherType: WeatherType;
constructor(oldWeatherType: WeatherType, newWeatherType: WeatherType, duration: number) {
super(ArenaEventType.WEATHER_CHANGED, duration);
this.oldWeatherType = oldWeatherType;
this.newWeatherType = newWeatherType;
}
}
/**
* Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events
* @extends ArenaEvent
*/
export class TerrainChangedEvent extends ArenaEvent {
/** The {@linkcode TerrainType} being overridden */
public oldTerrainType: TerrainType;
/** The {@linkcode TerrainType} being set */
public newTerrainType: TerrainType;
constructor(oldTerrainType: TerrainType, newTerrainType: TerrainType, duration: number) {
super(ArenaEventType.TERRAIN_CHANGED, duration);
this.oldTerrainType = oldTerrainType;
this.newTerrainType = newTerrainType;
}
}
/**
* Container class for {@linkcode ArenaEventType.TAG_CHANGED} events
* @extends ArenaEvent
*/
export class TagChangedEvent extends ArenaEvent {
/** The {@linkcode ArenaTagType} being set */
public arenaTagType: ArenaTagType;
/** The {@linkcode ArenaTagSide} the tag is being placed on */
public arenaTagSide: ArenaTagSide;
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
super(ArenaEventType.TAG_CHANGED, duration);
this.arenaTagType = arenaTagType;
this.arenaTagSide = arenaTagSide;
}
}

View File

@ -19,6 +19,7 @@ import { Terrain, TerrainType } from "../data/terrain";
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
import Pokemon from "./pokemon";
import * as Overrides from "../overrides";
import { WeatherChangedEvent, TerrainChangedEvent, TagChangedEvent } from "./arena-events";
export class Arena {
public scene: BattleScene;
@ -34,6 +35,8 @@ export class Arena {
private pokemonPool: PokemonPools;
private trainerPool: BiomeTierTrainerPools;
public readonly eventTarget: EventTarget = new EventTarget();
constructor(scene: BattleScene, biome: Biome, bgm: string) {
this.scene = scene;
this.biomeType = biome;
@ -300,6 +303,7 @@ export class Arena {
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null;
this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft));
if (this.weather) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
@ -324,6 +328,7 @@ export class Arena {
const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null;
this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType, this.terrain?.turnsLeft));
if (this.terrain) {
if (!ignoreAnim) {
@ -545,6 +550,8 @@ export class Arena {
this.tags.push(newTag);
newTag.onAdd(this);
this.eventTarget.dispatchEvent(new TagChangedEvent(newTag.tagType, newTag.side, newTag.turnCount));
return true;
}

View File

@ -978,7 +978,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
if (this.isOnField() && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) {
const suppressed = new Utils.BooleanHolder(false);
this.scene.getField(true).map(p => {
this.scene.getField(true).filter(p => p !== this).map(p => {
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability]));
}
@ -1222,7 +1222,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
}
/**
* Function that tries to set a Pokemon shiny based on the trainer's trainer ID and secret ID
* Endless Pokemon in the end biome are unable to be set to shiny
*
* The exact mechanic is that it calculates E as the XOR of the player's trainer ID and secret ID
* F is calculated as the XOR of the first 16 bits of the Pokemon's ID with the last 16 bits
* The XOR of E and F are then compared to the thresholdOverride (default case 32) to see whether or not to generate a shiny
* @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance
* @returns true if the Pokemon has been set as a shiny, false otherwise
*/
trySetShiny(thresholdOverride?: integer): boolean {
// Shiny Pokemon should not spawn in the end biome in endless
if (this.scene.gameMode.isEndless && this.scene.arena.biomeType === Biome.END) {
return false;
}
const rand1 = Utils.binToDec(Utils.decToBin(this.id).substring(0, 16));
const rand2 = Utils.binToDec(Utils.decToBin(this.id).substring(16, 32));
@ -1250,18 +1265,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.shiny;
}
/**
* Generates a variant
* Has a 10% of returning 2 (epic variant)
* And a 30% of returning 1 (rare variant)
* Returns 0 (basic shiny) if there is no variant or 60% of the time otherwise
* @returns the shiny variant
*/
generateVariant(): Variant {
if (!this.shiny || !variantData.hasOwnProperty(this.species.speciesId)) {
return 0;
}
const rand = Utils.randSeedInt(10);
if (rand > 3) {
return 0;
if (rand >= 4) {
return 0; // 6/10
} else if (rand >= 1) {
return 1; // 3/10
} else {
return 2; // 1/10
}
if (rand) {
return 1;
}
return 2;
}
generateFusionSpecies(forStarter?: boolean): void {
@ -3167,7 +3189,7 @@ export class PlayerPokemon extends Pokemon {
&& (m as PokemonHeldItemModifier).pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
const transferModifiers: Promise<boolean>[] = [];
for (const modifier of fusedPartyMemberHeldModifiers) {
transferModifiers.push(this.scene.tryTransferHeldItemModifier(modifier, this, true, false, true, true));
transferModifiers.push(this.scene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true));
}
Promise.allSettled(transferModifiers).then(() => {
this.scene.updateModifiers(true, true).then(() => {

View File

@ -3,4 +3,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}} wurde durch {{abilityName}}\nvor Rückstoß geschützt!",
"badDreams": "{{pokemonName}} ist in einem Alptraum gefangen!",
"windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!",
} as const;

View File

@ -367,6 +367,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "Ich werde für das nächste Rennen tunen."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "Meine Expertise in Bezug auf Gesteins-Pokémon wird dich besiegen! Komm schon!",
@ -516,7 +528,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"victory": {
1: "Oh, es scheint, als hätte ich verloren.",
2: "Es scheint, als hätte ich noch so viel mehr zu lernen, wenn es um den Kämpfe geht.",
2: "Es scheint, als hätte ich noch so viel mehr zu lernen, wenn es um Kämpfe geht.",
3: "Ich werde mir zu Herzen nehmen, was ich heute gelernt habe."
},
"defeat": {
@ -775,7 +787,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"marshal": {
"encounter": {
1: `Mein Mentor, Lauro, sieht sieht Potential in dir. Ich werde dich testen,
1: `Mein Mentor, Lauro, sieht Potential in dir. Ich werde dich testen,
$dich an die Grenzen deiner Stärke bringen. Kiai!`,
2: "Ein Sieg, ein entscheidender Sieg, das ist mein Ziel! Herausforderer, hier komme ich!",
3: "Ich selber suche die Stärke eines Kämpfers zu entwickeln und jede Schwäche in mir zu brechen!"
@ -930,7 +942,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"lacey": {
"encounter": {
1: "Ich werde dir mit meiner gewohnten Team, als Mitglied der Top Vier gegenüberstehen."
1: "Ich werde dir mit meinem gewohnten Team, als Mitglied der Top Vier gegenüberstehen."
},
"victory": {
1: "Das war ein großartiger Kampf!"
@ -1025,7 +1037,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"cress": {
"encounter": {
1: "Das ist korrekt! Ich und meine geschätzten Wasser-Typen werden deine Gegner im Kampf sein!"
1: "Das ist korrekt! Ich und meine geschätzten Wasser-Pokémon werden deine Gegner im Kampf sein!"
},
"victory": {
1: "Verlieren? Ich? Das glaube ich nicht."
@ -1234,8 +1246,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Wow, erstaunlich! Du bist ein Experte für Pokémon!
$Meine Forschung ist noch nicht abgeschlossen. OK, du gewinnst.`,
1: "Ich bin Kai, der Arenaleiter von Azalea City. Ich bin ein großer Fan von Käfer-Pokémon.",
},
"victory": {
1: `Wow, erstaunlich! Du bist ein Experte für Pokémon!

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "Bahnangestellter",
"doctor": "Arzt",
"doctor_female": "Ärztin",
"firebreather": "Feuerspucker",
"fisherman": "Angler",
"fisherman_female": "Angler", // Seems to be the same in german but exists in other languages like italian
"gentleman": "Gentleman",

View File

@ -3,5 +3,7 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}}'s {{abilityName}}\nprotected it from recoil!",
"badDreams": "{{pokemonName}} is tormented!",
"perishBody": "{{pokemonName}}'s {{abilityName}}\n will faint both pokemon in 3 turns!",
"windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!",
"perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both pokemon in 3 turns!",
"poisonHeal": "{{pokemonName}}'s {{abilityName}}\nrestored its HP a little!"
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
"LEFTOVERS": { name: "Leftovers", description: "Heals 1/16 of a Pokémon's maximum HP every turn" },
"SHELL_BELL": { name: "Shell Bell", description: "Heals 1/8 of a Pokémon's dealt damage" },
"TOXIC_ORB": { name: "Toxic Orb", description: "Badly poisons its holder at the end of the turn if they do not have a status condition already" },
"FLAME_ORB": { name: "Flame Orb", description: "Burns its holder at the end of the turn if they do not have a status condition already" },
"BATON": { name: "Baton", description: "Allows passing along effects when switching Pokémon, which also bypasses traps" },
"SHINY_CHARM": { name: "Shiny Charm", description: "Dramatically increases the chance of a wild Pokémon being Shiny" },

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "Depot Agent",
"doctor": "Doctor",
"doctor_female": "Doctor",
"firebreather": "Firebreather",
"fisherman": "Fisherman",
"fisherman_female": "Fisherman",
"gentleman": "Gentleman",

View File

@ -2,5 +2,6 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "¡{{abilityName}} de {{pokemonName}}\nlo protegió del daño de retroceso!",
"badDreams": "¡{{pokemonName}} está atormentado!"
"badDreams": "¡{{pokemonName}} está atormentado!",
"windPowerCharged": "¡{{pokemonName}} se ha cargado de electricidad gracias a {{moveName}}!",
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "Ferroviario",
"doctor": "Enfermero",
"doctor_female": "Enfermera",
"firebreather": "Comefuegos",
"fisherman": "Pescador",
"fisherman_female": "Pescadora",
"gentleman": "Aristócrata",

View File

@ -2,5 +2,6 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{abilityName}}\nde {{pokemonName}} le protège du contrecoup !",
"badDreams": "{{pokemonName}} a le sommeil agité !"
"badDreams": "{{pokemonName}} a le sommeil agité !",
"windPowerCharged": "{{pokemonName}} a été touché par la capacité {{moveName}} et se charge en électricité !"
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "Cheminot",
"doctor": "Docteur",
"doctor_female": "Docteure",
"firebreather": "Firebreather",
"fisherman": "Pêcheur",
"fisherman_female": "Pêcheuse",
"gentleman": "Gentleman",

View File

@ -3,4 +3,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{abilityName}} di {{pokemonName}}\nl'ha protetto dal contraccolpo!",
"badDreams": "{{pokemonName}} è tormentato!",
"windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!",
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "Depot Agent",
"doctor": "Doctor",
"doctor_female": "Doctor",
"firebreather": "Firebreather",
"fisherman": "Fisherman",
"fisherman_female": "Fisherman",
"gentleman": "Gentleman",

View File

@ -2,5 +2,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}}(는)은 {{abilityName}} 때문에\n반동 데미지를 받지 않는다!",
"badDreams": "{{pokemonName}} is tormented!",
"badDreams": "{{pokemonName}}(는)은\n나이트메어 때문에 시달리고 있다!",
} as const;

View File

@ -15,7 +15,7 @@ export const battle: SimpleTranslationEntries = {
"trainerDefeated": "{{trainerName}}과(와)의\n승부에서 이겼다!",
"moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!",
"pokemonCaught": "신난다-!\n{{pokemonName}}(를)을 잡았다!",
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}(를)\n대신해 포켓몬을 놓아주시겠습니까?",
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}를(을)\n대신해 포켓몬을 놓아주시겠습니까?",
"pokemon": "포켓몬",
"sendOutPokemon": "가랏! {{pokemonName}}!",
"hitResultCriticalHit": "급소에 맞았다!",

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."
@ -2086,127 +2097,125 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"rival": {
"encounter": {
1: `@c{smile}Hey, I was looking for you! I knew you were eager to get going but I expected at least a goodbye
$@c{smile_eclosed}So you're really pursuing your dream after all?\n I almost can't believe it.
$@c{serious_smile_fists}Since we're here, how about a battle?\nAfter all, I want to make sure you're ready.
$@c{serious_mopen_fists}Don't hold back, I want you to give me everything you've got!`
1: `@c{smile}오, 찾았다! 떠나려는 건 알고 있었지만\n인사정도는 해줄 줄 알았는데
$@c{smile_eclosed} ?\n듣고도 믿.
$@c{serious_smile_fists} , ?\n준비가 .
$@c{serious_mopen_fists} ,\n너의 !`
},
"victory": {
1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner?
$@c{smile}Maybe it was a bit of luck but\nWho knows you might just be able to go all the way.
$By the way, the professor asked me to give you these items. They look pretty cool.
$@c{serious_smile_fists}Good luck out there!`
1: `@c{shock}와… 정말 깔끔하게 당했네.\n초보자 맞아?
$@c{smile} \n그래도 .
$그나저나, .\n좋아 .
$@c{serious_smile_fists}, !`
},
},
"rival_female": {
"encounter": {
1: `@c{smile_wave}There you are! I've been looking everywhere for you!\n@c{angry_mopen}Did you forget to say goodbye to your best friend?
$@c{smile_ehalf}You're going after your dream, huh?\nThat day is really today isn't it
$@c{smile}Anyway, I'll forgive you for forgetting me, but on one condition. @c{smile_wave_wink}You have to battle me!
$@c{angry_mopen}Give it your all! Wouldn't want your adventure to be over before it started, right?`
1: `@c{smile_wave}여깄구나! 찾고 있었어!\n@c{angry_mopen}절친한테 작별인사도 안 하는거야?
$@c{smile_ehalf} . ?\n이런
$@c{smile}, .\n@c{smile_wave_wink} !
$@c{angry_mopen} .\n모험을 !`
},
"victory": {
1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you?
$@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square I have a feeling you're going to do really well out there.
$@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful!
$@c{smile_wave}Do your best like always! I believe in you!`
1: `@c{shock}왜 벌써 이렇게 센 건데?!@d{96}\n@c{angry}아니면 뭔가 속임수, 그런 거?
$@c{smile_wave_wink}, !@d{64} @c{smile_eclosed} \n너 .
$@c{smile} , .\n도움이 !
$@c{smile_wave} ! 믿 !`
},
},
"rival_2": {
"encounter": {
1: `@c{smile}Hey, you're here too?\n@c{smile_eclosed}Still a perfect record, huh…?
$@c{serious_mopen_fists}I know it kind of looks like I followed you here, but that's mostly not true.
$@c{serious_smile_fists}Honestly though, I've been itching for a rematch since you beat me back at home.
$I've been doing a lot of my own training so I'll definitely put up a fight this time.
$@c{serious_mopen_fists}Don't hold back, just like before!\nLet's go!`
1: `@c{smile}어라, 너 여깄었구나?\n@c{smile_eclosed}아직도 전승 중이라, 이거지…?
$@c{serious_mopen_fists} , .
$@c{serious_smile_fists} .
$ ?
$@c{serious_mopen_fists}, !\n배틀이야!`
},
"victory": {
1: `@c{neutral_eclosed}Oh. I guess I was overconfident.
$@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n
$@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n
$@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all.
$@c{smile}Anyway, take care!`
1: `@c{neutral_eclosed}이런. 자신감이 과했나봐.
$@c{smile}, .\n@c{serious_mopen_fists} !\n
$@c{smile} , .\n남는 .
$@c{serious_smile_fists} , ?\n공평하게 .
$@c{smile} . !`
},
},
"rival_2_female": {
"encounter": {
1: `@c{smile_wave}Oh, fancy meeting you here. Looks like you're still undefeated. @c{angry_mopen}Huh… Not bad!
$@c{angry_mopen}I know what you're thinking, and no, I wasn't creeping on you. @c{smile_eclosed}I just happened to be in the area.
$@c{smile_ehalf}I'm happy for you but I just want to let you know that it's OK to lose sometimes.
$@c{smile}We learn from our mistakes, often more than we would if we kept succeeding.
$@c{angry_mopen}In any case, I've been training hard for our rematch, so you'd better give it your all!`
1: `@c{smile_wave}우연이네, 이런 곳에서 만나고.\n아직 연전연승이구나? @c{angry_mopen}나쁘지 않아!
$@c{angry_mopen} , .\n@c{smile_eclosed} .
$@c{smile_ehalf} , .
$@c{smile} .\n때로는 .
$@c{angry_mopen}, ,\n너도 !`
},
"victory": {
1: `@c{neutral}I… wasn't supposed to lose that time
$@c{smile}Aw well. That just means I'll have to train even harder for next time!
$@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~.
$@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this!
$@c{smile_wave}Keep at it!`
1: `@c{neutral}이… 번에도 져버릴 줄이야
$@c{smile}, . !
$@c{smile_wave} !\n@c{smile_wave_wink} ~.
$@c{angry_mopen}, !\n또 !
$@c{smile_wave}!`
},
"defeat": {
1: "It's OK to lose sometimes…"
1: "가끔은 지는 것도 괜찮아…"
}
},
"rival_3": {
"encounter": {
1: `@c{smile}Hey, look who it is! It's been a while.\n@c{neutral}You're… still undefeated? Huh.
$@c{neutral_eclosed}Things have been kind of strange.\nIt's not the same back home without you.
$@c{serious}I know it's selfish, but I need to get this off my chest.\n@c{neutral_eclosed}I think you're in over your head here.
$@c{serious}Never losing once is just unrealistic.\nWe need to lose sometimes in order to grow.
$@c{neutral_eclosed}You've had a great run but there's still so much ahead, and it only gets harder. @c{neutral}Are you prepared for that?
$@c{serious_mopen_fists}If so, prove it to me.`
1: `@c{smile}이게 누구야! 오랜만이야.\n@c{neutral}역시나… 쭉 이기고 있었구나?
$@c{neutral_eclosed} . \n혼자 .
$@c{serious} .\n@c{neutral_eclosed} .
$@c{serious} .\n사람은 .
$@c{neutral_eclosed} ,\n갈 . @c{neutral} ?
$@c{serious_mopen_fists}, .`
},
"victory": {
1: "@c{angry_mhalf}This is ridiculous… I've hardly stopped training…\nHow are we still so far apart?"
1: "@c{angry_mhalf}말도 안 돼… 한시도 쉬지 않고 훈련했는데…\n어째서 전혀 닿을 수 없는거야?"
},
},
"rival_3_female": {
"encounter": {
1: `@c{smile_wave}Long time no see! Still haven't lost, huh.\n@c{angry}You're starting to get on my nerves. @c{smile_wave_wink}Just kidding!
$@c{smile_ehalf}But really, don't you miss home by now? Or… me?\nI… I mean, we've really missed you.
$@c{smile_eclosed}I support you in your dream and everything, but the reality is you're going to lose sooner or later.
$@c{smile}And when you do, I'll be there for you like always.\n@c{angry_mopen}Now, let me show you how strong I've become!`
1: `@c{smile_wave}오랜만이야! 아직도 안 졌구나.\n@c{angry}거슬리게 말야. @c{smile_wave_wink}농담이야!
$@c{smile_ehalf} , ? ?\n나 , .
$@c{smile_eclosed} ,\n넌 .
$@c{smile} .\n@c{angry_mopen} , !`
},
"victory": {
1: "@c{shock}After all that… it wasn't enough…?\nYou'll never come back at this rate…"
1: "@c{shock}이렇게까지 했는데도… 모자랐던 거야…?\n이러면 정말로 너는 이제 영영…"
},
"defeat": {
1: "You gave it your best, now let's go home."
1: "최선을 다 했지.\n집에 갈 시간이야."
}
},
"rival_4": {
"encounter": {
1: `@c{neutral}Hey.
$I won't mince words or pleasantries with you.\n@c{neutral_eclosed}I'm here to win, plain and simple.
$@c{serious_mhalf_fists}I've learned to maximize my potential by putting all my time into training.
$@c{smile}You get a lot of extra time when you cut out the unnecessary sleep and social interaction.
$@c{serious_mopen_fists}None of that matters anymore, not until I win.
$@c{neutral_eclosed}I've even reached the point where I don't lose anymore.\n@c{smile_eclosed}I suppose your philosophy wasn't so wrong after all.
$@c{angry_mhalf}Losing is for the weak, and I'm not weak anymore.
$@c{serious_mopen_fists}Prepare yourself.`
1: `@c{neutral}안녕.
$잡담은 .\n@c{neutral_eclosed} . .
$@c{serious_mhalf_fists} .
$@c{smile} .\n줄일 .
$@c{serious_mopen_fists} .\n이제 .
$@c{neutral_eclosed} .\n@c{smile_eclosed} .
$@c{angry_mhalf} , .
$@c{serious_mopen_fists}.`
},
"victory": {
1: "@c{neutral}What…@d{64} What are you?"
1: "@c{neutral}너…@d{64} 너 대체 뭐야?"
},
},
"rival_4_female": {
"encounter": {
1: `@c{neutral}It's me! You didn't forget about me again… did you?
$@c{smile}You should be proud of how far you made it. Congrats!\nBut it looks like it's the end of your journey.
$@c{smile_eclosed}You've awoken something in me I never knew was there.\nIt seems like all I do now is train.
$@c{smile_ehalf}I hardly even eat or sleep now, I just train my Pokémon all day, getting stronger every time.
$@c{neutral}In fact, I hardly recognize myself.
$And now, I've finally reached peak performance.\nI don't think anyone could beat me now.
$And you know what? It's all because of you.\n@c{smile_ehalf}I don't know whether to thank you or hate you.
$@c{angry_mopen}Prepare yourself.`
1: `@c{neutral}나야! 날 잊어버리고 있던 건… 아니지?
$@c{smile} . !\n하지만 .
$@c{smile_eclosed} .\n이제 .
$@c{smile_ehalf} , .\n더 .
$@c{neutral}, .
$그렇지만 , .\n아무도 .
$그거 ? .\n@c{smile_ehalf} .
$@c{angry_mopen}.`
},
"victory": {
1: "@c{neutral}What…@d{64} What are you?"
1: "@c{neutral}너…@d{64} 너 대체 뭐야?"
},
"defeat": {
1: "$@c{smile}You should be proud of how far you made it."
1: "$@c{smile}네가 키운 상대야. 자랑스럽지?"
}
},
"rival_5": {
@ -2231,47 +2240,46 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"rival_6": {
"encounter": {
1: `@c{smile_eclosed}We meet again.
$@c{neutral}I've had some time to reflect on all this.\nThere's a reason this all seems so strange.
$@c{neutral_eclosed}Your dream, my drive to beat you\nIt's all a part of something greater.
$@c{serious}This isn't about me, or about you… This is about the world, @c{serious_mhalf_fists}and it's my purpose to push you to your limits.
$@c{neutral_eclosed}Whether I've fulfilled that purpose I can't say, but I've done everything in my power.
$@c{neutral}This place we ended up in is terrifying Yet somehow I feel unphased, like I've been here before.
$@c{serious_mhalf_fists}You feel the same, don't you?
$@c{serious}and it's like something here is speaking to me.\nThis is all the world's known for a long time now.
$Those times we cherished together that seem so recent are nothing but a distant memory.
$@c{neutral_eclosed}Who can say whether they were ever even real in the first place.
$@c{serious_mopen_fists}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this.
$@c{serious_smile_fists}I hardly know what any of this means, I just know that it's true.
$@c{serious_mopen_fists}If you can't defeat me here and now, you won't stand a chance.`
1: `@c{smile_eclosed}다시 만났구나.
$@c{neutral} .\n이 .
$@c{neutral_eclosed} , \n이건 .
$@c{serious} \n@c{serious_mhalf_fists} .
$@c{neutral_eclosed} .
$@c{neutral} \n언젠가 .
$@c{serious_mhalf_fists} ?
$@c{serious} .\n오랫동안 .
$우리가 .
$@c{neutral_eclosed} ?
$@c{serious_mopen_fists} . .\n오직 .
$@c{serious_smile_fists} , .
$@c{serious_mopen_fists} , .`
},
"victory": {
1: `@c{smile_eclosed}It looks like my work is done here.
$I want you to promise me one thing.\n@c{smile}After you heal the world, please come home.`
1: `@c{smile_eclosed}내 역할은 여기까진 것 같아.
$하나만 .\n@c{smile} , .`
},
},
"rival_6_female": {
"encounter": {
1: `@c{smile_ehalf}So it's just us again.
$@c{smile_eclosed}You know, I keep going around and around in my head
$@c{smile_ehalf}There's something to all this, why everything seems so strange now
$@c{smile}You have your dream, and I have this ambition in me
$I just can't help but feel there's a greater purpose to all this, to what we're doing, you and I.
$@c{smile_eclosed}I think I'm supposed to push you to your limits.
$@c{smile_ehalf}I'm not sure if I've been doing a good job at that, but I've tried my best up to now.
$It's something about this strange and dreadful place Everything seems so clear
$This is all the world's known for a long time now.
$@c{smile_eclosed}It's like I can barely remember the memories we cherished together.
$@c{smile_ehalf}Were they even real? They seem so far away now
$@c{angry_mopen}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this.
$@c{smile_ehalf}I don't know what all this means… but I feel it's true.
$@c{neutral}If you can't defeat me here and now, you won't stand a chance.`
1: `@c{smile_ehalf}또 이렇게 만났네.
$@c{smile_eclosed} 릿
$@c{smile_ehalf} .\n분명
$@c{smile} ,\n나는
$, \n더 .
$@c{smile_eclosed} \n한계까지 .
$@c{smile_ehalf} , .
$ \n모든게
$이건 .
$@c{smile_eclosed} , .
$@c{smile_ehalf} ?
$@c{angry_mopen} . , . \n오직 .
$@c{smile_ehalf} \n이거 .
$@c{neutral} , .`
},
"victory": {
1: `@c{smile_ehalf}I… I think I fulfilled my purpose…
$@c{smile_eclosed}Promise me After you heal the world Please come home safe.
$@c{smile_ehalf}Thank you.`
1: `@c{smile_ehalf}나… 내 소임을 다 한 기분이야…
$@c{smile_eclosed} \n집으로 .
$@c{smile_ehalf}.`
},
},
};
@ -2282,17 +2290,17 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue;
// Dialogue of the endboss of the game when the player character is male (Or unset)
export const PGMbattleSpecDialogue: SimpleTranslationEntries = {
"encounter": `It appears the time has finally come once again.\nYou know why you have come here, do you not?
$You were drawn here, because you have been here before.\nCountless times.
$Though, perhaps it can be counted.\nTo be precise, this is in fact your 5,643,853rd cycle.
$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.
$Until now you have yet to succeed, but I sense a different presence in you this time.\n
$You are the only one here, though it is as if there is another.
$Will you finally prove a formidable challenge to me?\nThe challenge I have longed for for millennia?
$We begin.`,
"firstStageWin": `I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.
$Do not disappoint me.`,
"secondStageWin": "…Magnificent."
"encounter": `드디어 때가 다시 도래했다.\n당도한 연유를 아는가?
$이미 .\n셀 .
$아니, .\n정확히 5,643,853.
$ .\n하지만 , .
$실패만을 ,\n지금은 .\n
$홀로 ,
$마침내 ?\n수천 ?
$시작하지.`,
"firstStageWin": `그렇군. 정말로 있었어.\n이제 주저할 필요는 없다.
$실망시키지 .`,
"secondStageWin": "…고무적이군."
};
// Dialogue of the endboss of the game when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMbattleSpecDialogue.
@ -2301,22 +2309,22 @@ export const PGFbattleSpecDialogue: SimpleTranslationEntries = PGMbattleSpecDial
// Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is male
export const PGMmiscDialogue: SimpleTranslationEntries = {
"ending":
`@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.
$@c{smile}It's over.@d{64} You ended the loop.
$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.
$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?
$@c{serious_smile_fists}Your legend will always live on in our hearts.
$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.
$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.`,
`@c{smile}오? 이긴거야?@d{96} @c{smile_eclosed}진즉 알았어야 했는데.\n아무튼, 돌아왔구나.
$@c{smile} .@d{64} .
$@c{serious_smile_fists} .\n진짜로 .
$@c{neutral} .@d{96}\n그래도, ?
$@c{serious_smile_fists} \n너와 .
$@c{smile_eclosed} \n이제 .
$@c{serious_smile_fists}, ?\n네가 .`,
"ending_female":
`@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.
$@c{smile_eclosed}Of course I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.
$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.
$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!
$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.
$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.
$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?`,
"ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.",
`@c{shock}돌아왔구나?@d{32} 그 말은…@d{96} 이겼어?!\n@c{smile_ehalf}그럴 줄 알았다니까.
$@c{smile_eclosed} .\n@c{smile} , ? .
$@c{smile_ehalf} .\n어떻게 ?
$네가 .\n@c{angry_mopen}, !
$@c{smile_wave_wink}!@d{64} @c{smile} .@d{32}\n오늘 .
$@c{smile_wave},@d{64} @d{96}\n이런 ?
$집에 . @c{smile_wave_wink} ,\n추억을 .`,
"ending_endless": "끝에 도달하신 것을 축하드립니다!\n더 많은 컨텐츠를 기다려주세요.",
"ending_name": "Devs"
};
// Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMmiscDialogue.

View File

@ -11,6 +11,7 @@ export const menu: SimpleTranslationEntries = {
"dailyRun": "데일리 런 (베타)",
"loadGame": "불러오기",
"newGame": "새 게임",
"settings": "설정",
"selectGameMode": "게임 모드를 선택해주세요.",
"logInOrCreateAccount": "로그인 또는 등록을 해 주세요. 개인정보를 요구하지 않습니다!",
"username": "이름",

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "역무원",
"doctor": "의사",
"doctor_female": "간호사", // doctor_f.png 파일이 간호사
"firebreather": "Firebreather",
"fisherman": "낚시꾼",
"fisherman_female": "낚시꾼",
"gentleman": "신사",

View File

@ -12,7 +12,7 @@ export const tutorial: SimpleTranslationEntries = {
$설정에서 , .
$여기에는 !`,
starterSelect: `이 화면에서 스타팅을 선택할 수 있습니다.\n이들은 당신의 첫 번째 파티 멤버들입니다.
starterSelect: `이 화면에서 Z나 스페이스바로 스타팅을 선택할 수 있습니다.\n이들은 당신의 첫 번째 파티 멤버들입니다.
$최대 6 \n포켓몬에 10 .
$계속 \n선택할 , , .
$개체값도 ,\n같은 !`,
@ -22,7 +22,8 @@ export const tutorial: SimpleTranslationEntries = {
statChange: `포켓몬은 교체하지 않으면 다음 전투에서도 능력치 변화가 유지됩니다.
$대신 .
$C Shift를 .`,
$C Shift를 .
$V를 .`,
selectItem: `전투가 끝날때마다 무작위 아이템 3개 중 하나를 선택하여 얻습니다.
$종류는 , , .

View File

@ -3,4 +3,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{abilityName}} de {{pokemonName}}\nprotegeu-o do dano de recuo!",
"badDreams": "{{pokemonName}} está tendo pesadelos!",
"windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!"
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "Ferroviário",
"doctor": "Doutor",
"doctor_female": "Doutora",
"firebreather": "Firebreather",
"fishermen": "Pescador",
"fishermen_female": "Pescadora",
"gentleman": "Cavalheiro",

View File

@ -2,5 +2,6 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}} 的 {{abilityName}}\n抵消了反作用力",
"badDreams": "{{pokemonName}} 被折磨着!"
"badDreams": "{{pokemonName}} 被折磨着!",
"windPowerCharged": "受 {{moveName}} 的影响, {{pokemonName}} 提升了能力!"
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "铁路员工",
"doctor": "医生",
"doctor_female": "医生",
"firebreather": "Firebreather",
"fisherman": "垂钓者",
"fisherman_female": "垂钓者",
"gentleman": "绅士",

View File

@ -3,4 +3,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}} 的 {{abilityName}}\n抵消了反作用力!",
"badDreams": "{{pokemonName}} 被折磨着!",
"windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!"
} as const;

View File

@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
1: "I'll tune up for the next race."
},
},
"firebreather": {
"encounter": {
1: "My flames shall devour you!",
2: "My soul is on fire. I'll show you how hot it burns!",
3: "Step right up and take a look!"
},
"victory": {
1: "I burned down to ashes...",
2: "Yow! That's hot!",
3: "Ow! I scorched the tip of my nose!"
},
},
"brock": {
"encounter": {
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
@ -1203,8 +1215,7 @@ export const PGMdialogue: DialogueTranslationEntries = {
},
"bugsy": {
"encounter": {
1: `Whoa, amazing! You're an expert on Pokémon!
$My research isn't complete yet. OK, you win.`,
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
},
"victory": {
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."

View File

@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
"depot_agent": "鐵路員工",
"doctor": "醫生",
"doctor_female": "醫生",
"firebreather": "Firebreather",
"fisherman": "垂釣者",
"fisherman_female": "垂釣者",
"gentleman": "紳士",

View File

@ -1,6 +1,7 @@
import * as Modifiers from "./modifier";
import { AttackMove, allMoves } from "../data/move";
import { Moves } from "../data/enums/moves";
import { Abilities } from "../data/enums/abilities";
import { PokeballType, getPokeballCatchMultiplier, getPokeballName } from "../data/pokeball";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "../field/pokemon";
import { EvolutionItem, pokemonEvolutions } from "../data/pokemon-evolutions";
@ -756,14 +757,23 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
}
}
/**
* Class that represents form changing items
*/
export class FormChangeItemModifierType extends PokemonModifierType implements GeneratedPersistentModifierType {
public formChangeItem: FormChangeItem;
constructor(formChangeItem: FormChangeItem) {
super("", FormChangeItem[formChangeItem].toLowerCase(), (_type, args) => new Modifiers.PokemonFormChangeItemModifier(this, (args[0] as PlayerPokemon).id, formChangeItem, true),
(pokemon: PlayerPokemon) => {
if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && !!pokemonFormChanges[pokemon.species.speciesId].find(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger)
&& (fc.trigger as SpeciesFormChangeItemTrigger).item === this.formChangeItem)) {
// Make sure the Pokemon has alternate forms
if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId)
// Get all form changes for this species with an item trigger, including any compound triggers
&& pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger))
// Returns true if any form changes match this item
.map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
.flat().flatMap(fc => fc.item).includes(this.formChangeItem)
) {
return null;
}
@ -1182,6 +1192,9 @@ export const modifierTypes = {
LEFTOVERS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEFTOVERS", "leftovers", (type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)),
SHELL_BELL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SHELL_BELL", "shell_bell", (type, args) => new Modifiers.HitHealModifier(type, (args[0] as Pokemon).id)),
TOXIC_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.TOXIC_ORB", "toxic_orb", (type, args) => new Modifiers.TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
FLAME_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.FLAME_ORB", "flame_orb", (type, args) => new Modifiers.TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
BATON: () => new PokemonHeldItemModifierType("modifierType:ModifierType.BATON", "stick", (type, args) => new Modifiers.SwitchEffectTransferModifier(type, (args[0] as Pokemon).id)),
SHINY_CHARM: () => new ModifierType("modifierType:ModifierType.SHINY_CHARM", "shiny_charm", (type, _args) => new Modifiers.ShinyRateBoosterModifier(type)),
@ -1247,7 +1260,12 @@ const modifierPool: ModifierPool = {
[ModifierTier.GREAT]: [
new WeightedModifierType(modifierTypes.GREAT_BALL, 6),
new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status).length, 3);
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
if (i instanceof Modifiers.TurnStatusEffectModifier) {
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect;
}
return false;
})).length, 3);
return statusEffectPartyMemberCount * 6;
}, 18),
new WeightedModifierType(modifierTypes.REVIVE, (party: Pokemon[]) => {
@ -1270,7 +1288,12 @@ const modifierPool: ModifierPool = {
return thresholdPartyMemberCount;
}, 3),
new WeightedModifierType(modifierTypes.FULL_RESTORE, (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status).length, 3);
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
if (i instanceof Modifiers.TurnStatusEffectModifier) {
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect;
}
return false;
})).length, 3);
const thresholdPartyMemberCount = Math.floor((Math.min(party.filter(p => (p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5) && !p.isFainted()).length, 3) + statusEffectPartyMemberCount) / 2);
return thresholdPartyMemberCount;
}, 3),
@ -1312,6 +1335,40 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.MINT, 4),
new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32),
new WeightedModifierType(modifierTypes.AMULET_COIN, 3),
new WeightedModifierType(modifierTypes.TOXIC_ORB, (party: Pokemon[]) => {
let weight = 0;
const filteredParty = party.filter(p => (p.status?.effect === StatusEffect.TOXIC || p.canSetStatus(StatusEffect.TOXIC, true, true))
&& !p.hasAbility(Abilities.FLARE_BOOST)
&& !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier));
if (filteredParty.some(p => p.hasAbility(Abilities.TOXIC_BOOST) || p.hasAbility(Abilities.POISON_HEAL))) {
weight = 4;
} else if (filteredParty.some(p => p.hasAbility(Abilities.GUTS) || p.hasAbility(Abilities.QUICK_FEET) || p.hasAbility(Abilities.MARVEL_SCALE))) {
weight = 2;
} else {
const moveList = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT];
if (filteredParty.some(p => p.getMoveset().some(m => moveList.includes(m.moveId)))) {
weight = 1;
}
}
return Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * weight, 8 * weight);
}, 32),
new WeightedModifierType(modifierTypes.FLAME_ORB, (party: Pokemon[]) => {
let weight = 0;
const filteredParty = party.filter(p => (p.status?.effect === StatusEffect.BURN || p.canSetStatus(StatusEffect.BURN, true, true))
&& !p.hasAbility(Abilities.TOXIC_BOOST) && !p.hasAbility(Abilities.POISON_HEAL)
&& !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier));
if (filteredParty.some(p => p.hasAbility(Abilities.FLARE_BOOST))) {
weight = 4;
} else if (filteredParty.some(p => p.hasAbility(Abilities.GUTS) || p.hasAbility(Abilities.QUICK_FEET) || p.hasAbility(Abilities.MARVEL_SCALE))) {
weight = 2;
} else {
const moveList = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT];
if (filteredParty.some(p => p.getMoveset().some(m => moveList.includes(m.moveId)))) {
weight = 1;
}
}
return Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * weight, 8 * weight);
}, 32),
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4),
new WeightedModifierType(modifierTypes.CANDY_JAR, 5),
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),

View File

@ -851,6 +851,66 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
}
}
/**
* Modifier used for held items, namely Toxic Orb and Flame Orb, that apply a
* set {@linkcode StatusEffect} at the end of a turn.
* @extends PokemonHeldItemModifier
* @see {@linkcode apply}
*/
export class TurnStatusEffectModifier extends PokemonHeldItemModifier {
/** The status effect to be applied by the held item */
private effect: StatusEffect;
constructor (type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
switch (type.id) {
case "TOXIC_ORB":
this.effect = StatusEffect.TOXIC;
break;
case "FLAME_ORB":
this.effect = StatusEffect.BURN;
break;
}
}
/**
* Checks if {@linkcode modifier} is an instance of this class,
* intentionally ignoring potentially different {@linkcode effect}s
* to prevent held item stockpiling since the item obtained first
* would be the only item able to {@linkcode apply} successfully.
* @override
* @param modifier {@linkcode Modifier} being type tested
* @return true if {@linkcode modifier} is an instance of
* TurnStatusEffectModifier, false otherwise
*/
matchType(modifier: Modifier): boolean {
return modifier instanceof TurnStatusEffectModifier;
}
clone() {
return new TurnStatusEffectModifier(this.type, this.pokemonId, this.stackCount);
}
/**
* Tries to inflicts the holder with the associated {@linkcode StatusEffect}.
* @param args [0] {@linkcode Pokemon} that holds the held item
* @returns true if the status effect was applied successfully, false if
* otherwise
*/
apply(args: any[]): boolean {
return (args[0] as Pokemon).trySetStatus(this.effect, true, undefined, undefined, this.type.name);
}
getMaxHeldItemCount(pokemon: Pokemon): integer {
return 1;
}
getStatusEffect(): StatusEffect {
return this.effect;
}
}
export class HitHealModifier extends PokemonHeldItemModifier {
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
@ -1868,7 +1928,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
}
const randItemIndex = pokemon.randSeedInt(itemModifiers.length);
const randItem = itemModifiers[randItemIndex];
heldItemTransferPromises.push(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false, false).then(success => {
heldItemTransferPromises.push(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false).then(success => {
if (success) {
transferredModifierTypes.push(randItem.type);
itemModifiers.splice(randItemIndex, 1);

View File

@ -14,6 +14,7 @@ import { PokeballType } from "./data/pokeball";
import {TimeOfDay} from "#app/data/enums/time-of-day";
import { Gender } from "./data/gender";
import { StatusEffect } from "./data/status-effect";
import { modifierTypes } from "./modifier/modifier-type";
/**
* Overrides for testing different in game situations
@ -100,7 +101,7 @@ export const OPP_VARIANT_OVERRIDE: Variant = 0;
* - BerryType is for BERRY
*/
interface ModifierOverride {
name: string,
name: keyof typeof modifierTypes & string,
count?: integer
type?: TempBattleStat|Stat|Nature|Type|BerryType
}

View File

@ -6,7 +6,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov
import { Mode } from "./ui/ui";
import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat";
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, PokemonMoveAccuracyBoosterModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier } from "./modifier/modifier";
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, PokemonMoveAccuracyBoosterModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier } from "./modifier/modifier";
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
@ -1513,7 +1513,7 @@ export class SwitchSummonPhase extends SummonPhase {
const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier
&& (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier;
if (batonPassModifier && !this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedPokemon.id)) {
this.scene.tryTransferHeldItemModifier(batonPassModifier, switchedPokemon, false, false);
this.scene.tryTransferHeldItemModifier(batonPassModifier, switchedPokemon, false);
}
}
}
@ -1548,11 +1548,16 @@ export class SwitchSummonPhase extends SummonPhase {
super.onEnd();
const pokemon = this.getPokemon();
const moveId = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.move?.move;
const moveId = this.lastPokemon?.scene.currentBattle.lastMove;
const lastUsedMove = moveId ? allMoves[moveId] : undefined;
const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command;
const lastPokemonIsForceSwitchedAndNotFainted = !!lastUsedMove?.findAttr(attr => attr instanceof ForceSwitchOutAttr) && !this.lastPokemon.isFainted();
// Compensate for turn spent summoning
if (pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command === Command.POKEMON || !!lastUsedMove?.findAttr(attr => attr instanceof ForceSwitchOutAttr)) { //check if hard switch OR pivot move was used
// Or compensate for force switch move if switched out pokemon is not fainted
if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) {
pokemon.battleSummonData.turnCount--;
}
@ -2284,6 +2289,8 @@ export class TurnEndPhase extends FieldPhase {
applyPostTurnAbAttrs(PostTurnAbAttr, pokemon);
this.scene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon);
this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon);
pokemon.battleSummonData.turnCount++;
@ -4895,14 +4902,12 @@ export class SelectModifierPhase extends BattlePhase {
this.scene.playSound("buy");
}
} else if (cursor === 1) {
this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, toSlotIndex: integer) => {
this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => {
if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) {
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)).then(() => {
const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[];
const itemModifier = itemModifiers[itemIndex];
this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, true);
});
const itemModifier = itemModifiers[itemIndex];
this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity);
} else {
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
}

View File

@ -30,6 +30,7 @@ import { allMoves } from "../data/move";
import { TrainerVariant } from "../field/trainer";
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
import { Variant, variantData } from "#app/data/variant";
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
@ -740,6 +741,10 @@ export class GameData {
});
scene.arena.weather = sessionData.arena.weather;
scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(null, scene.arena.weather?.weatherType, scene.arena.weather?.turnsLeft));
scene.arena.terrain = sessionData.arena.terrain;
scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(null, scene.arena.terrain?.terrainType, scene.arena.terrain?.turnsLeft));
// TODO
//scene.arena.tags = sessionData.arena.tags;

View File

@ -57,7 +57,7 @@ export enum PartyOption {
}
export type PartySelectCallback = (cursor: integer, option: PartyOption) => void;
export type PartyModifierTransferSelectCallback = (fromCursor: integer, index: integer, toCursor?: integer) => void;
export type PartyModifierTransferSelectCallback = (fromCursor: integer, index: integer, itemQuantity?: integer, toCursor?: integer) => void;
export type PartyModifierSpliceSelectCallback = (fromCursor: integer, toCursor?: integer) => void;
export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string;
export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => string;
@ -87,6 +87,10 @@ export default class PartyUiHandler extends MessageUiHandler {
private transferMode: boolean;
private transferOptionCursor: integer;
private transferCursor: integer;
/** Current quantity selection for every item held by the pokemon selected for the transfer */
private transferQuantities: integer[];
/** Stack size of every item that the selected pokemon is holding */
private transferQuantitiesMax: integer[];
private lastCursor: integer = 0;
private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback;
@ -231,8 +235,8 @@ export default class PartyUiHandler extends MessageUiHandler {
let success = false;
if (this.optionsMode) {
const option = this.options[this.optionsCursor];
if (button === Button.ACTION) {
const option = this.options[this.optionsCursor];
const pokemon = this.scene.getParty()[this.cursor];
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode && option !== PartyOption.CANCEL) {
this.startTransfer();
@ -270,7 +274,9 @@ export default class PartyUiHandler extends MessageUiHandler {
}
if (this.selectCallback) {
if (option === PartyOption.TRANSFER) {
(this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, this.transferOptionCursor, this.cursor);
if (this.transferCursor !== this.cursor) {
(this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, this.transferOptionCursor, this.transferQuantities[this.transferOptionCursor], this.cursor);
}
this.clearTransfer();
} else if (this.partyUiMode === PartyUiMode.SPLICE) {
if (option === PartyOption.SPLICE) {
@ -369,17 +375,50 @@ export default class PartyUiHandler extends MessageUiHandler {
return true;
} else {
switch (button) {
case Button.LEFT:
/** Decrease quantity for the current item and update UI */
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
this.transferQuantities[option] = this.transferQuantities[option] === 1 ? this.transferQuantitiesMax[option] : this.transferQuantities[option] - 1;
this.updateOptions();
success = this.setCursor(this.optionsCursor); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
}
break;
case Button.RIGHT:
/** Increase quantity for the current item and update UI */
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
this.transferQuantities[option] = this.transferQuantities[option] === this.transferQuantitiesMax[option] ? 1 : this.transferQuantities[option] + 1;
this.updateOptions();
success = this.setCursor(this.optionsCursor); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
}
break;
case Button.UP:
success = this.setCursor(this.optionsCursor ? this.optionsCursor - 1 : this.options.length - 1);
/** If currently selecting items to transfer, reset quantity selection */
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
this.transferQuantities[option] = this.transferQuantitiesMax[option];
this.updateOptions();
}
success = this.setCursor(this.optionsCursor ? this.optionsCursor - 1 : this.options.length - 1); /** Move cursor */
break;
case Button.DOWN:
success = this.setCursor(this.optionsCursor < this.options.length - 1 ? this.optionsCursor + 1 : 0);
/** If currently selecting items to transfer, reset quantity selection */
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
this.transferQuantities[option] = this.transferQuantitiesMax[option];
this.updateOptions();
}
success = this.setCursor(this.optionsCursor < this.options.length - 1 ? this.optionsCursor + 1 : 0); /** Move cursor */
break;
}
}
} else {
if (button === Button.ACTION) {
if (this.cursor < 6) {
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) {
/** Initialize item quantities for the selected Pokemon */
const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === this.scene.getParty()[this.cursor].id) as PokemonHeldItemModifier[];
this.transferQuantities = itemModifiers.map(item => item.getStackCount());
this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount());
}
this.showOptions();
ui.playSelect();
} else if (this.partyUiMode === PartyUiMode.FAINT_SWITCH || this.partyUiMode === PartyUiMode.REVIVAL_BLESSING) {
@ -555,7 +594,7 @@ export default class PartyUiHandler extends MessageUiHandler {
break;
case PartyUiMode.MODIFIER_TRANSFER:
if (!this.transferMode) {
optionsMessage = "Select a held item to transfer.";
optionsMessage = "Select a held item to transfer.\nUse < and > to change the quantity.";
}
break;
case PartyUiMode.SPLICE:
@ -569,7 +608,12 @@ export default class PartyUiHandler extends MessageUiHandler {
this.updateOptions();
this.partyMessageBox.setSize(262 - Math.max(this.optionsBg.displayWidth - 56, 0), 30);
/** When an item is being selected for transfer, the message box is taller as the message occupies two lines */
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
this.partyMessageBox.setSize(262 - Math.max(this.optionsBg.displayWidth - 56, 0), 42);
} else {
this.partyMessageBox.setSize(262 - Math.max(this.optionsBg.displayWidth - 56, 0), 30);
}
this.setCursor(0);
}
@ -741,8 +785,9 @@ export default class PartyUiHandler extends MessageUiHandler {
} else {
const itemModifier = itemModifiers[option];
optionName = itemModifier.type.name;
if (itemModifier.stackCount > 1) {
optionName += ` (${itemModifier.stackCount})`;
/** For every item that has stack bigger than 1, display the current quantity selection */
if (this.transferQuantitiesMax[option] > 1) {
optionName += ` (${this.transferQuantities[option]})`;
}
}

View File

@ -1339,11 +1339,27 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
case Button.UP:
if (row) {
success = this.setCursor(this.cursor - 9);
} else {
// when strictly opposite starter based on rows length
// does not exits, set cursor on the second to last row
if (this.cursor + (rows - 1) * 9 > genStarters - 1) {
success = this.setCursor(this.cursor + (rows - 2) * 9);
} else {
success = this.setCursor(this.cursor + (rows - 1) * 9);
}
}
break;
case Button.DOWN:
if (row < rows - 2 || (row < rows - 1 && this.cursor % 9 <= (genStarters - 1) % 9)) {
success = this.setCursor(this.cursor + 9);
} else {
// if there is no starter below while being on the second to
// last row, adjust cursor position with one line less
if (row === rows - 2 && this.cursor + 9 > genStarters - 1) {
success = this.setCursor(this.cursor - (rows - 2) * 9);
} else {
success = this.setCursor(this.cursor - (rows - 1) * 9);
}
}
break;
case Button.LEFT:

View File

@ -68,7 +68,14 @@ export class StatsContainer extends Phaser.GameObjects.Container {
this.statsIvsCache = ivChartData.slice(0);
this.ivStatValueTexts.map((t: BBCodeText, i: integer) => {
let label = ivs[i].toString();
let label = "";
// Check to see if IVs are 31, if so change the text style to gold, otherwise leave them be.
if (ivs[i] === 31) {
label += `[color=${getTextColor(TextStyle.SUMMARY_GOLD, false, (this.scene as BattleScene).uiTheme)}][shadow]${ivs[i].toString()}[/shadow][/color]`;
} else {
label = ivs[i].toString();
}
if (this.showDiff && originalIvs) {
if (originalIvs[i] < ivs[i]) {
label += ` ([color=${getTextColor(TextStyle.SUMMARY_BLUE, false, (this.scene as BattleScene).uiTheme)}][shadow=${getTextColor(TextStyle.SUMMARY_BLUE, true, (this.scene as BattleScene).uiTheme)}]+${ivs[i] - originalIvs[i]}[/shadow][/color])`;

View File

@ -407,6 +407,7 @@ export function verifyLang(lang?: string): boolean {
case "zh_CN":
case "zh_TW":
case "pt_BR":
case "ko":
return true;
default:
return false;

11
src/vite.env.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_BYPASS_LOGIN?: string;
readonly VITE_BYPASS_TUTORIAL?: string;
readonly VITE_API_BASE_URL?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

View File

@ -16,5 +16,10 @@
},
"outDir": "./build",
"noEmit": true
}
},
"typedocOptions": {
"entryPoints": ["src/"],
"entryPointStrategy": "expand",
"out": "typedoc",
}
}

11
vite.env.d.ts vendored
View File

@ -1,11 +0,0 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_BYPASS_LOGIN: string;
readonly VITE_BYPASS_TUTORIAL: string;
readonly VITE_API_BASE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}