diff --git a/public/battle-anims/encounter-smokescreen.json b/public/battle-anims/encounter-smokescreen.json index 00e552dc503..286cbe13a03 100644 --- a/public/battle-anims/encounter-smokescreen.json +++ b/public/battle-anims/encounter-smokescreen.json @@ -2,32 +2,6 @@ "graphic": "PRAS- Smokescreen", "frames": [ [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": 12.5, @@ -37,37 +11,11 @@ "target": 2, "graphicFrame": 0, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": 8.5, @@ -77,7 +25,7 @@ "target": 2, "graphicFrame": 0, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -89,37 +37,11 @@ "target": 2, "graphicFrame": 8, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": 0.5, @@ -129,7 +51,7 @@ "target": 2, "graphicFrame": 1, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -141,37 +63,11 @@ "target": 2, "graphicFrame": 8, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": -3.5, @@ -181,7 +77,7 @@ "target": 2, "graphicFrame": 2, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -193,7 +89,7 @@ "target": 2, "graphicFrame": 8, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -205,37 +101,11 @@ "target": 2, "graphicFrame": 0, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": -7.5, @@ -245,7 +115,7 @@ "target": 2, "graphicFrame": 2, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -257,7 +127,7 @@ "target": 2, "graphicFrame": 7, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -269,37 +139,11 @@ "target": 2, "graphicFrame": 0, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": -11.5, @@ -309,7 +153,7 @@ "target": 2, "graphicFrame": 3, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -321,7 +165,7 @@ "target": 2, "graphicFrame": 6, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -333,7 +177,7 @@ "target": 2, "graphicFrame": 0, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -345,37 +189,11 @@ "target": 2, "graphicFrame": 8, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": -15.5, @@ -385,7 +203,7 @@ "target": 2, "graphicFrame": 4, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -397,7 +215,7 @@ "target": 2, "graphicFrame": 6, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -409,7 +227,7 @@ "target": 2, "graphicFrame": 1, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -421,37 +239,11 @@ "target": 2, "graphicFrame": 8, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": -19.5, @@ -461,7 +253,7 @@ "target": 2, "graphicFrame": 4, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -473,7 +265,7 @@ "target": 2, "graphicFrame": 5, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -485,7 +277,7 @@ "target": 2, "graphicFrame": 2, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -497,7 +289,7 @@ "target": 2, "graphicFrame": 8, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -510,37 +302,11 @@ "target": 2, "graphicFrame": 8, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 15.5, "y": -23.5, @@ -550,7 +316,7 @@ "target": 2, "graphicFrame": 4, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -562,7 +328,7 @@ "target": 2, "graphicFrame": 5, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -574,7 +340,7 @@ "target": 2, "graphicFrame": 2, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -586,7 +352,7 @@ "target": 2, "graphicFrame": 7, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -599,37 +365,11 @@ "target": 2, "graphicFrame": 8, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -11, "y": -6.5, @@ -639,7 +379,7 @@ "target": 2, "graphicFrame": 3, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -651,7 +391,7 @@ "target": 2, "graphicFrame": 4, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -663,7 +403,7 @@ "target": 2, "graphicFrame": 6, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -676,7 +416,7 @@ "target": 2, "graphicFrame": 8, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -688,37 +428,11 @@ "target": 2, "graphicFrame": 0, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -11, "y": -10.5, @@ -728,7 +442,7 @@ "target": 2, "graphicFrame": 4, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -740,7 +454,7 @@ "target": 2, "graphicFrame": 4, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -752,7 +466,7 @@ "target": 2, "graphicFrame": 6, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -765,7 +479,7 @@ "target": 2, "graphicFrame": 7, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -777,37 +491,11 @@ "target": 2, "graphicFrame": 0, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -11, "y": -14.5, @@ -817,7 +505,7 @@ "target": 2, "graphicFrame": 4, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -829,7 +517,7 @@ "target": 2, "graphicFrame": 4, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -841,7 +529,7 @@ "target": 2, "graphicFrame": 5, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -854,7 +542,7 @@ "target": 2, "graphicFrame": 6, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -866,37 +554,11 @@ "target": 2, "graphicFrame": 0, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -11, "y": -18.5, @@ -906,7 +568,7 @@ "target": 2, "graphicFrame": 4, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -918,7 +580,7 @@ "target": 2, "graphicFrame": 4, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -931,7 +593,7 @@ "target": 2, "graphicFrame": 6, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -943,37 +605,11 @@ "target": 2, "graphicFrame": 1, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -12.5, "y": -15.5, @@ -984,7 +620,7 @@ "target": 2, "graphicFrame": 5, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -996,7 +632,7 @@ "target": 2, "graphicFrame": 4, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -1008,37 +644,11 @@ "target": 2, "graphicFrame": 2, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -12.5, "y": -19.5, @@ -1049,7 +659,7 @@ "target": 2, "graphicFrame": 5, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -1061,7 +671,7 @@ "target": 2, "graphicFrame": 4, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -1073,37 +683,11 @@ "target": 2, "graphicFrame": 2, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": -12.5, "y": -23.5, @@ -1114,7 +698,7 @@ "target": 2, "graphicFrame": 5, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 }, { @@ -1126,37 +710,11 @@ "target": 2, "graphicFrame": 3, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 4.5, "y": -9, @@ -1166,37 +724,11 @@ "target": 2, "graphicFrame": 4, "opacity": 150, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 4.5, "y": -13, @@ -1206,37 +738,11 @@ "target": 2, "graphicFrame": 4, "opacity": 100, - "priority": -1, + "priority": 4, "focus": 2 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": -1, - "focus": 3 - }, { "x": 4.5, "y": -17, @@ -1246,7 +752,7 @@ "target": 2, "graphicFrame": 4, "opacity": 50, - "priority": -1, + "priority": 4, "focus": 2 } ], @@ -1261,7 +767,7 @@ "graphicFrame": 0, "opacity": 255, "locked": true, - "priority": -1, + "priority": 4, "focus": 3 }, { @@ -1274,402 +780,24 @@ "graphicFrame": 0, "opacity": 255, "locked": true, - "priority": -1, + "priority": 4, "focus": 3 } ], - [ - { - "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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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": 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 - } - ] + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [] ], "frameTimedEvents": { "0": [ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index fe7cae9de65..57d642bd3fd 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -95,10 +95,9 @@ import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase import { TurnInitPhase } from "./phases/turn-init-phase"; import { ShopCursorTarget } from "./enums/shop-cursor-target"; import MysteryEncounter from "./data/mystery-encounters/mystery-encounter"; -import { allMysteryEncounters, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; +import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import HeldModifierConfig from "#app/interfaces/held-modifier-config"; @@ -1151,33 +1150,36 @@ export default class BattleScene extends SceneBase { } // TODO: remove these once ME spawn rates are finalized - // let testStartingWeight = 0; - // while (testStartingWeight < 3) { + // let testStartingWeight = 3; + // while (testStartingWeight < 4) { // calculateMEAggregateStats(this, testStartingWeight); - // testStartingWeight += 2; + // testStartingWeight += 1; // } // calculateRareSpawnAggregateStats(this, 14); // Check for mystery encounter - // Can only occur in place of a standard wild battle, waves 10-180 + // Can only occur in place of a standard (non-boss) wild battle, waves 10-180 const highestMysteryEncounterWave = 180; const lowestMysteryEncounterWave = 10; if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) { const roll = Utils.randSeedInt(256); - // Base spawn weight is 1/256, and increases by 5/256 for each missed attempt at spawning an encounter on a valid floor - const sessionEncounterRate = !isNullOrUndefined(this.mysteryEncounterData?.encounterSpawnChance) ? this.mysteryEncounterData.encounterSpawnChance : BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; + // Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor + const sessionEncounterRate = this.mysteryEncounterData.encounterSpawnChance; + const encounteredEvents = this.mysteryEncounterData.encounteredEvents; - // If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn - // Do the reverse as well - // Reduces occurrence of runs with very few (<6) and a ton (>10) of encounters + // If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well) + // Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (newWaveIndex - lowestMysteryEncounterWave); - const currentRunDiffFromAvg = expectedEncountersByFloor - (this.mysteryEncounterData?.encounteredEvents?.length || 0); - const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * 5; + const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length; + const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER; const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!; - if (roll < successRate) { + // If the most recent ME was 3 or fewer waves ago, can never spawn a ME + const canSpawn = encounteredEvents.length === 0 || (newWaveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex) > 3 || !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE); + + if (canSpawn && roll < successRate) { newBattleType = BattleType.MYSTERY_ENCOUNTER; // Reset base spawn weight this.mysteryEncounterData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; @@ -1240,7 +1242,7 @@ export default class BattleScene extends SceneBase { const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0; const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49; const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne); - const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; + const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); this.trySpreadPokerus(); if (!isNewBiome && (newWaveIndex % 10) === 5) { @@ -1248,14 +1250,19 @@ export default class BattleScene extends SceneBase { } if (resetArenaState) { this.arena.resetArenaEffects(); - if (lastBattle?.mysteryEncounter?.encounterMode !== MysteryEncounterMode.NO_BATTLE) { - playerField.forEach((_, p) => this.pushPhase(new ReturnPhase(this, p))); - for (const pokemon of this.getParty()) { - pokemon.resetBattleData(); - applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); + playerField.forEach((pokemon, p) => { + if (pokemon.isOnField()) { + this.pushPhase(new ReturnPhase(this, p)); } + }); + for (const pokemon of this.getParty()) { + pokemon.resetBattleData(); + applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); + } + + if (!this.trainer.visible) { this.pushPhase(new ShowTrainerPhase(this)); } } @@ -2960,8 +2967,8 @@ export default class BattleScene extends SceneBase { } let availableEncounters: MysteryEncounter[] = []; - // New encounter will never be the same as the most recent encounter - const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null; + // New encounter should never be the same as the most recent encounter + const previousEncounter = this.mysteryEncounterData.encounteredEvents.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1].type : null; const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? []; // If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available while (availableEncounters.length === 0 && tier !== null) { @@ -2977,10 +2984,10 @@ export default class BattleScene extends SceneBase { if (!encounterCandidate.meetsRequirements!(this)) { // Meets encounter requirements return false; } - if (!isNullOrUndefined(previousEncounter) && encounterType === previousEncounter) { // Previous encounter was not this one + if (previousEncounter !== null && encounterType === previousEncounter) { // Previous encounter was not this one return false; } - if (this.mysteryEncounterData.encounteredEvents?.length > 0 && // Encounter has not exceeded max allowed encounters + if (this.mysteryEncounterData.encounteredEvents.length > 0 && // Encounter has not exceeded max allowed encounters (encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0) && this.mysteryEncounterData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) { return false; diff --git a/src/battle.ts b/src/battle.ts index b3ccacccdfe..70f77bf263b 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -194,7 +194,11 @@ export default class Battle { getBgmOverride(scene: BattleScene): string | null { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); - if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { + if (this.battleType === BattleType.MYSTERY_ENCOUNTER && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { + // Music is overridden MEs during ME onInit() + // Should not use BGM overrides before swapping from DEFAULT mode + return null; + } else if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) { return `encounter_${this.trainer?.getEncounterBgm()}`; } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 993bd31b9f6..519ab5a69ba 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -891,7 +891,7 @@ export abstract class BattleAnim { const isUser = frame.target === AnimFrameTarget.USER; if (isUser && target === user) { continue; - } else if (this.playOnEmptyField && frame.target === AnimFrameTarget.TARGET) { + } else if (this.playOnEmptyField && frame.target === AnimFrameTarget.TARGET && !target.isOnField()) { continue; } const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET]; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 41094f5c4a1..a05b86452c4 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -294,7 +294,7 @@ export const ClowningAroundEncounter: MysteryEncounter = // Play animations const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); background.playWithoutTargets(scene, 230, 40, 2); - await transitionMysteryEncounterIntroVisuals(scene); + await transitionMysteryEncounterIntroVisuals(scene, true, true, 200); }) .build() ) @@ -358,7 +358,7 @@ export const ClowningAroundEncounter: MysteryEncounter = // Play animations const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); background.playWithoutTargets(scene, 230, 40, 2); - await transitionMysteryEncounterIntroVisuals(scene); + await transitionMysteryEncounterIntroVisuals(scene, true, true, 200); }) .build() ) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 3cc28c5df52..b2dc3d6cf08 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -86,8 +86,8 @@ export const FightOrFlightEncounter: MysteryEncounter = : ModifierTier.GREAT; regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); let item: ModifierTypeOption | null = null; - // TMs excluded from possible rewards as they're too swingy in value for a singular item reward - while (!item || item.type.id.includes("TM_")) { + // TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward + while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") { item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0]; } encounter.setDialogueToken("itemName", item.type.name); diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index 01112e04fa0..91bb8d47752 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -32,7 +32,7 @@ const BST_INCREASE_VALUE = 10; */ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF) - .withEncounterTier(MysteryEncounterTier.COMMON) + .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 .withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party .withHideWildIntroMessage(true) @@ -43,7 +43,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = fileRoot: "items", hasShadow: true, isItem: true, - scale: 1.5, + scale: 1.25, x: -15, y: 3, disableAnimation: true @@ -53,7 +53,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = fileRoot: "pokemon", hasShadow: true, repeat: true, - scale: 1.5, + scale: 1.25, x: 20, y: 10, yShadow: 7 @@ -76,7 +76,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = species: getPokemonSpecies(Species.SHUCKLE), isBoss: true, bossSegments: 5, - mysteryEncounterData: new MysteryEncounterPokemonData(1.5), + mysteryEncounterData: new MysteryEncounterPokemonData(1.25), nature: Nature.BOLD, moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER], modifierConfigs: [ @@ -142,7 +142,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = if (index < 2) { // -15 to the two highest BST mons modifyPlayerPokemonBST(pokemon, -HIGH_BST_REDUCTION_VALUE); - encounter.setDialogueToken("highBstPokemon" + index, pokemon.getNameToRender()); + encounter.setDialogueToken("highBstPokemon" + (index + 1), pokemon.getNameToRender()); } else { // +10 for the rest modifyPlayerPokemonBST(pokemon, BST_INCREASE_VALUE); diff --git a/src/data/mystery-encounters/mystery-encounter-data.ts b/src/data/mystery-encounters/mystery-encounter-data.ts index a4fbc14541e..a16dc5ecb7d 100644 --- a/src/data/mystery-encounters/mystery-encounter-data.ts +++ b/src/data/mystery-encounters/mystery-encounter-data.ts @@ -20,9 +20,9 @@ export class MysteryEncounterData { encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; nextEncounterQueue: [MysteryEncounterType, integer][] = []; - constructor(flags: MysteryEncounterData | null) { - if (!isNullOrUndefined(flags)) { - Object.assign(this, flags); + constructor(data: MysteryEncounterData | null) { + if (!isNullOrUndefined(data)) { + Object.assign(this, data); } } } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 1a842568e85..34ce2cf68bb 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -31,10 +31,36 @@ import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounter import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter"; import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter"; -// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 -export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; -export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 5; -export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15; +/** + * Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 + */ +export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3; +/** + * When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks. + * These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + */ +export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 3; +/** + * Specifies the target average for total ME spawns in a single Classic run. + * Used by anti-variance mechanic to check whether a run is above or below the target on a given wave. + */ +export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12; +/** + * Will increase/decrease the chance of spawning a ME based on the current run's total MEs encountered vs AVERAGE_ENCOUNTERS_PER_RUN_TARGET + * Example: + * AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 17 (expects avg 1 ME every 10 floors) + * ANTI_VARIANCE_WEIGHT_MODIFIER = 15 + * + * On wave 20, if 1 ME has been encountered, the difference from expected average is 0 MEs. + * So anti-variance adds 0/256 to the spawn weight check for ME spawn. + * + * On wave 20, if 0 MEs have been encountered, the difference from expected average is 1 ME. + * So anti-variance adds 15/256 to the spawn weight check for ME spawn. + * + * On wave 20, if 2 MEs have been encountered, the difference from expected average is -1 ME. + * So anti-variance adds -15/256 to the spawn weight check for ME spawn. + */ +export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15; export const EXTREME_ENCOUNTER_BIOMES = [ Biome.SEA, diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 7fc21cb4653..692fdec8e28 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1,7 +1,7 @@ import Battle, { BattlerIndex, BattleType } from "#app/battle"; import { biomeLinks, BiomePoolTier } from "#app/data/biomes"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; +import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; @@ -908,13 +908,13 @@ export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): bool export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) { const numRuns = 1000; let run = 0; - const targetEncountersPerRun = 15; // AVERAGE_ENCOUNTERS_PER_RUN_TARGET const biomes = Object.keys(Biome).filter(key => isNaN(Number(key))); const alwaysPickTheseBiomes = [Biome.ISLAND, Biome.ABYSS, Biome.WASTELAND, Biome.FAIRY_CAVE, Biome.TEMPLE, Biome.LABORATORY, Biome.SPACE, Biome.WASTELAND]; const calculateNumEncounters = (): any[] => { let encounterRate = baseSpawnWeight; // BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT const numEncounters = [0, 0, 0, 0]; + let mostRecentEncounterWave = 0; const encountersByBiome = new Map(biomes.map(b => [b, 0])); const validMEfloorsByBiome = new Map(biomes.map(b => [b, 0])); let currentBiome = Biome.TOWN; @@ -976,16 +976,20 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n // If total number of encounters is lower than expected for the run, slightly favor a new encounter // Do the reverse as well - const expectedEncountersByFloor = targetEncountersPerRun / (180 - 10) * i; + const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (180 - 10) * (i - 10); const currentRunDiffFromAvg = expectedEncountersByFloor - numEncounters.reduce((a, b) => a + b); - const favoredEncounterRate = encounterRate + currentRunDiffFromAvg * 5; + const favoredEncounterRate = encounterRate + currentRunDiffFromAvg * 15; - if (roll < favoredEncounterRate) { + // If the most recent ME was 3 or fewer waves ago, can never spawn a ME + const canSpawn = (i - mostRecentEncounterWave) > 3; + + if (canSpawn && roll < favoredEncounterRate) { + mostRecentEncounterWave = i; encounterRate = baseSpawnWeight; // Calculate encounter rarity // Common / Uncommon / Rare / Super Rare (base is out of 128) - const tierWeights = [64, 40, 21, 3]; + const tierWeights = [66, 40, 19, 3]; // Adjust tier weights by currently encountered events (pity system that lowers odds of multiple common/uncommons) tierWeights[0] = tierWeights[0] - 6 * numEncounters[0]; diff --git a/src/enums/mystery-encounter-mode.ts b/src/enums/mystery-encounter-mode.ts index 3acab7b4797..bba3ec3772e 100644 --- a/src/enums/mystery-encounter-mode.ts +++ b/src/enums/mystery-encounter-mode.ts @@ -1,8 +1,9 @@ export enum MysteryEncounterMode { + /** MysteryEncounter will always begin in this mode, but will always swap modes when an option is selected */ DEFAULT, TRAINER_BATTLE, WILD_BATTLE, - /** Enables wild boss music during encounter */ + /** Enables special boss music during encounter */ BOSS_BATTLE, NO_BATTLE } diff --git a/src/enums/mystery-encounter-tier.ts b/src/enums/mystery-encounter-tier.ts index b3924b2ff9d..484acc7aba9 100644 --- a/src/enums/mystery-encounter-tier.ts +++ b/src/enums/mystery-encounter-tier.ts @@ -1,10 +1,11 @@ /** - * Enum values are base spawn weights of each tier + * Enum values are base spawn weights of each tier. + * The weights aim for 46.25/31.25/18.5/4% spawn ratios, AFTER accounting for anti-variance and pity mechanisms */ export enum MysteryEncounterTier { - COMMON = 64, + COMMON = 66, GREAT = 40, - ULTRA = 21, + ULTRA = 19, ROGUE = 3, MASTER = 0 // Not currently used } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f9d704c6854..cd1d0daca0b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -5,7 +5,7 @@ import { variantData } from "#app/data/variant"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info"; import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; -import { Constructor } from "#app/utils"; +import { Constructor, isNullOrUndefined } from "#app/utils"; import * as Utils from "../utils"; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type"; import { getLevelTotalExp } from "../data/exp"; @@ -577,7 +577,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const formKey = this.getFormKey(); if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) { return 1.5; - } else if (this?.mysteryEncounterData?.spriteScale) { + } else if (!isNullOrUndefined(this.mysteryEncounterData?.spriteScale)) { return this.mysteryEncounterData.spriteScale; } return 1; diff --git a/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.json b/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.json index f5f5a2f4a14..3cacea1d85e 100644 --- a/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.json +++ b/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.json @@ -5,10 +5,10 @@ "query": "What will you do?", "option": { "1": { - "label": "Touch the Shuckle", + "label": "Approach the Shuckle", "tooltip": "(?) Something awful or amazing might happen", "selected": "You black out.", - "selected_2": "@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}} feel a terrible lethargy come over them!\nTheir base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!" + "selected_2": "@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}}\nfeel a terrible lethargy come over them!$Their base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!" }, "2": { "label": "Battle the Shuckle", diff --git a/src/overrides.ts b/src/overrides.ts index 709587e8481..da778967803 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -141,9 +141,9 @@ class DefaultOverrides { // ------------------------- /** 1 to 256, set to null to ignore */ - readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = null; + readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = 256; readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier | null = null; - readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = null; + readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = MysteryEncounterType.CLOWNING_AROUND; // ------------------------- // MODIFIER / ITEM OVERRIDES diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 4b4af25741e..f1f620db05e 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -6,6 +6,7 @@ import { StatusEffect } from "#app/enums/status-effect.js"; import { PokemonPhase } from "./pokemon-phase"; import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { BattleType } from "#app/battle"; export class PostSummonPhase extends PokemonPhase { constructor(scene: BattleScene, battlerIndex: BattlerIndex) { @@ -23,7 +24,7 @@ export class PostSummonPhase extends PokemonPhase { this.scene.arena.applyTags(ArenaTrapTag, pokemon); // If this is mystery encounter and has post summon phase tag, apply post summon effects - if (pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag)) { + if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) { pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); } diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 9847fe5ff44..185bd548d19 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -15,12 +15,14 @@ import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-d import i18next from "i18next"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; export default class MysteryEncounterUiHandler extends UiHandler { private cursorContainer: Phaser.GameObjects.Container; private cursorObj?: Phaser.GameObjects.Image; private optionsContainer: Phaser.GameObjects.Container; + private optionScrollTweens: (Phaser.Tweens.Tween | null)[] = [null, null, null, null]; private tooltipWindow: Phaser.GameObjects.NineSlice; private tooltipContainer: Phaser.GameObjects.Container; @@ -81,7 +83,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.rarityBall.setScale(0.75); this.descriptionContainer.add(this.rarityBall); - const dexProgressIndicator = this.scene.add.sprite(12, 10, "encounter_radar"); + const dexProgressIndicator = this.scene.add.sprite(12, 10, "encounter_radar"); dexProgressIndicator.setScale(0.80); this.dexProgressContainer.add(dexProgressIndicator); this.dexProgressContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, 24, 28), Phaser.Geom.Rectangle.Contains); @@ -341,16 +343,17 @@ export default class MysteryEncounterUiHandler extends UiHandler { for (let i = 0; i < this.encounterOptions.length; i++) { const option = this.encounterOptions[i]; - let optionText; + let optionText: BBCodeText; switch (this.encounterOptions.length) { + default: case 2: - optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, 8, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 }); + optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, 8, "-", TextStyle.WINDOW, { fontSize: "80px", lineSpacing: -8 }); break; case 3: - optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 }); + optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { fontSize: "80px", lineSpacing: -8 }); break; case 4: - optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 }); + optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { fontSize: "80px", lineSpacing: -8 }); break; } @@ -375,6 +378,38 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (this.blockInput) { optionText.setAlpha(0.5); } + + // Sets up the mask that hides the option text to give an illusion of scrolling + const nonScrollWidth = 90; + const optionTextMaskRect = this.scene.make.graphics({}); + optionTextMaskRect.setScale(6); + optionTextMaskRect.fillStyle(0xFFFFFF); + optionTextMaskRect.beginPath(); + optionTextMaskRect.fillRect(optionText.x + 11, optionText.y + 140, nonScrollWidth, 18); + + const optionTextMask = optionTextMaskRect.createGeometryMask(); + optionText.setMask(optionTextMask); + + const optionTextWidth = optionText.displayWidth; + + const tween = this.optionScrollTweens[i]; + if (tween) { + tween.remove(); + this.optionScrollTweens[i] = null; + } + + // Animates the option text scrolling sideways + if (optionTextWidth > nonScrollWidth) { + this.optionScrollTweens[i] = this.scene.tweens.add({ + targets: optionText, + delay: Utils.fixedInt(2000), + loop: -1, + hold: Utils.fixedInt(2000), + duration: Utils.fixedInt((optionTextWidth - nonScrollWidth) / 15 * 2000), + x: `-=${(optionTextWidth - nonScrollWidth)}` + }); + } + this.optionsContainer.add(optionText); }