Merge branch 'beta' into ChampionAdjustments
@ -17,7 +17,7 @@
|
||||
"typecheck": "tsc --noEmit",
|
||||
"eslint": "eslint --fix .",
|
||||
"eslint-ci": "eslint .",
|
||||
"biome": "biome check --write --changed --no-errors-on-unmatched",
|
||||
"biome": "biome check --write --changed --no-errors-on-unmatched --diagnostic-level=error",
|
||||
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
|
||||
"docs": "typedoc",
|
||||
"depcruise": "depcruise src test",
|
||||
|
188
public/images/statuses_tl.json
Normal file
@ -0,0 +1,188 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "statuses_tl.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 22,
|
||||
"h": 64
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "pokerus",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 22,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 22,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 22,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "burn",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "faint",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 16,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "freeze",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 24,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "paralysis",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 32,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "poison",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 40,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "sleep",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 48,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "toxic",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 56,
|
||||
"w": 20,
|
||||
"h": 8
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$"
|
||||
}
|
||||
}
|
BIN
public/images/statuses_tl.png
Normal file
After Width: | Height: | Size: 419 B |
440
public/images/types_tl.json
Normal file
@ -0,0 +1,440 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "types_tl.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_tl.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
public/images/ui/legacy/party_bg_double_manage.png
Normal file
After Width: | Height: | Size: 431 B |
62
public/images/ui/legacy/party_discard.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "party_discard.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 75,
|
||||
"h": 50
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "normal",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "selected",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 25,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:17219773dfffd6b1204d988fea3f9462:1127ad21d64bc7ebb9df4fc28f3d2d39:7ad46e8fb4648c3d3d84a746ecb371ea$"
|
||||
}
|
||||
}
|
BIN
public/images/ui/legacy/party_discard.png
Normal file
After Width: | Height: | Size: 346 B |
62
public/images/ui/legacy/party_transfer.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "party_transfer.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 75,
|
||||
"h": 50
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "normal",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "selected",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 25,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:17219773dfffd6b1204d988fea3f9462:1127ad21d64bc7ebb9df4fc28f3d2d39:7ad46e8fb4648c3d3d84a746ecb371ea$"
|
||||
}
|
||||
}
|
BIN
public/images/ui/legacy/party_transfer.png
Normal file
After Width: | Height: | Size: 366 B |
BIN
public/images/ui/party_bg_double_manage.png
Normal file
After Width: | Height: | Size: 837 B |
62
public/images/ui/party_discard.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "party_discard.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 75,
|
||||
"h": 50
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "normal",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "selected",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 25,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:17219773dfffd6b1204d988fea3f9462:1127ad21d64bc7ebb9df4fc28f3d2d39:7ad46e8fb4648c3d3d84a746ecb371ea$"
|
||||
}
|
||||
}
|
BIN
public/images/ui/party_discard.png
Normal file
After Width: | Height: | Size: 386 B |
62
public/images/ui/party_transfer.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "party_transfer.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 75,
|
||||
"h": 50
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "normal",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "selected",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 25,
|
||||
"w": 75,
|
||||
"h": 25
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:17219773dfffd6b1204d988fea3f9462:1127ad21d64bc7ebb9df4fc28f3d2d39:7ad46e8fb4648c3d3d84a746ecb371ea$"
|
||||
}
|
||||
}
|
BIN
public/images/ui/party_transfer.png
Normal file
After Width: | Height: | Size: 403 B |
@ -1 +1 @@
|
||||
Subproject commit e2fbba17ea7a96068970ea98a8a84ed3e25b6f07
|
||||
Subproject commit 7898c0018a70601a6ead76c9dd497ff966cc2e2a
|
@ -2845,6 +2845,23 @@ export class BattleScene extends SceneBase {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Attempt to discard one or more copies of a held item.
|
||||
* @param itemModifier - The {@linkcode PokemonHeldItemModifier} being discarded
|
||||
* @param discardQuantity - The number of copies to remove (up to the amount currently held); default `1`
|
||||
* @returns Whether the item was successfully discarded.
|
||||
* Removing fewer items than requested is still considered a success.
|
||||
*/
|
||||
tryDiscardHeldItemModifier(itemModifier: PokemonHeldItemModifier, discardQuantity = 1): boolean {
|
||||
const countTaken = Math.min(discardQuantity, itemModifier.stackCount);
|
||||
itemModifier.stackCount -= countTaken;
|
||||
|
||||
if (itemModifier.stackCount > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.removeModifier(itemModifier);
|
||||
}
|
||||
|
||||
canTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferQuantity = 1): boolean {
|
||||
const mod = itemModifier.clone() as PokemonHeldItemModifier;
|
||||
|
@ -71,13 +71,10 @@ import i18next from "i18next";
|
||||
* // Then we must also define a loadTag method with one of the following signatures
|
||||
* public override loadTag(source: BaseArenaTag & Pick<ExampleTag, "tagType" | "a" | "b"): void;
|
||||
* public override loadTag<const T extends this>(source: BaseArenaTag & Pick<T, "tagType" | "a" | "b">): void;
|
||||
* public override loadTag(source: NonFunctionProperties<ExampleTag>): void;
|
||||
* }
|
||||
* ```
|
||||
* Notes
|
||||
* - If the class has any subclasses, then the second form of `loadTag` *must* be used.
|
||||
* - The third form *must not* be used if the class has any getters, as typescript would expect such fields to be
|
||||
* present in `source`.
|
||||
*/
|
||||
|
||||
/** Interface containing the serializable fields of ArenaTagData. */
|
||||
@ -1659,7 +1656,10 @@ export function getArenaTag(
|
||||
* @param source - An arena tag
|
||||
* @returns The valid arena tag
|
||||
*/
|
||||
export function loadArenaTag(source: ArenaTag | ArenaTagTypeData): ArenaTag {
|
||||
export function loadArenaTag(source: ArenaTag | ArenaTagTypeData | { tagType: ArenaTagType.NONE }): ArenaTag {
|
||||
if (source.tagType === ArenaTagType.NONE) {
|
||||
return new NoneTag();
|
||||
}
|
||||
const tag =
|
||||
getArenaTag(source.tagType, source.turnCount, source.sourceMove, source.sourceId, source.side) ?? new NoneTag();
|
||||
tag.loadTag(source);
|
||||
|
@ -44,7 +44,7 @@ import type {
|
||||
SemiInvulnerableTagType,
|
||||
TrappingBattlerTagType,
|
||||
} from "#types/battler-tags";
|
||||
import type { Mutable, NonFunctionProperties } from "#types/type-helpers";
|
||||
import type { Mutable } from "#types/type-helpers";
|
||||
import { BooleanHolder, coerceArray, getFrameMs, isNullOrUndefined, NumberHolder, toDmgValue } from "#utils/common";
|
||||
|
||||
/**
|
||||
@ -80,13 +80,10 @@ import { BooleanHolder, coerceArray, getFrameMs, isNullOrUndefined, NumberHolder
|
||||
* // Then we must also define a loadTag method with one of the following signatures
|
||||
* public override loadTag(source: BaseBattlerTag & Pick<ExampleTag, "tagType" | "a" | "b"): void;
|
||||
* public override loadTag<const T extends this>(source: BaseBattlerTag & Pick<T, "tagType" | "a" | "b">): void;
|
||||
* public override loadTag(source: NonFunctionProperties<ExampleTag>): void;
|
||||
* }
|
||||
* ```
|
||||
* Notes
|
||||
* - If the class has any subclasses, then the second form of `loadTag` *must* be used.
|
||||
* - The third form *must not* be used if the class has any getters, as typescript would expect such fields to be
|
||||
* present in `source`.
|
||||
*/
|
||||
|
||||
/** Interface containing the serializable fields of BattlerTag */
|
||||
@ -201,7 +198,7 @@ export class BattlerTag implements BaseBattlerTag {
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class SerializableBattlerTag extends BattlerTag {
|
||||
export class SerializableBattlerTag extends BattlerTag {
|
||||
/** Nonexistent, dummy field to allow typescript to distinguish this class from `BattlerTag` */
|
||||
private declare __SerializableBattlerTag: never;
|
||||
}
|
||||
@ -419,7 +416,6 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
||||
public override readonly tagType = BattlerTagType.GORILLA_TACTICS;
|
||||
/** ID of the move that the user is locked into using*/
|
||||
public readonly moveId: MoveId = MoveId.NONE;
|
||||
|
||||
constructor() {
|
||||
super(BattlerTagType.GORILLA_TACTICS, BattlerTagLapseType.CUSTOM, 0);
|
||||
}
|
||||
@ -1235,7 +1231,7 @@ export class EncoreTag extends MoveRestrictionBattlerTag {
|
||||
);
|
||||
}
|
||||
|
||||
public override loadTag(source: NonFunctionProperties<EncoreTag>): void {
|
||||
public override loadTag(source: BaseBattlerTag & Pick<EncoreTag, "tagType" | "moveId">): void {
|
||||
super.loadTag(source);
|
||||
this.moveId = source.moveId;
|
||||
}
|
||||
@ -2038,7 +2034,7 @@ export class TruantTag extends AbilityBattlerTag {
|
||||
|
||||
const lastMove = pokemon.getLastXMoves()[0];
|
||||
|
||||
if (!lastMove) {
|
||||
if (!lastMove || lastMove.move === MoveId.NONE) {
|
||||
// Don't interrupt move if last move was `Moves.NONE` OR no prior move was found
|
||||
return true;
|
||||
}
|
||||
@ -2618,7 +2614,7 @@ export class CommandedTag extends SerializableBattlerTag {
|
||||
}
|
||||
}
|
||||
|
||||
override loadTag(source: NonFunctionProperties<CommandedTag>): void {
|
||||
override loadTag(source: BaseBattlerTag & Pick<CommandedTag, "tagType" | "tatsugiriFormKey">): void {
|
||||
super.loadTag(source);
|
||||
(this as Mutable<this>).tatsugiriFormKey = source.tatsugiriFormKey;
|
||||
}
|
||||
@ -2659,7 +2655,9 @@ export class StockpilingTag extends SerializableBattlerTag {
|
||||
}
|
||||
};
|
||||
|
||||
public override loadTag(source: NonFunctionProperties<StockpilingTag>): void {
|
||||
public override loadTag(
|
||||
source: BaseBattlerTag & Pick<StockpilingTag, "tagType" | "stockpiledCount" | "statChangeCounts">,
|
||||
): void {
|
||||
super.loadTag(source);
|
||||
this.stockpiledCount = source.stockpiledCount || 0;
|
||||
this.statChangeCounts = {
|
||||
@ -3006,7 +3004,7 @@ export class AutotomizedTag extends SerializableBattlerTag {
|
||||
this.onAdd(pokemon);
|
||||
}
|
||||
|
||||
public override loadTag(source: NonFunctionProperties<AutotomizedTag>): void {
|
||||
public override loadTag(source: BaseBattlerTag & Pick<AutotomizedTag, "tagType" | "autotomizeCount">): void {
|
||||
super.loadTag(source);
|
||||
this.autotomizeCount = source.autotomizeCount;
|
||||
}
|
||||
@ -3641,7 +3639,7 @@ export function getBattlerTag(
|
||||
case BattlerTagType.FRENZY:
|
||||
return new FrenzyTag(turnCount, sourceMove, sourceId);
|
||||
case BattlerTagType.CHARGING:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, 1, sourceMove, sourceId);
|
||||
return new SerializableBattlerTag(tagType, BattlerTagLapseType.CUSTOM, 1, sourceMove, sourceId);
|
||||
case BattlerTagType.ENCORE:
|
||||
return new EncoreTag(sourceId);
|
||||
case BattlerTagType.HELPING_HAND:
|
||||
@ -3726,10 +3724,10 @@ export function getBattlerTag(
|
||||
return new DragonCheerTag();
|
||||
case BattlerTagType.ALWAYS_CRIT:
|
||||
case BattlerTagType.IGNORE_ACCURACY:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove);
|
||||
return new SerializableBattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove);
|
||||
case BattlerTagType.ALWAYS_GET_HIT:
|
||||
case BattlerTagType.RECEIVE_DOUBLE_DAMAGE:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.PRE_MOVE, 1, sourceMove);
|
||||
return new SerializableBattlerTag(tagType, BattlerTagLapseType.PRE_MOVE, 1, sourceMove);
|
||||
case BattlerTagType.BYPASS_SLEEP:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove);
|
||||
case BattlerTagType.IGNORE_FLYING:
|
||||
@ -3856,7 +3854,7 @@ export type BattlerTagTypeMap = {
|
||||
[BattlerTagType.POWDER]: PowderTag;
|
||||
[BattlerTagType.NIGHTMARE]: NightmareTag;
|
||||
[BattlerTagType.FRENZY]: FrenzyTag;
|
||||
[BattlerTagType.CHARGING]: BattlerTag;
|
||||
[BattlerTagType.CHARGING]: SerializableBattlerTag;
|
||||
[BattlerTagType.ENCORE]: EncoreTag;
|
||||
[BattlerTagType.HELPING_HAND]: HelpingHandTag;
|
||||
[BattlerTagType.INGRAIN]: IngrainTag;
|
||||
@ -3897,10 +3895,10 @@ export type BattlerTagTypeMap = {
|
||||
[BattlerTagType.FIRE_BOOST]: TypeBoostTag;
|
||||
[BattlerTagType.CRIT_BOOST]: CritBoostTag;
|
||||
[BattlerTagType.DRAGON_CHEER]: DragonCheerTag;
|
||||
[BattlerTagType.ALWAYS_CRIT]: BattlerTag;
|
||||
[BattlerTagType.IGNORE_ACCURACY]: BattlerTag;
|
||||
[BattlerTagType.ALWAYS_GET_HIT]: BattlerTag;
|
||||
[BattlerTagType.RECEIVE_DOUBLE_DAMAGE]: BattlerTag;
|
||||
[BattlerTagType.ALWAYS_CRIT]: SerializableBattlerTag;
|
||||
[BattlerTagType.IGNORE_ACCURACY]: SerializableBattlerTag;
|
||||
[BattlerTagType.ALWAYS_GET_HIT]: SerializableBattlerTag;
|
||||
[BattlerTagType.RECEIVE_DOUBLE_DAMAGE]: SerializableBattlerTag;
|
||||
[BattlerTagType.BYPASS_SLEEP]: BattlerTag;
|
||||
[BattlerTagType.IGNORE_FLYING]: GroundedTag;
|
||||
[BattlerTagType.ROOSTED]: RoostedTag;
|
||||
|
@ -119,6 +119,7 @@ export class LoadingScene extends SceneBase {
|
||||
|
||||
this.loadImage("party_bg", "ui");
|
||||
this.loadImage("party_bg_double", "ui");
|
||||
this.loadImage("party_bg_double_manage", "ui");
|
||||
this.loadAtlas("party_slot_main", "ui");
|
||||
this.loadAtlas("party_slot", "ui");
|
||||
this.loadImage("party_slot_overlay_lv", "ui");
|
||||
@ -126,6 +127,8 @@ export class LoadingScene extends SceneBase {
|
||||
this.loadAtlas("party_slot_hp_overlay", "ui");
|
||||
this.loadAtlas("party_pb", "ui");
|
||||
this.loadAtlas("party_cancel", "ui");
|
||||
this.loadAtlas("party_discard", "ui");
|
||||
this.loadAtlas("party_transfer", "ui");
|
||||
|
||||
this.loadImage("summary_bg", "ui");
|
||||
this.loadImage("summary_overlay_shiny", "ui");
|
||||
|
@ -79,13 +79,13 @@ const fonts: Array<LoadingFontFaceProperty> = [
|
||||
face: new FontFace("emerald", "url(./fonts/pokemon-bw.ttf)", {
|
||||
unicodeRange: rangesByLanguage.japanese,
|
||||
}),
|
||||
only: ["en", "es", "fr", "it", "de", "pt", "ko", "ja", "ca", "da", "tr", "ro", "ru"],
|
||||
only: ["en", "es", "fr", "it", "de", "pt", "ko", "ja", "ca", "da", "tr", "ro", "ru", "tl"],
|
||||
},
|
||||
{
|
||||
face: new FontFace("pkmnems", "url(./fonts/pokemon-bw.ttf)", {
|
||||
unicodeRange: rangesByLanguage.japanese,
|
||||
}),
|
||||
only: ["en", "es", "fr", "it", "de", "pt", "ko", "ja", "ca", "da", "tr", "ro", "ru"],
|
||||
only: ["en", "es", "fr", "it", "de", "pt", "ko", "ja", "ca", "da", "tr", "ro", "ru", "tl"],
|
||||
},
|
||||
];
|
||||
|
||||
@ -191,6 +191,7 @@ export async function initI18n(): Promise<void> {
|
||||
"tr",
|
||||
"ro",
|
||||
"ru",
|
||||
"tl",
|
||||
],
|
||||
backend: {
|
||||
loadPath(lng: string, [ns]: string[]) {
|
||||
|
@ -981,6 +981,10 @@ export function setSetting(setting: string, value: number): boolean {
|
||||
label: "Română (Needs Help)",
|
||||
handler: () => changeLocaleHandler("ro"),
|
||||
},
|
||||
{
|
||||
label: "Tagalog (Needs Help)",
|
||||
handler: () => changeLocaleHandler("tl"),
|
||||
},
|
||||
{
|
||||
label: i18next.t("settings:back"),
|
||||
handler: () => cancelHandler(),
|
||||
|
@ -69,7 +69,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||
|
||||
if (context) {
|
||||
context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily;
|
||||
this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width;
|
||||
this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:manageItems")).width;
|
||||
this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width;
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||
this.transferButtonContainer.setVisible(false);
|
||||
ui.add(this.transferButtonContainer);
|
||||
|
||||
const transferButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:transfer"), TextStyle.PARTY);
|
||||
const transferButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:manageItems"), TextStyle.PARTY);
|
||||
transferButtonText.setName("text-transfer-btn");
|
||||
transferButtonText.setOrigin(1, 0);
|
||||
this.transferButtonContainer.add(transferButtonText);
|
||||
@ -601,7 +601,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||
(globalScene.game.canvas.width - this.transferButtonWidth - this.checkButtonWidth) / 6 - 30,
|
||||
OPTION_BUTTON_YPOSITION + 4,
|
||||
);
|
||||
ui.showText(i18next.t("modifierSelectUiHandler:transferDesc"));
|
||||
ui.showText(i18next.t("modifierSelectUiHandler:manageItemsDesc"));
|
||||
} else if (cursor === 2) {
|
||||
this.cursorObj.setPosition(
|
||||
(globalScene.game.canvas.width - this.checkButtonWidth) / 6 - 10,
|
||||
|
@ -103,6 +103,11 @@ export enum PartyUiMode {
|
||||
* This is generally used in for Mystery Encounter or special effects that require the player to select a Pokemon
|
||||
*/
|
||||
SELECT,
|
||||
/**
|
||||
* Indicates that the party UI is open to select a party member from which items will be discarded.
|
||||
* This type of selection can be cancelled.
|
||||
*/
|
||||
DISCARD,
|
||||
}
|
||||
|
||||
export enum PartyOption {
|
||||
@ -121,6 +126,7 @@ export enum PartyOption {
|
||||
RELEASE,
|
||||
RENAME,
|
||||
SELECT,
|
||||
DISCARD,
|
||||
SCROLL_UP = 1000,
|
||||
SCROLL_DOWN = 1001,
|
||||
FORM_CHANGE_ITEM = 2000,
|
||||
@ -155,6 +161,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
private partySlotsContainer: Phaser.GameObjects.Container;
|
||||
private partySlots: PartySlot[];
|
||||
private partyCancelButton: PartyCancelButton;
|
||||
private partyDiscardModeButton: PartyDiscardModeButton;
|
||||
private partyMessageBox: Phaser.GameObjects.NineSlice;
|
||||
private moveInfoOverlay: MoveInfoOverlay;
|
||||
|
||||
@ -180,6 +187,8 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
private transferAll: boolean;
|
||||
|
||||
private lastCursor = 0;
|
||||
private lastLeftPokemonCursor = 0;
|
||||
private lastRightPokemonCursor = 0;
|
||||
private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback | null;
|
||||
private selectFilter: PokemonSelectFilter | PokemonModifierTransferSelectFilter;
|
||||
private moveSelectFilter: PokemonMoveSelectFilter;
|
||||
@ -308,6 +317,12 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.iconAnimHandler = new PokemonIconAnimHandler();
|
||||
this.iconAnimHandler.setup();
|
||||
|
||||
const partyDiscardModeButton = new PartyDiscardModeButton(60, -globalScene.game.canvas.height / 15 - 1, this);
|
||||
|
||||
partyContainer.add(partyDiscardModeButton);
|
||||
|
||||
this.partyDiscardModeButton = partyDiscardModeButton;
|
||||
|
||||
// prepare move overlay. in case it appears to be too big, set the overlayScale to .5
|
||||
const overlayScale = 1;
|
||||
this.moveInfoOverlay = new MoveInfoOverlay({
|
||||
@ -349,8 +364,18 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.showMovePp = args.length > 6 && args[6];
|
||||
|
||||
this.partyContainer.setVisible(true);
|
||||
this.partyBg.setTexture(`party_bg${globalScene.currentBattle.double ? "_double" : ""}`);
|
||||
if (this.isItemManageMode()) {
|
||||
this.partyBg.setTexture(`party_bg${globalScene.currentBattle.double ? "_double_manage" : ""}`);
|
||||
} else {
|
||||
this.partyBg.setTexture(`party_bg${globalScene.currentBattle.double ? "_double" : ""}`);
|
||||
}
|
||||
|
||||
this.populatePartySlots();
|
||||
// If we are currently transferring items, set the icon to its proper state and reveal the button.
|
||||
if (this.isItemManageMode()) {
|
||||
this.partyDiscardModeButton.toggleIcon(this.partyUiMode as PartyUiMode.MODIFIER_TRANSFER | PartyUiMode.DISCARD);
|
||||
}
|
||||
this.showPartyText();
|
||||
this.setCursor(0);
|
||||
|
||||
return true;
|
||||
@ -595,7 +620,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
const option = this.options[this.optionsCursor];
|
||||
if (button === Button.LEFT) {
|
||||
/** Decrease quantity for the current item and update UI */
|
||||
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
|
||||
if (this.isItemManageMode()) {
|
||||
this.transferQuantities[option] =
|
||||
this.transferQuantities[option] === 1
|
||||
? this.transferQuantitiesMax[option]
|
||||
@ -609,7 +634,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
|
||||
if (button === Button.RIGHT) {
|
||||
/** Increase quantity for the current item and update UI */
|
||||
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
|
||||
if (this.isItemManageMode()) {
|
||||
this.transferQuantities[option] =
|
||||
this.transferQuantities[option] === this.transferQuantitiesMax[option]
|
||||
? 1
|
||||
@ -639,6 +664,45 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
return success;
|
||||
}
|
||||
|
||||
private processDiscardMenuInput(pokemon: PlayerPokemon) {
|
||||
const ui = this.getUi();
|
||||
const option = this.options[this.optionsCursor];
|
||||
this.clearOptions();
|
||||
|
||||
this.blockInput = true;
|
||||
this.showText(i18next.t("partyUiHandler:discardConfirmation"), null, () => {
|
||||
this.blockInput = false;
|
||||
ui.setModeWithoutClear(
|
||||
UiMode.CONFIRM,
|
||||
() => {
|
||||
ui.setMode(UiMode.PARTY);
|
||||
this.doDiscard(option, pokemon);
|
||||
},
|
||||
() => {
|
||||
ui.setMode(UiMode.PARTY);
|
||||
this.showPartyText();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private doDiscard(option: PartyOption, pokemon: PlayerPokemon) {
|
||||
const itemModifiers = this.getTransferrableItemsFromPokemon(pokemon);
|
||||
this.clearOptions();
|
||||
|
||||
if (option === PartyOption.ALL) {
|
||||
// Discard all currently held items
|
||||
for (let i = 0; i < itemModifiers.length; i++) {
|
||||
globalScene.tryDiscardHeldItemModifier(itemModifiers[i], this.transferQuantities[i]);
|
||||
}
|
||||
} else {
|
||||
// Discard the currently selected item
|
||||
globalScene.tryDiscardHeldItemModifier(itemModifiers[option], this.transferQuantities[option]);
|
||||
}
|
||||
}
|
||||
|
||||
private moveOptionCursor(button: Button.UP | Button.DOWN): boolean {
|
||||
if (button === Button.UP) {
|
||||
return this.setCursor(this.optionsCursor ? this.optionsCursor - 1 : this.options.length - 1);
|
||||
@ -725,6 +789,10 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
return this.processModifierTransferModeInput(pokemon);
|
||||
}
|
||||
|
||||
if (this.partyUiMode === PartyUiMode.DISCARD) {
|
||||
return this.processDiscardMenuInput(pokemon);
|
||||
}
|
||||
|
||||
// options specific to the mode (moves)
|
||||
if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) {
|
||||
return this.processRememberMoveModeInput(pokemon);
|
||||
@ -864,7 +932,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
}
|
||||
|
||||
if (button === Button.LEFT || button === Button.RIGHT) {
|
||||
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) {
|
||||
if (this.isItemManageMode()) {
|
||||
return this.processModifierTransferModeLeftRightInput(button);
|
||||
}
|
||||
}
|
||||
@ -919,10 +987,22 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
return !(this.partyUiMode === PartyUiMode.FAINT_SWITCH || this.partyUiMode === PartyUiMode.REVIVAL_BLESSING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this UI handler is responsible for managing items.
|
||||
* Used to ensure proper placement of mode toggle buttons in the UI, etc.
|
||||
* @returns Whether the current handler is responsible for managing items.
|
||||
*/
|
||||
private isItemManageMode(): boolean {
|
||||
return this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.DISCARD;
|
||||
}
|
||||
|
||||
private processPartyActionInput(): boolean {
|
||||
const ui = this.getUi();
|
||||
if (this.cursor < 6) {
|
||||
if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) {
|
||||
if (
|
||||
(this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) ||
|
||||
this.partyUiMode === PartyUiMode.DISCARD
|
||||
) {
|
||||
/** Initialize item quantities for the selected Pokemon */
|
||||
const itemModifiers = globalScene.findModifiers(
|
||||
m =>
|
||||
@ -936,6 +1016,25 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.showOptions();
|
||||
ui.playSelect();
|
||||
}
|
||||
|
||||
// Toggle item transfer mode to discard items or vice versa
|
||||
if (this.cursor === 7) {
|
||||
switch (this.partyUiMode) {
|
||||
case PartyUiMode.DISCARD:
|
||||
this.partyUiMode = PartyUiMode.MODIFIER_TRANSFER;
|
||||
break;
|
||||
case PartyUiMode.MODIFIER_TRANSFER:
|
||||
this.partyUiMode = PartyUiMode.DISCARD;
|
||||
break;
|
||||
default:
|
||||
ui.playError();
|
||||
return false;
|
||||
}
|
||||
this.partyDiscardModeButton.toggleIcon(this.partyUiMode);
|
||||
ui.playSelect();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pressing return button
|
||||
if (this.cursor === 6) {
|
||||
if (!this.allowCancel()) {
|
||||
@ -956,6 +1055,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.clearTransfer();
|
||||
ui.playSelect();
|
||||
} else if (this.allowCancel()) {
|
||||
this.partyDiscardModeButton.clear();
|
||||
if (this.selectCallback) {
|
||||
const selectCallback = this.selectCallback;
|
||||
this.selectCallback = null;
|
||||
@ -974,30 +1074,74 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
const slotCount = this.partySlots.length;
|
||||
const battlerCount = globalScene.currentBattle.getBattlerCount();
|
||||
|
||||
if (this.lastCursor < battlerCount) {
|
||||
this.lastLeftPokemonCursor = this.lastCursor;
|
||||
}
|
||||
if (this.lastCursor >= battlerCount && this.lastCursor < 6) {
|
||||
this.lastRightPokemonCursor = this.lastCursor;
|
||||
}
|
||||
|
||||
let success = false;
|
||||
switch (button) {
|
||||
// Item manage mode adds an extra 8th "toggle mode" button to the UI, located *below* both active party members.
|
||||
// The following logic serves to ensure its menu behaviour matches its in-game position,
|
||||
// being selected when scrolling up from the first inactive party member or down from the last active one.
|
||||
case Button.UP:
|
||||
if (this.isItemManageMode()) {
|
||||
if (this.cursor === 1) {
|
||||
success = this.setCursor(globalScene.currentBattle.double ? 0 : 7);
|
||||
break;
|
||||
}
|
||||
if (this.cursor === 2) {
|
||||
success = this.setCursor(globalScene.currentBattle.double ? 7 : 1);
|
||||
break;
|
||||
}
|
||||
if (this.cursor === 6) {
|
||||
success = this.setCursor(slotCount <= globalScene.currentBattle.getBattlerCount() ? 7 : slotCount - 1);
|
||||
break;
|
||||
}
|
||||
if (this.cursor === 7) {
|
||||
success = this.setCursor(globalScene.currentBattle.double && slotCount > 1 ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
success = this.setCursor(this.cursor ? (this.cursor < 6 ? this.cursor - 1 : slotCount - 1) : 6);
|
||||
break;
|
||||
case Button.DOWN:
|
||||
if (this.isItemManageMode()) {
|
||||
if (this.cursor === 0) {
|
||||
success = this.setCursor(globalScene.currentBattle.double && slotCount > 1 ? 1 : 7);
|
||||
break;
|
||||
}
|
||||
if (this.cursor === 1) {
|
||||
success = this.setCursor(globalScene.currentBattle.double ? 7 : slotCount > 2 ? 2 : 6);
|
||||
break;
|
||||
}
|
||||
if (this.cursor === 7) {
|
||||
success = this.setCursor(
|
||||
slotCount > globalScene.currentBattle.getBattlerCount() ? globalScene.currentBattle.getBattlerCount() : 6,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
success = this.setCursor(this.cursor < 6 ? (this.cursor < slotCount - 1 ? this.cursor + 1 : 6) : 0);
|
||||
break;
|
||||
case Button.LEFT:
|
||||
if (this.cursor >= battlerCount && this.cursor <= 6) {
|
||||
success = this.setCursor(0);
|
||||
if (this.cursor === 6) {
|
||||
success = this.setCursor(this.isItemManageMode() ? 7 : this.lastLeftPokemonCursor);
|
||||
}
|
||||
if (this.cursor >= battlerCount && this.cursor < 6) {
|
||||
success = this.setCursor(this.lastLeftPokemonCursor);
|
||||
}
|
||||
break;
|
||||
case Button.RIGHT:
|
||||
if (slotCount === battlerCount) {
|
||||
// Scrolling right from item transfer button or with no backup party members goes to cancel
|
||||
if (this.cursor === 7 || slotCount <= battlerCount) {
|
||||
success = this.setCursor(6);
|
||||
break;
|
||||
}
|
||||
if (battlerCount >= 2 && slotCount > battlerCount && this.getCursor() === 0 && this.lastCursor === 1) {
|
||||
success = this.setCursor(2);
|
||||
break;
|
||||
}
|
||||
if (slotCount > battlerCount && this.cursor < battlerCount) {
|
||||
success = this.setCursor(this.lastCursor < 6 ? this.lastCursor || battlerCount : battlerCount);
|
||||
if (this.cursor < battlerCount) {
|
||||
success = this.setCursor(this.lastRightPokemonCursor || battlerCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1044,11 +1188,15 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.partySlots[this.lastCursor].deselect();
|
||||
} else if (this.lastCursor === 6) {
|
||||
this.partyCancelButton.deselect();
|
||||
} else if (this.lastCursor === 7) {
|
||||
this.partyDiscardModeButton.deselect();
|
||||
}
|
||||
if (cursor < 6) {
|
||||
this.partySlots[cursor].select();
|
||||
} else if (cursor === 6) {
|
||||
this.partyCancelButton.select();
|
||||
} else if (cursor === 7) {
|
||||
this.partyDiscardModeButton.select();
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
@ -1143,14 +1291,16 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
optionsMessage = i18next.t("partyUiHandler:selectAnotherPokemonToSplice");
|
||||
}
|
||||
break;
|
||||
case PartyUiMode.DISCARD:
|
||||
optionsMessage = i18next.t("partyUiHandler:changeQuantityDiscard");
|
||||
}
|
||||
|
||||
this.showText(optionsMessage, 0);
|
||||
|
||||
this.updateOptions();
|
||||
|
||||
/** 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) {
|
||||
/** When an item is being selected for transfer or discard, the message box is taller as the message occupies two lines */
|
||||
if (this.isItemManageMode()) {
|
||||
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);
|
||||
@ -1159,6 +1309,20 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.setCursor(0);
|
||||
}
|
||||
|
||||
showPartyText() {
|
||||
switch (this.partyUiMode) {
|
||||
case PartyUiMode.MODIFIER_TRANSFER:
|
||||
this.showText(i18next.t("partyUiHandler:partyTransfer"));
|
||||
break;
|
||||
case PartyUiMode.DISCARD:
|
||||
this.showText(i18next.t("partyUiHandler:partyDiscard"));
|
||||
break;
|
||||
default:
|
||||
this.showText("", 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private allowBatonModifierSwitch(): boolean {
|
||||
return !!(
|
||||
this.partyUiMode !== PartyUiMode.FAINT_SWITCH &&
|
||||
@ -1276,6 +1440,9 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.addCommonOptions(pokemon);
|
||||
}
|
||||
break;
|
||||
case PartyUiMode.DISCARD:
|
||||
this.updateOptionsWithModifierTransferMode(pokemon);
|
||||
break;
|
||||
// TODO: This still needs to be broken up.
|
||||
// It could use a rework differentiating different kind of switches
|
||||
// to treat baton passing separately from switching on faint.
|
||||
@ -1381,7 +1548,8 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
optionName = "↓";
|
||||
} else if (
|
||||
(this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER &&
|
||||
(this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER || this.transferMode)) ||
|
||||
(this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER || this.transferMode) &&
|
||||
this.partyUiMode !== PartyUiMode.DISCARD) ||
|
||||
option === PartyOption.CANCEL
|
||||
) {
|
||||
switch (option) {
|
||||
@ -1444,7 +1612,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
const itemModifiers = this.getItemModifiers(pokemon);
|
||||
const itemModifier = itemModifiers[option];
|
||||
if (
|
||||
this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER &&
|
||||
this.isItemManageMode() &&
|
||||
this.transferQuantitiesMax[option] > 1 &&
|
||||
!this.transferMode &&
|
||||
itemModifier !== undefined &&
|
||||
@ -1474,7 +1642,6 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
optionText.x = 15 - this.optionsBg.width;
|
||||
}
|
||||
}
|
||||
|
||||
startTransfer(): void {
|
||||
this.transferMode = true;
|
||||
this.transferCursor = this.cursor;
|
||||
@ -1608,7 +1775,7 @@ export class PartyUiHandler extends MessageUiHandler {
|
||||
this.eraseOptionsCursor();
|
||||
|
||||
this.partyMessageBox.setSize(262, 30);
|
||||
this.showText("", 0);
|
||||
this.showPartyText();
|
||||
}
|
||||
|
||||
eraseOptionsCursor() {
|
||||
@ -1663,7 +1830,9 @@ class PartySlot extends Phaser.GameObjects.Container {
|
||||
? -184 +
|
||||
(globalScene.currentBattle.double ? -40 : 0) +
|
||||
(28 + (globalScene.currentBattle.double ? 8 : 0)) * slotIndex
|
||||
: -124 + (globalScene.currentBattle.double ? -8 : 0) + slotIndex * 64,
|
||||
: partyUiMode === PartyUiMode.MODIFIER_TRANSFER
|
||||
? -124 + (globalScene.currentBattle.double ? -20 : 0) + slotIndex * 55
|
||||
: -124 + (globalScene.currentBattle.double ? -8 : 0) + slotIndex * 64,
|
||||
);
|
||||
|
||||
this.slotIndex = slotIndex;
|
||||
@ -1918,7 +2087,6 @@ class PartySlot extends Phaser.GameObjects.Container {
|
||||
|
||||
class PartyCancelButton extends Phaser.GameObjects.Container {
|
||||
private selected: boolean;
|
||||
|
||||
private partyCancelBg: Phaser.GameObjects.Sprite;
|
||||
private partyCancelPb: Phaser.GameObjects.Sprite;
|
||||
|
||||
@ -1965,3 +2133,96 @@ class PartyCancelButton extends Phaser.GameObjects.Container {
|
||||
this.partyCancelPb.setFrame("party_pb");
|
||||
}
|
||||
}
|
||||
|
||||
class PartyDiscardModeButton extends Phaser.GameObjects.Container {
|
||||
private selected: boolean;
|
||||
private transferIcon: Phaser.GameObjects.Sprite;
|
||||
private discardIcon: Phaser.GameObjects.Sprite;
|
||||
private textBox: Phaser.GameObjects.Text;
|
||||
private party: PartyUiHandler;
|
||||
|
||||
constructor(x: number, y: number, party: PartyUiHandler) {
|
||||
super(globalScene, x, y);
|
||||
|
||||
this.setup(party);
|
||||
}
|
||||
|
||||
setup(party: PartyUiHandler) {
|
||||
this.transferIcon = globalScene.add.sprite(0, 0, "party_transfer");
|
||||
this.discardIcon = globalScene.add.sprite(0, 0, "party_discard");
|
||||
this.textBox = addTextObject(-8, -7, i18next.t("partyUiHandler:TRANSFER"), TextStyle.PARTY);
|
||||
this.party = party;
|
||||
|
||||
this.add(this.transferIcon);
|
||||
this.add(this.discardIcon);
|
||||
this.add(this.textBox);
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
select() {
|
||||
if (this.selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = true;
|
||||
|
||||
this.party.showText(i18next.t("partyUiHandler:changeMode"));
|
||||
|
||||
this.transferIcon.setFrame("selected");
|
||||
this.discardIcon.setFrame("selected");
|
||||
}
|
||||
|
||||
deselect() {
|
||||
if (!this.selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = false;
|
||||
this.party.showPartyText();
|
||||
|
||||
this.transferIcon.setFrame("normal");
|
||||
this.discardIcon.setFrame("normal");
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current mode deals with transferring items, toggle the discard items button's name and assets.
|
||||
* @param partyMode - The current {@linkcode PartyUiMode}
|
||||
* @remarks
|
||||
* This will also reveal the button if it is currently hidden.
|
||||
*/
|
||||
public toggleIcon(partyMode: PartyUiMode.MODIFIER_TRANSFER | PartyUiMode.DISCARD): void {
|
||||
this.setActive(true).setVisible(true);
|
||||
switch (partyMode) {
|
||||
case PartyUiMode.MODIFIER_TRANSFER:
|
||||
this.transferIcon.setVisible(true);
|
||||
this.discardIcon.setVisible(false);
|
||||
this.textBox.setVisible(true);
|
||||
this.textBox.setText(i18next.t("partyUiHandler:TRANSFER"));
|
||||
this.setPosition(
|
||||
globalScene.currentBattle.double ? 64 : 60,
|
||||
globalScene.currentBattle.double ? -48 : -globalScene.game.canvas.height / 15 - 1,
|
||||
);
|
||||
this.transferIcon.displayWidth = this.textBox.text.length * 9 + 3;
|
||||
break;
|
||||
case PartyUiMode.DISCARD:
|
||||
this.transferIcon.setVisible(false);
|
||||
this.discardIcon.setVisible(true);
|
||||
this.textBox.setVisible(true);
|
||||
this.textBox.setText(i18next.t("partyUiHandler:DISCARD"));
|
||||
this.setPosition(
|
||||
globalScene.currentBattle.double ? 64 : 60,
|
||||
globalScene.currentBattle.double ? -48 : -globalScene.game.canvas.height / 15 - 1,
|
||||
);
|
||||
this.discardIcon.displayWidth = this.textBox.text.length * 9 + 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.setActive(false).setVisible(false);
|
||||
this.transferIcon.setVisible(false);
|
||||
this.discardIcon.setVisible(false);
|
||||
this.textBox.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,12 @@ export class SettingsDisplayUiHandler extends AbstractSettingsUiHandler {
|
||||
label: "Română (Needs Help)",
|
||||
};
|
||||
break;
|
||||
case "tl":
|
||||
this.settings[languageIndex].options[0] = {
|
||||
value: "Tagalog",
|
||||
label: "Tagalog (Needs Help)",
|
||||
};
|
||||
break;
|
||||
default:
|
||||
this.settings[languageIndex].options[0] = {
|
||||
value: "English",
|
||||
|
@ -176,6 +176,10 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
||||
starterInfoYOffset: 0.5,
|
||||
starterInfoXPos: 26,
|
||||
},
|
||||
tl: {
|
||||
starterInfoTextSize: "56px",
|
||||
instructionTextSize: "38px",
|
||||
},
|
||||
};
|
||||
|
||||
const valueReductionMax = 2;
|
||||
|
@ -436,6 +436,7 @@ export function hasAllLocalizedSprites(lang?: string): boolean {
|
||||
case "ja":
|
||||
case "ca":
|
||||
case "ru":
|
||||
case "tl":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
72
test/abilities/truant.test.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MoveResult } from "#enums/move-result";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import i18next from "i18next";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Ability - Truant", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.criticalHits(false)
|
||||
.moveset([MoveId.SPLASH, MoveId.TACKLE])
|
||||
.ability(AbilityId.TRUANT)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
});
|
||||
|
||||
it("should loaf around and prevent using moves every other turn", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
const player = game.field.getPlayerPokemon();
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
|
||||
// Turn 1: Splash succeeds
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(player.getLastXMoves(1)[0]).toEqual(
|
||||
expect.objectContaining({ move: MoveId.SPLASH, result: MoveResult.SUCCESS }),
|
||||
);
|
||||
|
||||
// Turn 2: Truant activates, cancelling tackle and displaying message
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(player.getLastXMoves(1)[0]).toEqual(expect.objectContaining({ move: MoveId.NONE, result: MoveResult.FAIL }));
|
||||
expect(enemy.hp).toBe(enemy.getMaxHp());
|
||||
expect(game.textInterceptor.logs).toContain(
|
||||
i18next.t("battlerTags:truantLapse", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(player),
|
||||
}),
|
||||
);
|
||||
|
||||
// Turn 3: Truant didn't activate, tackle worked
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(player.getLastXMoves(1)[0]).toEqual(
|
||||
expect.objectContaining({ move: MoveId.TACKLE, result: MoveResult.SUCCESS }),
|
||||
);
|
||||
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
|
||||
});
|
||||
});
|
172
test/ui/item-manage-button.test.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Button } from "#enums/buttons";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import type { Pokemon } from "#field/pokemon";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
|
||||
import type { PartyUiHandler } from "#ui/party-ui-handler";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("UI - Transfer Items", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.startingLevel(100)
|
||||
.startingHeldItems([
|
||||
{ name: "BERRY", count: 1, type: BerryType.SITRUS },
|
||||
{ name: "BERRY", count: 2, type: BerryType.APICOT },
|
||||
{ name: "BERRY", count: 2, type: BerryType.LUM },
|
||||
])
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.RAYQUAZA, SpeciesId.RAYQUAZA]);
|
||||
|
||||
game.move.use(MoveId.DRAGON_CLAW);
|
||||
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
});
|
||||
|
||||
it("manage button exists in the proper screen", async () => {
|
||||
let handlerLength: Phaser.GameObjects.GameObject[] | undefined;
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
//select manage items menu
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.setCursor(1);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as PartyUiHandler;
|
||||
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.processInput(Button.ACTION);
|
||||
handlerLength = handler.optionsContainer.list;
|
||||
|
||||
handler.processInput(Button.CANCEL);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
expect(handlerLength).toHaveLength(0); // should select manage button, which has no menu
|
||||
});
|
||||
|
||||
it("manage button doesn't exist in the other screens", async () => {
|
||||
let handlerLength: Phaser.GameObjects.GameObject[] | undefined;
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.setCursor(2);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as PartyUiHandler;
|
||||
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.processInput(Button.ACTION);
|
||||
handlerLength = handler.optionsContainer.list;
|
||||
|
||||
handler.processInput(Button.CANCEL);
|
||||
handler.processInput(Button.CANCEL);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
expect(handlerLength).toHaveLength(6); // should select 2nd pokemon (length is 5 options + image)
|
||||
});
|
||||
|
||||
// Test that the manage button actually discards items, needs proofreading
|
||||
it("should discard items when button is selected", async () => {
|
||||
let pokemon: Pokemon | undefined;
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.setCursor(1);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as PartyUiHandler;
|
||||
|
||||
// Enter discard mode and select first party member
|
||||
handler.setCursor(7);
|
||||
handler.processInput(Button.ACTION);
|
||||
handler.setCursor(0);
|
||||
handler.processInput(Button.ACTION);
|
||||
pokemon = game.field.getPlayerPokemon();
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
expect(pokemon).toBeDefined();
|
||||
if (pokemon) {
|
||||
expect(pokemon.getHeldItems()).toHaveLength(3);
|
||||
expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([1, 2, 2]);
|
||||
}
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as PartyUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
const handler = game.scene.ui.getHandler() as PartyUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
|
||||
pokemon = game.field.getPlayerPokemon();
|
||||
|
||||
handler.processInput(Button.CANCEL);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
expect(pokemon).toBeDefined();
|
||||
if (pokemon) {
|
||||
// Sitrus berry was discarded, leaving 2 stacks of 2 berries behind
|
||||
expect(pokemon.getHeldItems()).toHaveLength(2);
|
||||
expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([2, 2]);
|
||||
}
|
||||
});
|
||||
});
|