diff --git a/README.md b/README.md index 513bee8c09d..424d47ff1b0 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,9 @@ Check out our [Trello Board](https://trello.com/b/z10B703R/pokerogue-board) to s - GAMEFREAK - LJ Birdman +### 🎨 Pagefault Games Intro + - Spectremint + ### 🎨 Game Logo - Gonstar (Paid Commission) diff --git a/package.json b/package.json index fd189fd4d89..0765a180c7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", "scripts": { "start": "vite", diff --git a/public/battle-anims/common-snow.json b/public/battle-anims/common-snow.json new file mode 100644 index 00000000000..03bca8434fb --- /dev/null +++ b/public/battle-anims/common-snow.json @@ -0,0 +1,2477 @@ +{ + "graphic": "PRAS- Weather", + "frames": [ + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -49.214285714285715, + "y": -88.64285714285714, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -41.92857142857143, + "y": -67.78571428571429, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 75, + "y": -111, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -34.64285714285714, + "y": -46.928571428571445, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 88.25, + "y": -80, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -7.5, + "y": -111, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -27.35714285714286, + "y": -26.071428571428584, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 101.5, + "y": -49.00000000000002, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 30.5, + "y": -111, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 2.5269971999999967, + "y": -90.30207, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -20.071428571428577, + "y": -5.214285714285737, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 114.75, + "y": -18.000000000000043, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 37.28571428571429, + "y": -93.35714285714286, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 14.297732800000006, + "y": -66.00468000000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -12.785714285714299, + "y": 15.64285714285711, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 114.5, + "y": -18.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 44.07142857142857, + "y": -75.71428571428572, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -63.5, + "y": -110.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 25.196381200000005, + "y": -43.50747, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -13, + "y": 15.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 114.5, + "y": -14.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 50.85714285714286, + "y": -58.071428571428584, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -56, + "y": -88.5034, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 155, + "y": -111, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 36.96960200000001, + "y": -19.204949999999997, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -13, + "y": 19.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 100.5, + "y": -111, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 57.642857142857125, + "y": -40.42857142857146, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -44, + "y": -58.5068, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 162.59260970569616, + "y": -85.3547946619629, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 47, + "y": 1.4999999999999858, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": 107.87500000000003, + "y": -92.75, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 64.42857142857142, + "y": -22.785714285714334, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -32, + "y": -28.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 170.1999439097043, + "y": -59.71395402493992, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 47, + "y": 1.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -63.5, + "y": -76, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 115.25000000000003, + "y": -74.50000000000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 71.21428571428572, + "y": -5.142857142857196, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -32, + "y": -28.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 177.80249439597412, + "y": -34.07198837210045, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 47, + "y": 5.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 150, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -56, + "y": -52.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 122.625, + "y": -56.250000000000014, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 71, + "y": -5.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -32, + "y": -24.25, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 185.01460118233896, + "y": -8.317186570033272, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 12.5, + "y": -111.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -44.50000000000001, + "y": -22.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 130, + "y": -38, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 71, + "y": -1.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": -18.5, + "y": -111, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 185, + "y": -8.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 21.5, + "y": -91.80000000000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -35, + "y": 4.25, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 137.375, + "y": -19.750000000000014, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 185, + "y": -4.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 1, + "focus": 4 + }, + { + "x": -6.997244140625007, + "y": -86.904753515625, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 30.5, + "y": -72.10000000000002, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -28, + "y": 24, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 144.75, + "y": -1.5000000000000142, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 5, + "y": -112, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 5.247328124999996, + "y": -61.255596875, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 39.5, + "y": -52.400000000000006, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -28, + "y": 24, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 152.12500000000003, + "y": 16.74999999999997, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 13.676215624999998, + "y": -92.46028749999999, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 17.493458984374996, + "y": -35.603175390625, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 48.499999999999986, + "y": -32.70000000000002, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -63.5, + "y": -107, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -28, + "y": 28, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 152, + "y": 16.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 24.83135, + "y": -67.3378, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 29, + "y": -11.500000000000014, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 120, + "y": -111.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 48.5, + "y": -33, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -52.25, + "y": -83.75, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": 152, + "y": 20.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 1, + "focus": 4 + }, + { + "x": 34.74665312500001, + "y": -45.0075375, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 29, + "y": -11.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 136.83165, + "y": -83.8361, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 48.5, + "y": -29, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 150, + "priority": 1, + "focus": 4 + }, + { + "x": -41.00000000000001, + "y": -60.50000000000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -63, + "y": -110.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 44.6627, + "y": -22.675600000000003, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 29, + "y": -7.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 153.6633, + "y": -56.172200000000004, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": -29.75, + "y": -37.250000000000014, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -53.2489752, + "y": -88.7901712, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 55.82080937500001, + "y": 2.4535874999999976, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 170.50000000000006, + "y": -28.50000000000003, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 10, + "y": -111.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -30, + "y": -37.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -41.8022048, + "y": -63.3049088, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 73, + "y": -111.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 64.50000000000003, + "y": 22, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 170.5, + "y": -29, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 18.749124999999992, + "y": -91.085375, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -30, + "y": -33.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 150, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -30.703519199999995, + "y": -41.70783519999999, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 81.5, + "y": -93, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 64.5, + "y": 22, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 170.85714285714286, + "y": -24.642857142857153, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 29.99799999999999, + "y": -64.838, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -19.75433199999999, + "y": -14.217191999999983, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 87.71428571428572, + "y": -75.78571428571429, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 64.5, + "y": 22, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 39.996624999999995, + "y": -41.50787500000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -64, + "y": -76.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -10.000000000000007, + "y": 7.500000000000014, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 95.07142857142858, + "y": -57.92857142857144, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -4, + "y": -110.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 49.995999999999995, + "y": -18.176000000000002, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -56.875, + "y": -54.75, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -10, + "y": 7.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 102.42857142857142, + "y": -40.0714285714286, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 6.5, + "y": -88.4, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 61.24787499999999, + "y": 8.078374999999994, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -49.75, + "y": -33, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -10, + "y": 11.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + }, + { + "x": 109.78571428571428, + "y": -22.214285714285737, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 17, + "y": -66.30000000000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 70, + "y": 28.49999999999997, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -42.625, + "y": -11.25, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": 117.14285714285714, + "y": -4.35714285714289, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 27.5, + "y": -44.19999999999999, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 70, + "y": 28, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": -43, + "y": -11.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": -43, + "y": -7.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 1, + "focus": 4 + }, + { + "x": 117, + "y": -4.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 1, + "focus": 4 + }, + { + "x": 37.999999999999986, + "y": -22.10000000000001, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + }, + { + "x": 70, + "y": 32, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 150, + "priority": 1, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": 117, + "y": -0.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 1, + "focus": 4 + }, + { + "x": 38, + "y": -22.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 2, + "focus": 4 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 1, + "focus": 1 + }, + { + "x": 38, + "y": -18.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 150, + "priority": 2, + "focus": 4 + } + ] + ], + "frameTimedEvents": { + "0": [ + { + "frameIndex": 0, + "resourceName": "PRSFX- Hail.wav", + "volume": 100, + "pitch": 101, + "eventType": "AnimTimedSoundEvent" + } + ], + "7": [ + { + "frameIndex": 7, + "resourceName": "PRSFX- Hail.wav", + "volume": 100, + "pitch": 102, + "eventType": "AnimTimedSoundEvent" + } + ], + "15": [ + { + "frameIndex": 15, + "resourceName": "PRSFX- Hail.wav", + "volume": 100, + "pitch": 100, + "eventType": "AnimTimedSoundEvent" + } + ], + "23": [ + { + "frameIndex": 23, + "resourceName": "PRSFX- Hail.wav", + "volume": 100, + "pitch": 102, + "eventType": "AnimTimedSoundEvent" + } + ] + }, + "position": 4, + "hue": 0 +} \ No newline at end of file diff --git a/public/images/intro_dark.mp4 b/public/images/intro_dark.mp4 new file mode 100644 index 00000000000..160d223e2fe Binary files /dev/null and b/public/images/intro_dark.mp4 differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 0efc4dc8ddb..bb4803a9eb1 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -128,6 +128,7 @@ export default class BattleScene extends SceneBase { public moveAnimations: boolean = true; public hpBarSpeed: integer = 0; public fusionPaletteSwaps: boolean = true; + public gamepadSupport: boolean = true; public enableTouchControls: boolean = false; public enableVibration: boolean = false; @@ -198,6 +199,26 @@ export default class BattleScene extends SceneBase { // (i.e. by holding down a button) at a time private movementButtonLock: Button; + // using a dualshock controller as a map + private gamepadKeyConfig = { + [Button.UP]: 12, // up + [Button.DOWN]: 13, // down + [Button.LEFT]: 14, // left + [Button.RIGHT]: 15, // right + [Button.SUBMIT]: 17, // touchpad + [Button.ACTION]: 0, // X + [Button.CANCEL]: 1, // O + [Button.MENU]: 9, // options + [Button.CYCLE_SHINY]: 5, // RB + [Button.CYCLE_FORM]: 4, // LB + [Button.CYCLE_GENDER]: 6, // LT + [Button.CYCLE_ABILITY]: 7, // RT + [Button.CYCLE_NATURE]: 2, // square + [Button.SPEED_UP]: 10, // L3 + [Button.SLOW_DOWN]: 11 // R3 + }; + public gamepadButtonStates: boolean[] = new Array(17).fill(false); + public rngCounter: integer = 0; public rngSeedOverride: string = ''; public rngOffset: integer = 0; @@ -936,6 +957,8 @@ export default class BattleScene extends SceneBase { case Species.TATSUGIRI: case Species.PALDEA_TAUROS: return Utils.randSeedInt(species.forms.length); + case Species.MINIOR: + return Utils.randSeedInt(6); case Species.MEOWSTIC: case Species.INDEEDEE: case Species.BASCULEGION: @@ -1243,14 +1266,19 @@ export default class BattleScene extends SceneBase { } else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { if (this.buttonJustPressed(Button.CYCLE_SHINY)) { inputSuccess = this.ui.processInput(Button.CYCLE_SHINY); + this.setLastProcessedMovementTime(Button.CYCLE_SHINY); } else if (this.buttonJustPressed(Button.CYCLE_FORM)) { inputSuccess = this.ui.processInput(Button.CYCLE_FORM); + this.setLastProcessedMovementTime(Button.CYCLE_FORM); } else if (this.buttonJustPressed(Button.CYCLE_GENDER)) { inputSuccess = this.ui.processInput(Button.CYCLE_GENDER); + this.setLastProcessedMovementTime(Button.CYCLE_GENDER); } else if (this.buttonJustPressed(Button.CYCLE_ABILITY)) { inputSuccess = this.ui.processInput(Button.CYCLE_ABILITY); + this.setLastProcessedMovementTime(Button.CYCLE_ABILITY); } else if (this.buttonJustPressed(Button.CYCLE_NATURE)) { inputSuccess = this.ui.processInput(Button.CYCLE_NATURE); + this.setLastProcessedMovementTime(Button.CYCLE_NATURE); } else return; } else if (this.buttonJustPressed(Button.SPEED_UP)) { @@ -1271,8 +1299,29 @@ export default class BattleScene extends SceneBase { navigator.vibrate(vibrationLength || 10); } + /** + * gamepadButtonJustDown returns true if @param button has just been pressed down + * or not. It will only return true once, until the key is released and pressed down + * again. + */ + gamepadButtonJustDown(button: Phaser.Input.Gamepad.Button) : boolean { + if (!button || !this.gamepadSupport) + return false; + + let ret = false; + if (button.pressed) { + if (!this.gamepadButtonStates[button.index]) + ret = true; + this.gamepadButtonStates[button.index] = true; + } else + this.gamepadButtonStates[button.index] = false; + + return ret; + } + buttonJustPressed(button: Button): boolean { - return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustDown(k)); + const gamepad = this.input.gamepad?.gamepads[0]; + return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustDown(k)) || this.gamepadButtonJustDown(gamepad?.buttons[this.gamepadKeyConfig[button]]); } /** @@ -1284,7 +1333,7 @@ export default class BattleScene extends SceneBase { if (this.movementButtonLock !== null && this.movementButtonLock !== button) { return false; } - if (this.buttonKeys[button].every(k => k.isUp)) { + if (this.buttonKeys[button].every(k => k.isUp) && this.gamepadButtonStates.every(b => b == false)) { this.movementButtonLock = null; return false; } @@ -1935,4 +1984,4 @@ export default class BattleScene extends SceneBase { return false; } -} \ No newline at end of file +} diff --git a/src/data/ability.ts b/src/data/ability.ts index a0645d4af9c..301d0fecdc2 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -386,6 +386,20 @@ export class PostDefendAbAttr extends AbAttr { } } +export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { + applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const attackPriority = new Utils.IntegerHolder(move.getMove().priority); + applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move.getMove(), attackPriority); + + if(attackPriority.value > 0 && !move.getMove().isMultiTarget()) { + cancelled.value = true; + return true; + } + + return false; + } +} + export class PostStatChangeAbAttr extends AbAttr { applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { return false; @@ -1141,6 +1155,8 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { export class TraceAbAttr extends PostSummonAbAttr { applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); + if (!targets.length) + return false; let target: Pokemon; if (targets.length > 1) pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); @@ -2343,10 +2359,10 @@ export function initAbilities() { .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender !== target.gender, 0.75), new Ability(Abilities.STEADFAST, "Steadfast", "The Pokémon's determination boosts the Speed stat each time the Pokémon flinches.", 4) .attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1), - new Ability(Abilities.SNOW_CLOAK, "Snow Cloak", "Boosts evasiveness in a hailstorm.", 4) + new Ability(Abilities.SNOW_CLOAK, "Snow Cloak", "Boosts the Pokémon's evasiveness in snow.", 4) .attr(BattleStatMultiplierAbAttr, BattleStat.EVA, 1.2) .attr(BlockWeatherDamageAttr, WeatherType.HAIL) - .condition(getWeatherCondition(WeatherType.HAIL)) + .condition(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW)) .ignorable(), new Ability(Abilities.GLUTTONY, "Gluttony", "Makes the Pokémon eat a held Berry when its HP drops to half or less, which is sooner than usual.", 4) .attr(ReduceBerryUseThresholdAbAttr), @@ -2424,14 +2440,15 @@ export function initAbilities() { .attr(RedirectTypeMoveAbAttr, Type.WATER) .attr(TypeImmunityStatChangeAbAttr, Type.WATER, BattleStat.SPATK, 1) .ignorable(), - new Ability(Abilities.ICE_BODY, "Ice Body", "The Pokémon gradually regains HP in a hailstorm.", 4) - .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL), + new Ability(Abilities.ICE_BODY, "Ice Body", "The Pokémon gradually regains HP in snow.", 4) + .attr(BlockWeatherDamageAttr, WeatherType.HAIL) + .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL, WeatherType.SNOW), new Ability(Abilities.SOLID_ROCK, "Solid Rock", "Reduces the power of supereffective attacks taken.", 4) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type) >= 2, 0.75) .ignorable(), - new Ability(Abilities.SNOW_WARNING, "Snow Warning", "The Pokémon summons a hailstorm when it enters a battle.", 4) - .attr(PostSummonWeatherChangeAbAttr, WeatherType.HAIL) - .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HAIL), + new Ability(Abilities.SNOW_WARNING, "Snow Warning", "The Pokémon makes it snow when it enters a battle.", 4) + .attr(PostSummonWeatherChangeAbAttr, WeatherType.SNOW) + .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SNOW), new Ability(Abilities.HONEY_GATHER, "Honey Gather (N)", "The Pokémon may gather Honey after a battle.", 4), new Ability(Abilities.FRISK, "Frisk (N)", "When it enters a battle, the Pokémon can check an opposing Pokémon's held item.", 4), new Ability(Abilities.RECKLESS, "Reckless", "Powers up moves that have recoil damage.", 4) @@ -2536,9 +2553,9 @@ export function initAbilities() { .attr(PostDefendContactDamageAbAttr, 8) .bypassFaint(), new Ability(Abilities.ZEN_MODE, "Zen Mode", "Changes the Pokémon's shape when HP is half or less.", 5) - .attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1) - .attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1) - .attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1) + .attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0) + .attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0) + .attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), @@ -2616,7 +2633,10 @@ export function initAbilities() { new Ability(Abilities.WATER_COMPACTION, "Water Compaction", "Boosts the Pokémon's Defense stat sharply when hit by a Water-type move.", 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER, BattleStat.DEF, 2), new Ability(Abilities.MERCILESS, "Merciless (N)", "The Pokémon's attacks become critical hits if the target is poisoned.", 7), - new Ability(Abilities.SHIELDS_DOWN, "Shields Down (N)", "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", 7) + new Ability(Abilities.SHIELDS_DOWN, "Shields Down (P)", "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", 7) + .attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) + .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) + .attr(PostTurnFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), @@ -2629,9 +2649,9 @@ export function initAbilities() { new Ability(Abilities.STEELWORKER, "Steelworker", "Powers up Steel-type moves.", 7) .attr(MoveTypePowerBoostAbAttr, Type.STEEL), new Ability(Abilities.BERSERK, "Berserk (N)", "Boosts the Pokémon's Sp. Atk stat when it takes a hit that causes its HP to become half or less.", 7), - new Ability(Abilities.SLUSH_RUSH, "Slush Rush", "Boosts the Pokémon's Speed stat in a hailstorm.", 7) + new Ability(Abilities.SLUSH_RUSH, "Slush Rush", "Boosts the Pokémon's Speed stat in snow.", 7) .attr(BattleStatMultiplierAbAttr, BattleStat.SPD, 2) - .condition(getWeatherCondition(WeatherType.HAIL)), + .condition(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW)), new Ability(Abilities.LONG_REACH, "Long Reach", "The Pokémon uses its moves without making contact with the target.", 7) .attr(IgnoreContactAbAttr), new Ability(Abilities.LIQUID_VOICE, "Liquid Voice", "All sound-based moves become Water-type moves.", 7) @@ -2668,7 +2688,8 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), - new Ability(Abilities.QUEENLY_MAJESTY, "Queenly Majesty (N)", "Its majesty pressures the opposing Pokémon, making it unable to attack using priority moves.", 7) + new Ability(Abilities.QUEENLY_MAJESTY, "Queenly Majesty", "Its majesty pressures the opposing Pokémon, making it unable to attack using priority moves.", 7) + .attr(FieldPriorityMoveImmunityAbAttr) .ignorable(), new Ability(Abilities.INNARDS_OUT, "Innards Out (N)", "Damages the attacker landing the finishing hit by the amount equal to its last HP.", 7), new Ability(Abilities.DANCER, "Dancer (N)", "When another Pokémon uses a dance move, it can use a dance move following it regardless of its Speed.", 7), @@ -2677,7 +2698,8 @@ export function initAbilities() { .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 0.5) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.type === Type.FIRE, 2) .ignorable(), - new Ability(Abilities.DAZZLING, "Dazzling (N)", "Surprises the opposing Pokémon, making it unable to attack using priority moves.", 7) + new Ability(Abilities.DAZZLING, "Dazzling", "Surprises the opposing Pokémon, making it unable to attack using priority moves.", 7) + .attr(FieldPriorityMoveImmunityAbAttr) .ignorable(), new Ability(Abilities.SOUL_HEART, "Soul-Heart", "Boosts its Sp. Atk stat every time a Pokémon faints.", 7) .attr(PostKnockOutStatChangeAbAttr, BattleStat.SPATK, 1), @@ -2810,7 +2832,8 @@ export function initAbilities() { .bypassFaint(), new Ability(Abilities.SEED_SOWER, "Seed Sower", "Turns the ground into Grassy Terrain when the Pokémon is hit by an attack.", 9) .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY), - new Ability(Abilities.THERMAL_EXCHANGE, "Thermal Exchange (P)", "Boosts the Attack stat when the Pokémon is hit by a Fire-type move. The Pokémon also cannot be burned.", 9) + new Ability(Abilities.THERMAL_EXCHANGE, "Thermal Exchange", "Boosts the Attack stat when the Pokémon is hit by a Fire-type move. The Pokémon also cannot be burned.", 9) + .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.FIRE, BattleStat.ATK, 1) .attr(StatusEffectImmunityAbAttr, StatusEffect.BURN) .ignorable(), new Ability(Abilities.ANGER_SHELL, "Anger Shell (N)", "When an attack causes its HP to drop to half or less, the Pokémon gets angry. This lowers its Defense and Sp. Def stats but boosts its Attack, Sp. Atk, and Speed stats.", 9), @@ -2875,7 +2898,8 @@ export function initAbilities() { new Ability(Abilities.SUPREME_OVERLORD, "Supreme Overlord (N)", "When the Pokémon enters a battle, its Attack and Sp. Atk stats are slightly boosted for each of the allies in its party that have already been defeated.", 9), new Ability(Abilities.COSTAR, "Costar (N)", "When the Pokémon enters a battle, it copies an ally's stat changes.", 9), new Ability(Abilities.TOXIC_DEBRIS, "Toxic Debris (N)", "Scatters poison spikes at the feet of the opposing team when the Pokémon takes damage from physical moves.", 9), - new Ability(Abilities.ARMOR_TAIL, "Armor Tail (N)", "The mysterious tail covering the Pokémon's head makes opponents unable to use priority moves against the Pokémon or its allies.", 9) + new Ability(Abilities.ARMOR_TAIL, "Armor Tail", "The mysterious tail covering the Pokémon's head makes opponents unable to use priority moves against the Pokémon or its allies.", 9) + .attr(FieldPriorityMoveImmunityAbAttr) .ignorable(), new Ability(Abilities.EARTH_EATER, "Earth Eater", "If hit by a Ground-type move, the Pokémon has its HP restored instead of taking damage.", 9) .attr(TypeImmunityHealAbAttr, Type.GROUND) diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 84bf6077449..572738fd25f 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -89,6 +89,7 @@ export enum CommonAnim { RAIN, SANDSTORM, HAIL, + SNOW, WIND, HEAVY_RAIN, HARSH_SUN, diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 20946c784cb..a95527e6ae4 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -799,7 +799,7 @@ export class TruantTag extends AbilityBattlerTag { } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - if ((!pokemon.canApplyAbility() || pokemon.getAbility().id !== Abilities.TRUANT) && (!pokemon.canApplyAbility(true) || pokemon.getPassiveAbility().id !== Abilities.TRUANT)) + if (!pokemon.hasAbility(Abilities.TRUANT)) return super.lapse(pokemon, lapseType); const passive = pokemon.getAbility().id !== Abilities.TRUANT; @@ -827,7 +827,7 @@ export class SlowStartTag extends AbilityBattlerTag { } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - if ((!pokemon.canApplyAbility() || pokemon.getAbility().id !== this.ability) && (!pokemon.canApplyAbility(true) || pokemon.getPassiveAbility().id !== this.ability)) + if (!pokemon.hasAbility(this.ability)) this.turnCount = 1; return super.lapse(pokemon, lapseType); @@ -1077,6 +1077,7 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc return new TerrainHighestStatBoostTag(tagType, Abilities.QUARK_DRIVE, TerrainType.ELECTRIC); case BattlerTagType.FLYING: case BattlerTagType.UNDERGROUND: + case BattlerTagType.UNDERWATER: case BattlerTagType.HIDDEN: return new HideSpriteTag(tagType, turnCount, sourceMove); case BattlerTagType.FIRE_BOOST: diff --git a/src/data/enums/battler-tag-type.ts b/src/data/enums/battler-tag-type.ts index 85f00753457..2dcb12ea231 100644 --- a/src/data/enums/battler-tag-type.ts +++ b/src/data/enums/battler-tag-type.ts @@ -38,6 +38,7 @@ export enum BattlerTagType { QUARK_DRIVE = "QUARK_DRIVE", FLYING = "FLYING", UNDERGROUND = "UNDERGROUND", + UNDERWATER = "UNDERWATER", HIDDEN = "HIDDEN", FIRE_BOOST = "FIRE_BOOST", CRIT_BOOST = "CRIT_BOOST", diff --git a/src/data/move.ts b/src/data/move.ts index 912b1ad7ae0..1f8fad2c008 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -23,6 +23,7 @@ import { SpeciesFormChangeActiveTrigger } from "./pokemon-forms"; import { Species } from "./enums/species"; import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../ui/command-ui-handler"; +import { Biome } from "./enums/biome"; export enum MoveCategory { PHYSICAL, @@ -274,7 +275,7 @@ export default class Move { checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean { switch (flag) { case MoveFlags.MAKES_CONTACT: - if ((user.canApplyAbility() && user.getAbility().hasAttr(IgnoreContactAbAttr)) || (user.canApplyAbility(true) && user.getPassiveAbility().hasAttr(IgnoreContactAbAttr))) + if (user.hasAbilityWithAttr(IgnoreContactAbAttr)) return false; break; } @@ -776,6 +777,7 @@ export class PlantHealAttr extends WeatherHealAttr { case WeatherType.RAIN: case WeatherType.SANDSTORM: case WeatherType.HAIL: + case WeatherType.SNOW: case WeatherType.HEAVY_RAIN: return 0.25; default: @@ -1755,6 +1757,7 @@ export class AntiSunlightPowerDecreaseAttr extends VariablePowerAttr { case WeatherType.RAIN: case WeatherType.SANDSTORM: case WeatherType.HAIL: + case WeatherType.SNOW: case WeatherType.HEAVY_RAIN: power.value *= 0.5; return true; @@ -1804,6 +1807,28 @@ export class StatChangeCountPowerAttr extends VariablePowerAttr { } } +export class PresentPowerAttr extends VariablePowerAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + + const powerSeed = Utils.randSeedInt(100); + if (powerSeed <= 40) { + (args[0] as Utils.NumberHolder).value = 40; + } + else if (40 < powerSeed && powerSeed <= 70) { + (args[0] as Utils.NumberHolder).value = 80; + } + else if (70 < powerSeed && powerSeed <= 80) { + (args[0] as Utils.NumberHolder).value = 120; + } + else if (80 < powerSeed && powerSeed <= 100) { + target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), + Math.max(Math.floor(target.getMaxHp() / 4), 1), getPokemonMessage(target, ' regained\nhealth!'), true)); + } + + return true; + } +} + export class VariableAtkAttr extends MoveAttr { constructor() { super(); @@ -1904,7 +1929,7 @@ export class BlizzardAccuracyAttr extends VariableAccuracyAttr { if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) { const accuracy = args[0] as Utils.NumberHolder; const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE; - if (weatherType === WeatherType.HAIL) { + if (weatherType === WeatherType.HAIL || weatherType === WeatherType.SNOW) { accuracy.value = -1; return true; } @@ -2016,6 +2041,45 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr { } } +export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) { + const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex; + const type = (args[0] as Utils.IntegerHolder); + + switch (form) { + case 1: // Wellspring Mask + type.value = Type.WATER; + break; + case 2: // Hearthflame Mask + type.value = Type.FIRE; + break; + case 3: // Cornerstone Mask + type.value = Type.ROCK; + break; + case 4: // Teal Mask Tera + type.value = Type.GRASS; + break; + case 5: // Wellspring Mask Tera + type.value = Type.WATER; + break; + case 6: // Hearthflame Mask Tera + type.value = Type.FIRE; + break; + case 7: // Cornerstone Mask Tera + type.value = Type.ROCK; + break; + default: + type.value = Type.GRASS; + break; + } + return true; + } + + return false; + } +} + export class WeatherBallTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) { @@ -2034,6 +2098,7 @@ export class WeatherBallTypeAttr extends VariableMoveTypeAttr { type.value = Type.ROCK; break; case WeatherType.HAIL: + case WeatherType.SNOW: type.value = Type.ICE; break; default: @@ -2779,6 +2844,149 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr { } } +export class NaturePowerAttr extends OverrideMoveEffectAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { + return new Promise(resolve => { + var moveId; + switch (user.scene.arena.getTerrainType()) { + // this allows terrains to 'override' the biome move + case TerrainType.NONE: + switch (user.scene.arena.biomeType) { + case Biome.TOWN: + moveId = Moves.ROUND; + break; + case Biome.METROPOLIS: + moveId = Moves.TRI_ATTACK; + break; + case Biome.SLUM: + moveId = Moves.SLUDGE_BOMB; + break; + case Biome.PLAINS: + moveId = Moves.SILVER_WIND; + break; + case Biome.GRASS: + moveId = Moves.GRASS_KNOT; + break; + case Biome.TALL_GRASS: + moveId = Moves.POLLEN_PUFF; + break; + case Biome.MEADOW: + moveId = Moves.GIGA_DRAIN; + break; + case Biome.FOREST: + moveId = Moves.BUG_BUZZ; + break; + case Biome.JUNGLE: + moveId = Moves.LEAF_STORM; + break; + case Biome.SEA: + moveId = Moves.HYDRO_PUMP; + break; + case Biome.SWAMP: + moveId = Moves.MUD_BOMB; + break; + case Biome.BEACH: + moveId = Moves.SCALD; + break; + case Biome.LAKE: + moveId = Moves.BUBBLE_BEAM; + break; + case Biome.SEABED: + moveId = Moves.BRINE; + break; + case Biome.ISLAND: + moveId = Moves.LEAF_TORNADO; + break; + case Biome.MOUNTAIN: + moveId = Moves.AIR_SLASH; + break; + case Biome.BADLANDS: + moveId = Moves.EARTH_POWER; + break; + case Biome.DESERT: + moveId = Moves.SCORCHING_SANDS; + break; + case Biome.WASTELAND: + moveId = Moves.DRAGON_PULSE; + break; + case Biome.CONSTRUCTION_SITE: + moveId = Moves.STEEL_BEAM; + break; + case Biome.CAVE: + moveId = Moves.POWER_GEM; + break; + case Biome.ICE_CAVE: + moveId = Moves.ICE_BEAM; + break; + case Biome.SNOWY_FOREST: + moveId = Moves.FROST_BREATH; + break; + case Biome.VOLCANO: + moveId = Moves.LAVA_PLUME; + break; + case Biome.GRAVEYARD: + moveId = Moves.SHADOW_BALL; + break; + case Biome.RUINS: + moveId = Moves.ANCIENT_POWER; + break; + case Biome.TEMPLE: + moveId = Moves.EXTRASENSORY; + break; + case Biome.DOJO: + moveId = Moves.FOCUS_BLAST; + break; + case Biome.FAIRY_CAVE: + moveId = Moves.ALLURING_VOICE; + break; + case Biome.ABYSS: + moveId = Moves.OMINOUS_WIND; + break; + case Biome.SPACE: + moveId = Moves.DRACO_METEOR; + break; + case Biome.FACTORY: + moveId = Moves.FLASH_CANNON; + break; + case Biome.LABORATORY: + moveId = Moves.ZAP_CANNON; + break; + case Biome.POWER_PLANT: + moveId = Moves.CHARGE_BEAM; + break; + case Biome.END: + moveId = Moves.ETERNABEAM; + break; + } + break; + case TerrainType.MISTY: + moveId = Moves.MOONBLAST; + break; + case TerrainType.ELECTRIC: + moveId = Moves.THUNDERBOLT; + break; + case TerrainType.GRASSY: + moveId = Moves.ENERGY_BALL; + break; + case TerrainType.PSYCHIC: + moveId = Moves.PSYCHIC; + break; + default: + // Just in case there's no match + moveId = Moves.TRI_ATTACK; + break; + } + + user.getMoveQueue().push({ move: moveId, targets: [target.getBattlerIndex()], ignorePP: true }); + user.scene.unshiftPhase(new MovePhase(user.scene, user, [target.getBattlerIndex()], new PokemonMove(moveId, 0, 0, true), true)); + initMoveAnim(moveId).then(() => { + loadMoveAnimAssets(user.scene, [ moveId ], true) + .then(() => resolve(true)); + }); + }); + } +} + const lastMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { const copiableMove = user.scene.currentBattle.lastMove; @@ -3263,6 +3471,7 @@ export function initMoves() { new AttackMove(Moves.WING_ATTACK, "Wing Attack", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, "The target is struck with large, imposing wings spread wide to inflict damage.", -1, 0, 1), new StatusMove(Moves.WHIRLWIND, "Whirlwind", Type.NORMAL, -1, 20, "The target is blown away, and a different Pokémon is dragged out. In the wild, this ends a battle against a single Pokémon.", -1, -6, 1) .attr(ForceSwitchOutAttr) + .attr(HitsTagAttr, BattlerTagType.FLYING, false) .hidesTarget() .windMove(), new AttackMove(Moves.FLY, "Fly", Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, "The user flies up into the sky and then strikes its target on the next turn.", -1, 0, 1) @@ -3357,7 +3566,8 @@ export function initMoves() { new AttackMove(Moves.WATER_GUN, "Water Gun", Type.WATER, MoveCategory.SPECIAL, 40, 100, 25, "The target is blasted with a forceful shot of water.", -1, 0, 1), new AttackMove(Moves.HYDRO_PUMP, "Hydro Pump", Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, "The target is blasted by a huge volume of water launched under great pressure.", -1, 0, 1), new AttackMove(Moves.SURF, "Surf", Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, "The user attacks everything around it by swamping its surroundings with a giant wave.", -1, 0, 1) - .target(MoveTarget.ALL_NEAR_OTHERS), + .target(MoveTarget.ALL_NEAR_OTHERS) + .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), new AttackMove(Moves.ICE_BEAM, "Ice Beam", Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, "The target is struck with an icy-cold beam of energy. This may also leave the target frozen.", 10, 0, 1) .attr(StatusEffectAttr, StatusEffect.FREEZE), new AttackMove(Moves.BLIZZARD, "Blizzard", Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, "A howling blizzard is summoned to strike opposing Pokémon. This may also leave the opposing Pokémon frozen.", 10, 0, 1) @@ -3436,7 +3646,8 @@ export function initMoves() { .attr(StatusMoveTypeImmunityAttr, Type.GROUND), new AttackMove(Moves.THUNDER, "Thunder", Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, "A wicked thunderbolt is dropped on the target to inflict damage. This may also leave the target with paralysis.", 30, 0, 1) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) - .attr(ThunderAccuracyAttr), + .attr(ThunderAccuracyAttr) + .attr(HitsTagAttr, BattlerTagType.FLYING, false), new AttackMove(Moves.ROCK_THROW, "Rock Throw", Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, "The user picks up and throws a small rock at the target to attack.", -1, 0, 1) .makesContact(false), new AttackMove(Moves.EARTHQUAKE, "Earthquake", Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, "The user sets off an earthquake that strikes every Pokémon around it.", -1, 0, 1) @@ -3446,6 +3657,7 @@ export function initMoves() { new AttackMove(Moves.FISSURE, "Fissure", Type.GROUND, MoveCategory.PHYSICAL, -1, 30, 5, "The user opens up a fissure in the ground and drops the target in. The target faints instantly if this attack hits.", -1, 0, 1) .attr(OneHitKOAttr) .attr(OneHitKOAccuracyAttr) + .attr(HitsTagAttr, BattlerTagType.UNDERGROUND, false) .makesContact(false), new AttackMove(Moves.DIG, "Dig", Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, "The user burrows into the ground, then attacks on the next turn.", -1, 0, 1) .attr(ChargeAttr, ChargeAnim.DIG_CHARGING, 'dug a hole!', BattlerTagType.UNDERGROUND) @@ -3783,7 +3995,8 @@ export function initMoves() { .target(MoveTarget.USER_AND_ALLIES), new AttackMove(Moves.RETURN, "Return", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, "This full-power attack grows more powerful the more the user likes its Trainer.", -1, 0, 2) .attr(FriendshipPowerAttr), - new AttackMove(Moves.PRESENT, "Present (N)", Type.NORMAL, MoveCategory.PHYSICAL, -1, 90, 15, "The user attacks by giving the target a gift with a hidden trap. It restores HP sometimes, however.", -1, 0, 2) + new AttackMove(Moves.PRESENT, "Present", Type.NORMAL, MoveCategory.PHYSICAL, -1, 90, 15, "The user attacks by giving the target a gift with a hidden trap. It restores HP sometimes, however.", -1, 0, 2) + .attr(PresentPowerAttr) .makesContact(false), new AttackMove(Moves.FRUSTRATION, "Frustration", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, "This full-power attack grows more powerful the less the user likes its Trainer.", -1, 0, 2) .attr(FriendshipPowerAttr, true), @@ -3799,6 +4012,7 @@ export function initMoves() { new AttackMove(Moves.MAGNITUDE, "Magnitude", Type.GROUND, MoveCategory.PHYSICAL, -1, 100, 30, "The user attacks everything around it with a ground-shaking quake. Its power varies.", -1, 0, 2) .attr(PreMoveMessageAttr, magnitudeMessageFunc) .attr(MagnitudePowerAttr) + .attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.DYNAMIC_PUNCH, "Dynamic Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 50, 5, "The user punches the target with full, concentrated power. This confuses the target if it hits.", 100, 0, 2) @@ -3867,7 +4081,8 @@ export function initMoves() { new AttackMove(Moves.ROCK_SMASH, "Rock Smash", Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, "The user attacks with a punch. This may also lower the target's Defense stat.", 50, 0, 2) .attr(StatChangeAttr, BattleStat.DEF, -1), new AttackMove(Moves.WHIRLPOOL, "Whirlpool", Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, "The user traps the target in a violent swirling whirlpool for four to five turns.", 100, 0, 2) - .attr(TrapAttr, BattlerTagType.WHIRLPOOL), + .attr(TrapAttr, BattlerTagType.WHIRLPOOL) + .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), new AttackMove(Moves.BEAT_UP, "Beat Up (N)", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, "The user gets all party Pokémon to attack the target. The more party Pokémon, the greater the number of attacks.", -1, 0, 2) .makesContact(false), new AttackMove(Moves.FAKE_OUT, "Fake Out", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, "This attack hits first and makes the target flinch. It only works the first turn each time the user enters battle.", 100, 3, 3) @@ -3909,7 +4124,9 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS), new SelfStatusMove(Moves.FOLLOW_ME, "Follow Me (N)", Type.NORMAL, -1, 20, "The user draws attention to itself, making all targets take aim only at the user.", -1, 2, 3), - new StatusMove(Moves.NATURE_POWER, "Nature Power (N)", Type.NORMAL, -1, 20, "This attack makes use of nature's power. Its effects vary depending on the user's environment.", -1, 0, 3), + new StatusMove(Moves.NATURE_POWER, "Nature Power", Type.NORMAL, -1, 20, "This attack makes use of nature's power. Its effects vary depending on the user's environment.", -1, 0, 3) + .attr(NaturePowerAttr) + .ignoresVirtual(), new SelfStatusMove(Moves.CHARGE, "Charge (P)", Type.ELECTRIC, -1, 20, "The user boosts the power of the Electric move it uses on the next turn. This also raises the user's Sp. Def stat.", -1, 0, 3) .attr(StatChangeAttr, BattleStat.SPDEF, 1, true), new StatusMove(Moves.TAUNT, "Taunt (N)", Type.DARK, 100, 20, "The target is taunted into a rage that allows it to use only attack moves for three turns.", -1, 0, 3), @@ -3955,7 +4172,7 @@ export function initMoves() { new AttackMove(Moves.SECRET_POWER, "Secret Power (P)", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, "The additional effects of this attack depend upon where it was used.", 30, 0, 3) .makesContact(false), new AttackMove(Moves.DIVE, "Dive", Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, "Diving on the first turn, the user floats up and attacks on the next turn.", -1, 0, 3) - .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, 'hid\nunderwater!', BattlerTagType.UNDERGROUND) + .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, 'hid\nunderwater!', BattlerTagType.UNDERWATER) .ignoresVirtual(), new AttackMove(Moves.ARM_THRUST, "Arm Thrust", Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, "The user lets loose a flurry of open-palmed arm thrusts that hit two to five times in a row.", -1, 0, 3) .attr(MultiHitAttr), @@ -4008,7 +4225,7 @@ export function initMoves() { .attr(FlinchAttr), new AttackMove(Moves.WEATHER_BALL, "Weather Ball", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, "This attack move varies in power and type depending on the weather.", -1, 0, 3) .attr(WeatherBallTypeAttr) - .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) .ballBombMove(), new StatusMove(Moves.AROMATHERAPY, "Aromatherapy (N)", Type.GRASS, -1, 5, "The user releases a soothing scent that heals all status conditions affecting the user's party.", -1, 0, 3) .target(MoveTarget.USER_AND_ALLIES), @@ -4425,6 +4642,7 @@ export function initMoves() { .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.SMACK_DOWN, "Smack Down", Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, "The user throws a stone or similar projectile to attack the target. A flying Pokémon will fall to the ground when it's hit.", 100, 0, 5) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 5) + .attr(HitsTagAttr, BattlerTagType.FLYING, false) .makesContact(false), new AttackMove(Moves.STORM_THROW, "Storm Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, "The user strikes the target with a fierce blow. This attack always results in a critical hit.", -1, 0, 5) .attr(CritOnlyAttr), @@ -4565,6 +4783,7 @@ export function initMoves() { new AttackMove(Moves.HURRICANE, "Hurricane", Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, "The user attacks by wrapping its opponent in a fierce wind that flies up into the sky. This may also confuse the target.", 30, 0, 5) .attr(ThunderAccuracyAttr) .attr(ConfuseAttr) + .attr(HitsTagAttr, BattlerTagType.FLYING, false) .windMove(), new AttackMove(Moves.HEAD_CHARGE, "Head Charge", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, "The user charges its head into its target, using its powerful guard hair. This also damages the user a little.", -1, 0, 5) .attr(RecoilAttr), @@ -4715,9 +4934,9 @@ export function initMoves() { .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true) .ignoresVirtual(), new StatusMove(Moves.MAGNETIC_FLUX, "Magnetic Flux", Type.ELECTRIC, -1, 20, "The user manipulates magnetic fields, which raises the Defense and Sp. Def stats of ally Pokémon with the Plus or Minus Ability.", -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => a === user.getAbility().id || (user.canApplyPassive() && a === user.getPassiveAbility().id))) + .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => a === p.getAbility().id || (user.canApplyPassive() && a === user.getPassiveAbility().id)))), + .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))), new StatusMove(Moves.HAPPY_HOUR, "Happy Hour (N)", Type.NORMAL, -1, 30, "Using Happy Hour doubles the amount of prize money received after battle.", -1, 0, 6) // No animation .target(MoveTarget.USER_SIDE), new StatusMove(Moves.ELECTRIC_TERRAIN, "Electric Terrain", Type.ELECTRIC, -1, 10, "The user electrifies the ground for five turns, powering up Electric-type moves. Pokémon on the ground no longer fall asleep.", -1, 0, 6) @@ -4744,6 +4963,7 @@ export function initMoves() { .triageMove(), new AttackMove(Moves.THOUSAND_ARROWS, "Thousand Arrows", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, "This move also hits opposing Pokémon that are in the air. Those Pokémon are knocked down to the ground.", 100, 0, 6) .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) + .attr(HitsTagAttr, BattlerTagType.FLYING, false) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.THOUSAND_WAVES, "Thousand Waves", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, "The user attacks with a wave that crawls along the ground. Those it hits can't flee from battle.", -1, 0, 6) @@ -4845,9 +5065,9 @@ export function initMoves() { new SelfStatusMove(Moves.LASER_FOCUS, "Laser Focus", Type.NORMAL, -1, 30, "The user concentrates intensely. The attack on the next turn always results in a critical hit.", -1, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false), new StatusMove(Moves.GEAR_UP, "Gear Up", Type.STEEL, -1, 20, "The user engages its gears to raise the Attack and Sp. Atk stats of ally Pokémon with the Plus or Minus Ability.", -1, 0, 7) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => [ Abilities.PLUS, Abilities.MINUS ].includes(target.getAbility().id) || (target.canApplyPassive() && [ Abilities.PLUS, Abilities.MINUS ].includes(target.getPassiveAbility().id))) + .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false))) .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].find(p => p && [ Abilities.PLUS, Abilities.MINUS ].includes(p.getAbility().id) || (target.canApplyPassive() && [ Abilities.PLUS, Abilities.MINUS ].includes(target.getPassiveAbility().id)))), + .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))), new AttackMove(Moves.THROAT_CHOP, "Throat Chop (P)", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, "The user attacks the target's throat, and the resultant suffering prevents the target from using moves that emit sound for two turns.", 100, 0, 7), new AttackMove(Moves.POLLEN_PUFF, "Pollen Puff (P)", Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, "The user attacks the enemy with a pollen puff that explodes. If the target is an ally, it gives the ally a pollen puff that restores its HP instead.", -1, 0, 7) .ballBombMove(), @@ -4886,8 +5106,8 @@ export function initMoves() { new AttackMove(Moves.DRAGON_HAMMER, "Dragon Hammer", Type.DRAGON, MoveCategory.PHYSICAL, 90, 100, 15, "The user uses its body like a hammer to attack the target and inflict damage.", -1, 0, 7), new AttackMove(Moves.BRUTAL_SWING, "Brutal Swing", Type.DARK, MoveCategory.PHYSICAL, 60, 100, 20, "The user swings its body around violently to inflict damage on everything in its vicinity.", -1, 0, 7) .target(MoveTarget.ALL_NEAR_OTHERS), - new StatusMove(Moves.AURORA_VEIL, "Aurora Veil", Type.ICE, -1, 20, "This move reduces damage from physical and special moves for five turns. This can be used only in a hailstorm.", -1, 0, 7) - .condition((user, target, move) => user.scene.arena.weather?.weatherType === WeatherType.HAIL && !user.scene.arena.weather?.isEffectSuppressed(user.scene)) + new StatusMove(Moves.AURORA_VEIL, "Aurora Veil", Type.ICE, -1, 20, "This move reduces damage from physical and special moves for five turns. This can be used only when it is snowing.", -1, 0, 7) + .condition((user, target, move) => (user.scene.arena.weather?.weatherType === WeatherType.HAIL || user.scene.arena.weather?.weatherType === WeatherType.SNOW) && !user.scene.arena.weather?.isEffectSuppressed(user.scene)) .attr(AddArenaTagAttr, ArenaTagType.AURORA_VEIL, 5, true) .target(MoveTarget.USER_SIDE), /* Unused */ @@ -5407,13 +5627,13 @@ export function initMoves() { .makesContact(), new SelfStatusMove(Moves.SHED_TAIL, "Shed Tail (N)", Type.NORMAL, -1, 10, "The user creates a substitute for itself using its own HP before switching places with a party Pokémon in waiting.", -1, 0, 9), new StatusMove(Moves.CHILLY_RECEPTION, "Chilly Reception", Type.ICE, -1, 10, "The user tells a chillingly bad joke before switching places with a party Pokémon in waiting. This summons a snowstorm lasting five turns.", -1, 0, 9) - .attr(WeatherChangeAttr, WeatherType.HAIL) // Set to Hail for now, if Snow is added in the future, change this + .attr(WeatherChangeAttr, WeatherType.SNOW) .attr(ForceSwitchOutAttr, true, false) .target(MoveTarget.BOTH_SIDES), new SelfStatusMove(Moves.TIDY_UP, "Tidy Up (P)", Type.NORMAL, -1, 10, "The user tidies up and removes the effects of Spikes, Stealth Rock, Sticky Web, Toxic Spikes, and Substitute. This also boosts the user's Attack and Speed stats.", 100, 0, 9) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPD ], 1, true), new StatusMove(Moves.SNOWSCAPE, "Snowscape", Type.ICE, -1, 10, "The user summons a snowstorm lasting five turns. This boosts the Defense stats of Ice types.", -1, 0, 9) - .attr(WeatherChangeAttr, WeatherType.HAIL) // Set to Hail for now, if Snow is added in the future, change this + .attr(WeatherChangeAttr, WeatherType.SNOW) .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.POUNCE, "Pounce", Type.BUG, MoveCategory.PHYSICAL, 50, 100, 20, "The user attacks by pouncing on the target. This also lowers the target's Speed stat.", 100, 0, 9) .attr(StatChangeAttr, BattleStat.SPD, -1), @@ -5477,7 +5697,8 @@ export function initMoves() { new AttackMove(Moves.SYRUP_BOMB, "Syrup Bomb (P)", Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, "The user sets off an explosion of sticky candy syrup, which coats the target and causes the target's Speed stat to drop each turn for three turns.", -1, 0, 9) .attr(StatChangeAttr, BattleStat.SPD, -1) //Temporary .ballBombMove(), - new AttackMove(Moves.IVY_CUDGEL, "Ivy Cudgel (P)", Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit.", -1, 0, 9) + new AttackMove(Moves.IVY_CUDGEL, "Ivy Cudgel", Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit.", -1, 0, 9) + .attr(IvyCudgelTypeAttr) .attr(HighCritAttr) .makesContact(false), new AttackMove(Moves.ELECTRO_SHOT, "Electro Shot", Type.ELECTRIC, MoveCategory.SPECIAL, 130, 100, 10, "The user gathers electricity on the first turn, boosting its Sp. Atk stat, then fires a high-voltage shot on the next turn. The shot will be fired immediately in rain.", 100, 0, 9) diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index 290deb39eaa..f76c3ef2c9e 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -968,8 +968,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.COSMOEM, 43, null, null) ], [Species.COSMOEM]: [ - new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType !== Biome.SPACE && p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null), - new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType !== Biome.SPACE && p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), null) + new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null), + new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), null) ], [Species.MELTAN]: [ new SpeciesEvolution(Species.MELMETAL, 48, null, null) diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index e5bcf25a4fe..bb22f985976 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -535,8 +535,8 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangeActiveTrigger(false), true) ], [Species.AEGISLASH]: [ - new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.getAbility().id === Abilities.STANCE_CHANGE)), - new SpeciesFormChange(Species.AEGISLASH, 'shield', 'blade', new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.getAbility().id === Abilities.STANCE_CHANGE)), + new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))), + new SpeciesFormChange(Species.AEGISLASH, 'shield', 'blade', new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))), new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangeActiveTrigger(false), true) ], [Species.DIANCIE]: [ @@ -549,6 +549,22 @@ export const pokemonFormChanges: PokemonFormChanges = { new SpeciesFormChange(Species.WISHIWASHI, '', 'school', new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.WISHIWASHI, 'school', '', new SpeciesFormChangeManualTrigger(), true) ], + [Species.MINIOR]: [ + new SpeciesFormChange(Species.MINIOR, 'red-meteor', 'red', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'red', 'red-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'orange-meteor', 'orange', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'orange', 'orange-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'yellow-meteor', 'yellow', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'yellow', 'yellow-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'green-meteor', 'green', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'green', 'green-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'blue-meteor', 'blue', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'blue', 'blue-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'indigo-meteor', 'indigo', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'indigo', 'indigo-meteor', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'violet-meteor', 'violet', new SpeciesFormChangeManualTrigger(), true), + new SpeciesFormChange(Species.MINIOR, 'violet', 'violet-meteor', new SpeciesFormChangeManualTrigger(), true) + ], [Species.NECROZMA]: [ new SpeciesFormChange(Species.NECROZMA, '', 'dawn-wings', new SpeciesFormChangeItemTrigger(FormChangeItem.N_LUNARIZER)), new SpeciesFormChange(Species.NECROZMA, '', 'dusk-mane', new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER)) diff --git a/src/data/splash-messages.ts b/src/data/splash-messages.ts index 32a48d03063..198ff07cec9 100644 --- a/src/data/splash-messages.ts +++ b/src/data/splash-messages.ts @@ -15,6 +15,7 @@ splashMessages.push(...[ 'Now with 33% More Salt!', 'Infinite Fusion at Home!', 'Broken Egg Moves!', + 'Magnificent!', 'Mubstitute!', 'That\'s Crazy!', 'Orance Juice!', diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index 8d7f2e4fdce..a1c83af0779 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -79,7 +79,7 @@ export const trainerNamePools = { [TrainerType.BEAUTY]: ["Cassie","Julia","Olivia","Samantha","Valerie","Victoria","Bridget","Connie","Jessica","Johanna","Melissa","Sheila","Shirley","Tiffany","Namiko","Thalia","Grace","Lola","Lori","Maura","Tamia","Cyndy","Devon","Gabriella","Harley","Lindsay","Nicola","Callie","Charlotte","Kassandra","December","Fleming","Nikola","Aimee","Anais","Brigitte","Cassandra","Andrea","Brittney","Carolyn","Krystal","Alexis","Alice","Aina","Anya","Arianna","Aubrey","Beverly","Camille","Beauty","Evette","Hansol","Haruka","Jill","Jo","Lana","Lois","Lucy","Mai","Nickie","Nicole","Prita","Rose","Shelly","Suzy","Tessa","Anita","Alissa","Rita","Cudsy","Eloff","Miru","Minot","Nevah","Niven","Ogoin"], [TrainerType.BIKER]: ["Charles","Dwayne","Glenn","Harris","Joel","Riley","Zeke","Alex","Billy","Ernest","Gerald","Hideo","Isaac","Jared","Jaren","Jaxon","Jordy","Lao","Lukas","Malik","Nikolas","Ricardo","Ruben","Virgil","William","Aiden","Dale","Dan","Jacob","Markey","Reese","Teddy","Theron","Jeremy","Morgann","Phillip","Philip","Stanley","Dillon"], [TrainerType.BLACK_BELT]: [["Kenji","Lao","Lung","Nob","Wai","Yoshi","Atsushi","Daisuke","Hideki","Hitoshi","Kiyo","Koichi","Koji","Yuji","Cristian","Rhett","Takao","Theodore","Zander","Aaron","Hugh","Mike","Nicolas","Shea","Takashi","Adam","Carl","Colby","Darren","David","Davon","Derek","Eddie","Gregory","Griffin","Jarrett","Jeffery","Kendal","Kyle","Luke","Miles","Nathaniel","Philip","Rafael","Ray","Ricky","Sean","Willie","Ander","Manford","Benjamin","Corey","Edward","Grant","Jay","Kendrew","Kentaro","Ryder","Teppei","Thomas","Tyrone","Andrey","Donny","Drago","Gordon","Grigor","Jeriel","Kenneth","Martell","Mathis","Rich","Rocky","Rodrigo","Wesley","Zachery","Alonzo","Cadoc","Gunnar","Igor","Killian","Markus","Ricardo","Yanis","Banting","Clayton","Duane","Earl","Greg","Roy","Terry","Tracy","Walter","Alvaro","Curtis","Francis","Ross","Brice","Cheng","Dudley","Eric","Kano","Masahiro","Randy","Ryuji","Steve","Tadashi","Wong","Yuen","Brian","Carter","Reece","Nick","Yang"],["Cora","Cyndy","Jill","Laura","Sadie","Tessa","Vivian","Aisha","Callie","Danielle","Helene","Jocelyn","Lilith","Paula","Reyna","Helen","Kelsey","Tyler","Amy","Chandra","Hillary","Janie","Lee","Maggie","Mikiko","Miriam","Sharon","Susie","Xiao","Alize","Azra","Brenda","Chalina","Chan","Glinda","Maki","Tia","Tiffany","Wendy","Andrea","Gabrielle","Gerardine","Hailey","Hedvig","Justine","Kinsey","Sigrid","Veronique","Tess"]], - [TrainerType.BREEDER]: [["Isaac","Myles","Salvadore","Allison","Alize","Bethany","Lily","Albert","Kahlil","Eustace","Galen","Owen","Addison","Marcus","Foster","Cory","Glenn","Jay","Wesley","William","Adrian","Bradley","Jaime"],["Lydia","Gabrielle","Jayden","Pat","Veronica","Amber","Jennifer","Kaylee","Adelaide","Brooke","Ethel","April","Irene","Magnolia","Amala","Mercy","Amanda","Ikue","Savannah","Yuka","Chloe","Debra","Denise","Elena"]], + [TrainerType.BREEDER]: [["Isaac","Myles","Salvadore","Albert","Kahlil","Eustace","Galen","Owen","Addison","Marcus","Foster","Cory","Glenn","Jay","Wesley","William","Adrian","Bradley","Jaime"],["Allison","Alize","Bethany","Lily","Lydia","Gabrielle","Jayden","Pat","Veronica","Amber","Jennifer","Kaylee","Adelaide","Brooke","Ethel","April","Irene","Magnolia","Amala","Mercy","Amanda","Ikue","Savannah","Yuka","Chloe","Debra","Denise","Elena"]], [TrainerType.CLERK]: [["Chaz","Clemens","Doug","Fredric","Ivan","Isaac","Nelson","Wade","Warren","Augustin","Gilligan","Cody","Jeremy","Shane","Dugal","Royce","Ronald"],["Alberta","Ingrid","Katie","Piper","Trisha","Wren","Britney","Lana","Jessica","Kristen","Michelle","Gabrielle"]], [TrainerType.CYCLIST]: [["Axel","James","John","Ryan","Hector","Jeremiah"],["Kayla","Megan","Nicole","Rachel","Krissa","Adelaide"]], [TrainerType.DANCER]: ["Brian","Davey","Dirk","Edmond","Mickey","Raymond","Cara","Julia","Maika","Mireille","Ronda","Zoe"], diff --git a/src/data/weather.ts b/src/data/weather.ts index b3a59f34b98..1409920a1bc 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -14,6 +14,7 @@ export enum WeatherType { RAIN, SANDSTORM, HAIL, + SNOW, FOG, HEAVY_RAIN, HARSH_SUN, @@ -108,7 +109,7 @@ export class Weather { for (let pokemon of field) { let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr; if (!suppressWeatherEffectAbAttr) - suppressWeatherEffectAbAttr = pokemon.canApplyPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null; + suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null; if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) return true; } @@ -127,6 +128,8 @@ export function getWeatherStartMessage(weatherType: WeatherType): string { return 'A sandstorm brewed!'; case WeatherType.HAIL: return 'It started to hail!'; + case WeatherType.SNOW: + return 'It started to snow!'; case WeatherType.FOG: return 'A thick fog emerged!' case WeatherType.HEAVY_RAIN: @@ -150,6 +153,8 @@ export function getWeatherLapseMessage(weatherType: WeatherType): string { return 'The sandstorm rages.'; case WeatherType.HAIL: return 'Hail continues to fall.'; + case WeatherType.SNOW: + return 'The snow is falling down.'; case WeatherType.FOG: return 'The fog continues.'; case WeatherType.HEAVY_RAIN: @@ -184,6 +189,8 @@ export function getWeatherClearMessage(weatherType: WeatherType): string { return 'The sandstorm subsided.'; case WeatherType.HAIL: return 'The hail stopped.'; + case WeatherType.SNOW: + return 'The snow stopped.'; case WeatherType.FOG: return 'The fog disappeared.' case WeatherType.HEAVY_RAIN: @@ -292,11 +299,6 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a { weatherType: WeatherType.RAIN, weight: 1 } ]; break; - case Biome.MOUNTAIN: - weatherPool = [ - { weatherType: WeatherType.NONE, weight: 1 } - ]; - break; case Biome.BADLANDS: weatherPool = [ { weatherType: WeatherType.NONE, weight: 8 }, @@ -314,6 +316,8 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a break; case Biome.ICE_CAVE: weatherPool = [ + { weatherType: WeatherType.NONE, weight: 3 }, + { weatherType: WeatherType.SNOW, weight: 4 }, { weatherType: WeatherType.HAIL, weight: 1 } ]; break; @@ -334,20 +338,25 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a { weatherType: WeatherType.FOG, weight: 1 } ]; break; - case Biome.RUINS: + case Biome.JUNGLE: weatherPool = [ - { weatherType: WeatherType.NONE, weight: 4 } + { weatherType: WeatherType.NONE, weight: 8 }, + { weatherType: WeatherType.RAIN, weight: 2 } ]; break; - case Biome.WASTELAND: + case Biome.SNOWY_FOREST: weatherPool = [ - { weatherType: WeatherType.NONE, weight: 4 } + { weatherType: WeatherType.SNOW, weight: 7 }, + { weatherType: WeatherType.HAIL, weight: 1 } ]; break; - case Biome.ABYSS: + case Biome.ISLAND: weatherPool = [ - { weatherType: WeatherType.NONE, weight: 4 } + { weatherType: WeatherType.NONE, weight: 5 }, + { weatherType: WeatherType.RAIN, weight: 1 }, ]; + if (hasSun) + weatherPool.push({ weatherType: WeatherType.SUNNY, weight: 2 }); break; } diff --git a/src/field/arena.ts b/src/field/arena.ts index 73b6d87bea1..910d998d68e 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -166,6 +166,20 @@ export class Arena { return 2; } break; + case Species.ROTOM: + switch (this.biomeType) { + case Biome.VOLCANO: + return 1; + case Biome.SEA: + return 2; + case Biome.ICE_CAVE: + return 3; + case Biome.MOUNTAIN: + return 4; + case Biome.TALL_GRASS: + return 5; + } + break; case Species.SCATTERBUG: case Species.SPEWPA: case Species.VIVILLON: diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index c396ca37bcc..bf7073ad0bf 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -25,7 +25,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import { BattlerIndex } from '../battle'; @@ -523,6 +523,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ret >>= 1; break; case Stat.DEF: + if (this.isOfType(Type.ICE) && this.scene.arena.weather?.weatherType === WeatherType.SNOW) + ret *= 1.5; break; case Stat.SPATK: break; @@ -561,7 +563,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let value = Math.floor(((2 * baseStat + this.ivs[s]) * this.level) * 0.01); if (isHp) { value = value + this.level + 10; - if ((this.canApplyAbility() && this.getAbility().hasAttr(NonSuperEffectiveImmunityAbAttr)) || (this.canApplyAbility(true) && this.getPassiveAbility().hasAttr(NonSuperEffectiveImmunityAbAttr))) + if (this.hasAbility(Abilities.WONDER_GUARD, false, true)) value = 1; if (this.hp > value || this.hp === undefined) this.hp = value; @@ -730,12 +732,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return allAbilities[starterPassiveAbilities[starterSpeciesId]]; } - canApplyPassive(): boolean { + hasPassive(): boolean { return this.passive || this.isBoss(); } canApplyAbility(passive: boolean = false): boolean { - if (passive && !this.canApplyPassive()) + if (passive && !this.hasPassive()) return false; const ability = (!passive ? this.getAbility() : this.getPassiveAbility()); if (ability.isIgnorable && this.scene.arena.ignoreAbilities) @@ -743,6 +745,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); } + hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean { + if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability) + return true; + if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().id === ability) + return true; + return false; + } + + hasAbilityWithAttr(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): boolean { + if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) + return true; + if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType)) + return true; + return false; + } + getWeight(): number { const weight = new Utils.NumberHolder(this.species.weight); // This will trigger the ability overlay so only call this function when necessary @@ -850,10 +868,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return true; }); } else - levelMoves = this.getSpeciesForm().getLevelMoves(); + levelMoves = this.getSpeciesForm(true).getLevelMoves(); if (this.fusionSpecies) { const evolutionLevelMoves = levelMoves.slice(0, Math.max(levelMoves.findIndex(lm => !!lm[0]), 0)); - const fusionLevelMoves = this.getFusionSpeciesForm().getLevelMoves(); + const fusionLevelMoves = this.getFusionSpeciesForm(true).getLevelMoves(); const newLevelMoves: LevelMoves = []; while (levelMoves.length && levelMoves[0][0] < startingLevel) levelMoves.shift(); @@ -1114,7 +1132,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let result: HitResult; const move = battlerMove.getMove(); let damage = new Utils.NumberHolder(0); - + const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField(); + const variableCategory = new Utils.IntegerHolder(move.category); applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory); const moveCategory = variableCategory.value as MoveCategory; @@ -1156,8 +1175,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!typeless) applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); - if (!cancelled.value) + if (!cancelled.value) { applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); + defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, battlerMove, cancelled, typeMultiplier)); + } if (cancelled.value) result = HitResult.NO_EFFECT; @@ -1186,7 +1207,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(HighCritAttr, source, this, move, critLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); const bonusCrit = new Utils.BooleanHolder(false); - if (applyAbAttrs(BonusCritAbAttr, this, null, bonusCrit)) { + if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { if (bonusCrit.value) critLevel.value += 1; } @@ -1278,6 +1299,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); + if (power.value === 0) { + damage.value = 0; + } + console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef); if (damage.value) { @@ -1328,8 +1353,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { case MoveCategory.STATUS: if (!typeless) applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); - if (!cancelled.value) + if (!cancelled.value) { applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); + defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, battlerMove, cancelled, typeMultiplier)); + } if (!typeMultiplier.value) this.scene.queueMessage(`It doesn\'t affect ${this.name}!`); result = cancelled.value || !typeMultiplier.value ? HitResult.NO_EFFECT : HitResult.STATUS; @@ -2260,6 +2287,7 @@ export class PlayerPokemon extends Pokemon { if (newEvolution.condition.predicate(this)) { const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.ivs, this.nature); newPokemon.natureOverride = this.natureOverride; + newPokemon.moveset = this.moveset.slice(); newPokemon.fusionSpecies = this.fusionSpecies; newPokemon.fusionFormIndex = this.fusionFormIndex; newPokemon.fusionAbilityIndex = this.fusionAbilityIndex; diff --git a/src/loading-scene.ts b/src/loading-scene.ts index a3b92112990..53933294491 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -6,6 +6,7 @@ import { getBiomeHasProps } from "./field/arena"; import CacheBustedLoaderPlugin from "./plugins/cache-busted-loader-plugin"; import { SceneBase } from "./scene-base"; import { WindowVariant, getWindowVariantSuffix } from "./ui/ui-theme"; +import { isMobile } from "./touch-controls"; import * as Utils from "./utils"; export class LoadingScene extends SceneBase { @@ -23,6 +24,9 @@ export class LoadingScene extends SceneBase { this.load['cacheBuster'] = buildIdMatch[1]; } + if (!isMobile()) + this.load.video('intro_dark', 'images/intro_dark.mp4', true); + this.loadImage('loading_bg', 'arenas'); this.loadImage('logo', ''); @@ -250,6 +254,10 @@ export class LoadingScene extends SceneBase { } loadLoadingScreen() { + const mobile = isMobile(); + + const loadingGraphics: any[] = []; + const bg = this.add.image(0, 0, ''); bg.setOrigin(0, 0); bg.setScale(6); @@ -294,6 +302,10 @@ export class LoadingScene extends SceneBase { }); assetText.setOrigin(0.5, 0.5); + const intro = this.add.video(0, 0); + intro.setOrigin(0, 0); + intro.setScale(3); + this.load.on("progress", (value: string) => { const parsedValue = parseFloat(value); percentText.setText(`${Math.floor(parsedValue * 100)}%`); @@ -305,28 +317,51 @@ export class LoadingScene extends SceneBase { this.load.on("fileprogress", file => { assetText.setText(`Loading asset: ${file.key}`); }); + + loadingGraphics.push(bg, graphics, progressBar, progressBox, logo, percentText, assetText); - this.load.on('filecomplete', key => { - switch (key) { - case 'loading_bg': - bg.setVisible(true); - bg.setTexture('loading_bg'); - break; - case 'logo': - logo.setVisible(true); - logo.setTexture('logo'); - break; - } - }); + if (!mobile) + loadingGraphics.map(g => g.setVisible(false)); - this.load.on("complete", () => { + const destroyLoadingAssets = () => { + intro.destroy(); bg.destroy(); logo.destroy(); progressBar.destroy(); progressBox.destroy(); percentText.destroy(); assetText.destroy(); + }; + + this.load.on('filecomplete', key => { + switch (key) { + case 'intro_dark': + intro.load('intro_dark'); + intro.on('complete', () => { + this.tweens.add({ + targets: intro, + duration: 500, + alpha: 0, + ease: 'Sine.easeIn' + }); + loadingGraphics.map(g => g.setVisible(true)); + }); + intro.play(); + break; + case 'loading_bg': + bg.setTexture('loading_bg'); + if (mobile) + bg.setVisible(true); + break; + case 'logo': + logo.setTexture('logo'); + if (mobile) + logo.setVisible(true); + break; + } }); + + this.load.on("complete", () => destroyLoadingAssets()); } get gameHeight() { diff --git a/src/main.ts b/src/main.ts index 4c22606146e..13aa5076ff4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -44,6 +44,7 @@ const config: Phaser.Types.Core.GameConfig = { touch: { target: 'app' }, + gamepad: true }, dom: { createContainer: true @@ -78,4 +79,4 @@ document.fonts.load('16px emerald').then(() => document.fonts.load('10px pkmnems const game = new Phaser.Game(config); game.sound.pauseOnBlur = false; -export default game; \ No newline at end of file +export default game; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 258e79e3651..8078a7c06b9 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1341,7 +1341,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base new ModifierTypeOption(modifierTypes.FULL_RESTORE(), 0, baseCost * 2.25) ], [ - new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 12) + new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 10) ] ]; return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); diff --git a/src/phases.ts b/src/phases.ts index 7e5a3aab1d6..f668278d273 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -379,6 +379,16 @@ export class UnavailablePhase extends Phase { } } +export class OutdatedPhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + this.scene.ui.setMode(Mode.OUTDATED); + } +} + export class SelectGenderPhase extends Phase { constructor(scene: BattleScene) { super(scene); @@ -2190,7 +2200,7 @@ export class MovePhase extends BattlePhase { for (let opponent of targetedOpponents) { if (this.move.ppUsed === this.move.getMove().pp) break; - if ((opponent.canApplyAbility() && opponent.getAbility().hasAttr(IncreasePpAbAttr)) || (opponent.canApplyAbility(true) && opponent.getPassiveAbility().hasAttr(IncreasePpAbAttr))) + if (opponent.hasAbilityWithAttr(IncreasePpAbAttr)) this.move.ppUsed = Math.min(this.move.ppUsed + 1, this.move.getMovePp()); } } @@ -2389,7 +2399,7 @@ export class MoveEffectPhase extends PokemonPhase { } Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove()).then(() => { - return Utils.executeIf(!target.isFainted(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => { + return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => { if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target); })).then(() => { diff --git a/src/plugins/cache-busted-loader-plugin.ts b/src/plugins/cache-busted-loader-plugin.ts index 5a1cbf70a94..e1ee78709df 100644 --- a/src/plugins/cache-busted-loader-plugin.ts +++ b/src/plugins/cache-busted-loader-plugin.ts @@ -1,5 +1,7 @@ let cacheBuster = ''; +const ignoredFiles = [ 'intro_dark' ]; + export default class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin { constructor(scene: Phaser.Scene) { super(scene) @@ -15,9 +17,9 @@ export default class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin addFile(file): void { if (!Array.isArray(file)) - file = [ file ] - - if (cacheBuster) + file = [ file ]; + + if (!ignoredFiles.includes(file?.key) && cacheBuster) file.forEach(item => item.url += '?v=' + cacheBuster); super.addFile(file); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 78ed699e954..596f2b95459 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -27,6 +27,7 @@ import { Moves } from "../data/enums/moves"; import { speciesEggMoves } from "../data/egg-moves"; import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; +import { OutdatedPhase, UnavailablePhase } from "#app/phases"; const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary @@ -183,7 +184,7 @@ const systemShortKeys = { ivs: '$i', moveset: '$m', eggMoves: '$em', - candyCount: '$cc', + candyCount: '$x', passive: '$p', valueReduction: '$vr' }; @@ -269,6 +270,10 @@ export class GameData { .then(error => { this.scene.ui.savingIcon.hide(); if (error) { + if (error.startsWith('client version out of date')) { + this.scene.clearPhaseQueue(); + this.scene.unshiftPhase(new OutdatedPhase(this.scene)); + } console.error(error); return resolve(false); } diff --git a/src/system/settings.ts b/src/system/settings.ts index 09d5dde94a8..77dd1101716 100644 --- a/src/system/settings.ts +++ b/src/system/settings.ts @@ -19,6 +19,7 @@ export enum Setting { HP_Bar_Speed = "HP_BAR_SPEED", Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS", Player_Gender = "PLAYER_GENDER", + Gamepad_Support = "GAMEPAD_SUPPORT", Touch_Controls = "TOUCH_CONTROLS", Vibration = "VIBRATION" } @@ -47,6 +48,7 @@ export const settingOptions: SettingOptions = { [Setting.HP_Bar_Speed]: [ 'Normal', 'Fast', 'Faster', 'Instant' ], [Setting.Fusion_Palette_Swaps]: [ 'Off', 'On' ], [Setting.Player_Gender]: [ 'Boy', 'Girl' ], + [Setting.Gamepad_Support]: [ 'Auto', 'Disabled' ], [Setting.Touch_Controls]: [ 'Auto', 'Disabled' ], [Setting.Vibration]: [ 'Auto', 'Disabled' ] }; @@ -67,6 +69,7 @@ export const settingDefaults: SettingDefaults = { [Setting.HP_Bar_Speed]: 0, [Setting.Fusion_Palette_Swaps]: 1, [Setting.Player_Gender]: 0, + [Setting.Gamepad_Support]: 0, [Setting.Touch_Controls]: 0, [Setting.Vibration]: 0 }; @@ -130,6 +133,9 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer) } else return false; break; + case Setting.Gamepad_Support: + scene.gamepadSupport = settingOptions[setting][value] !== 'Disabled'; + break; case Setting.Touch_Controls: scene.enableTouchControls = settingOptions[setting][value] !== 'Disabled' && hasTouchscreen(); const touchControls = document.getElementById('touchControls'); diff --git a/src/ui/outdated-modal-ui-handler.ts b/src/ui/outdated-modal-ui-handler.ts new file mode 100644 index 00000000000..53243c42bbe --- /dev/null +++ b/src/ui/outdated-modal-ui-handler.ts @@ -0,0 +1,47 @@ +import BattleScene from "../battle-scene"; +import { ModalConfig, ModalUiHandler } from "./modal-ui-handler"; +import { addTextObject, TextStyle } from "./text"; +import { Mode } from "./ui"; + +export default class OutdatedModalUiHandler extends ModalUiHandler { + constructor(scene: BattleScene, mode?: Mode) { + super(scene, mode); + } + + getModalTitle(): string { + return ''; + } + + getWidth(): number { + return 160; + } + + getHeight(): number { + return 64; + } + + getMargin(): [number, number, number, number] { + return [ 0, 0, 48, 0 ]; + } + + getButtonLabels(): string[] { + return [ ]; + } + + setup(): void { + super.setup(); + + const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Your client is currently outdated.\nPlease reload to update the game.\n\nIf this error persists, please clear your browser cache.', TextStyle.WINDOW, { fontSize: '48px', align: 'center' }); + label.setOrigin(0.5, 0.5); + + this.modalContainer.add(label); + } + + show(args: any[]): boolean { + const config: ModalConfig = { + buttonActions: [] + }; + + return super.show([ config ]); + } +} \ No newline at end of file diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 054bb1ab9f9..21865045141 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -114,8 +114,10 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { ui.showText(null, 0); }, false, 0, 19, 2000); }); - } else + } else if (this.sessionSlots[this.cursor].hasData === false) saveAndCallback(); + else + return false; break; } success = true; @@ -210,7 +212,6 @@ class SessionSlot extends Phaser.GameObjects.Container { super(scene, 0, slotId * 56); this.slotId = slotId; - this.hasData = false; this.setup(); } @@ -282,6 +283,7 @@ class SessionSlot extends Phaser.GameObjects.Container { return new Promise(resolve => { this.scene.gameData.getSession(this.slotId).then(async sessionData => { if (!sessionData) { + this.hasData = false; this.loadingLabel.setText('Empty'); resolve(false); return; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 7521cbbc6bb..6c589a1b2d7 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1304,6 +1304,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.dexAttrCursor = 0n; this.natureCursor = -1; + if (species?.forms?.find(f => f.formKey === 'female')) { + if (female !== undefined) + formIndex = female ? 1 : 0; + else if (formIndex !== undefined) + female = formIndex === 1; + } + if (species) { this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps.female)) ? DexAttr.MALE : DexAttr.FEMALE; diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 422a0ab3e10..82eead45eb4 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -34,6 +34,7 @@ import SaveSlotSelectUiHandler from './save-slot-select-ui-handler'; import TitleUiHandler from './title-ui-handler'; import SavingIconHandler from './saving-icon-handler'; import UnavailableModalUiHandler from './unavailable-modal-ui-handler'; +import OutdatedModalUiHandler from './outdated-modal-ui-handler'; export enum Mode { MESSAGE, @@ -63,7 +64,8 @@ export enum Mode { LOGIN_FORM, REGISTRATION_FORM, LOADING, - UNAVAILABLE + UNAVAILABLE, + OUTDATED }; const transitionModes = [ @@ -90,7 +92,8 @@ const noTransitionModes = [ Mode.LOGIN_FORM, Mode.REGISTRATION_FORM, Mode.LOADING, - Mode.UNAVAILABLE + Mode.UNAVAILABLE, + Mode.OUTDATED ]; export default class UI extends Phaser.GameObjects.Container { @@ -141,7 +144,8 @@ export default class UI extends Phaser.GameObjects.Container { new LoginFormUiHandler(scene), new RegistrationFormUiHandler(scene), new LoadingModalUiHandler(scene), - new UnavailableModalUiHandler(scene) + new UnavailableModalUiHandler(scene), + new OutdatedModalUiHandler(scene) ]; }