mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-27 01:06:09 +00:00
Merge pull request #97 from AsdarDevelops/fiery-fallout
[Encounter] Fiery Fallout
This commit is contained in:
commit
d650abe790
66
public/battle-anims/encounter-magma-bg.json
Normal file
66
public/battle-anims/encounter-magma-bg.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"frames": [
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[]
|
||||||
|
],
|
||||||
|
"frameTimedEvents": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"frameIndex": 0,
|
||||||
|
"resourceName": "PRAS- Fire BG",
|
||||||
|
"bgX": 0,
|
||||||
|
"bgY": 0,
|
||||||
|
"opacity": 0,
|
||||||
|
"duration": 35,
|
||||||
|
"eventType": "AnimTimedAddBgEvent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frameIndex": 0,
|
||||||
|
"resourceName": "",
|
||||||
|
"bgX": 0,
|
||||||
|
"bgY": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"duration": 12,
|
||||||
|
"eventType": "AnimTimedUpdateBgEvent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"25": [
|
||||||
|
{
|
||||||
|
"frameIndex": 25,
|
||||||
|
"resourceName": "",
|
||||||
|
"bgX": 0,
|
||||||
|
"bgY": 0,
|
||||||
|
"opacity": 0,
|
||||||
|
"duration": 8,
|
||||||
|
"eventType": "AnimTimedUpdateBgEvent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": 1,
|
||||||
|
"hue": 0
|
||||||
|
}
|
902
public/battle-anims/encounter-magma-spout.json
Normal file
902
public/battle-anims/encounter-magma-spout.json
Normal file
@ -0,0 +1,902 @@
|
|||||||
|
{
|
||||||
|
"graphic": "PRAS- Magma Storm",
|
||||||
|
"frames": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 1,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 1,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 1,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 2,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 2,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 2,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 3,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 3,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 3,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 4,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 4,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 4,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 5,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 5,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 5,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 6,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 6,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 6,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 7,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 7,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 7,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 120,
|
||||||
|
"y": -56,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 8,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 144,
|
||||||
|
"y": -84,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 8,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 100,
|
||||||
|
"y": -86.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 8,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 140,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 9,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 136,
|
||||||
|
"y": -92,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 9,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 108,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 9,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -76,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 10,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 116,
|
||||||
|
"y": -88,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 10,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 128,
|
||||||
|
"y": -62.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 10,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 136,
|
||||||
|
"y": -96,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 7,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 100,
|
||||||
|
"y": -76,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 7,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 148,
|
||||||
|
"y": -66.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 7,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 108,
|
||||||
|
"y": -92,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 8,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 120,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 8,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 144,
|
||||||
|
"y": -86.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 8,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 100,
|
||||||
|
"y": -76,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 9,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 136,
|
||||||
|
"y": -68,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 9,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 128,
|
||||||
|
"y": -94.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 9,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 100.5,
|
||||||
|
"y": -70,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 10,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 144,
|
||||||
|
"y": -66,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 10,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 126,
|
||||||
|
"y": -86.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 10,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 6,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 6,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 6,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 5,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 5,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 5,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 4,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 4,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 4,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 3,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 3,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 3,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 2,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 2,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 2,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 1,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 1,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 1,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 101,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 130,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 152,
|
||||||
|
"y": -64,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 130,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 124.5,
|
||||||
|
"y": -78.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 140,
|
||||||
|
"priority": 4,
|
||||||
|
"focus": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"frameTimedEvents": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"frameIndex": 0,
|
||||||
|
"resourceName": "PRSFX- Magma Storm1.wav",
|
||||||
|
"volume": 100,
|
||||||
|
"pitch": 100,
|
||||||
|
"eventType": "AnimTimedSoundEvent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"8": [
|
||||||
|
{
|
||||||
|
"frameIndex": 8,
|
||||||
|
"resourceName": "PRSFX- Magma Storm2.wav",
|
||||||
|
"volume": 100,
|
||||||
|
"pitch": 100,
|
||||||
|
"eventType": "AnimTimedSoundEvent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": 1,
|
||||||
|
"hue": 0
|
||||||
|
}
|
41
public/images/mystery-encounters/exclaim.json
Normal file
41
public/images/mystery-encounters/exclaim.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "exclaim.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 32,
|
||||||
|
"h": 32
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 32,
|
||||||
|
"h": 32
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 32,
|
||||||
|
"h": 32
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 32,
|
||||||
|
"h": 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/images/mystery-encounters/exclaim.png
Normal file
BIN
public/images/mystery-encounters/exclaim.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 378 B |
@ -4,51 +4,72 @@
|
|||||||
"image": "mud.png",
|
"image": "mud.png",
|
||||||
"format": "RGBA8888",
|
"format": "RGBA8888",
|
||||||
"size": {
|
"size": {
|
||||||
"w": 18,
|
"w": 14,
|
||||||
"h": 55
|
"h": 68
|
||||||
},
|
},
|
||||||
"scale": 1,
|
"scale": 1,
|
||||||
"frames": [
|
"frames": [
|
||||||
{
|
{
|
||||||
"filename": "0002.png",
|
"filename": "0001.png",
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"trimmed": true,
|
"trimmed": true,
|
||||||
"sourceSize": {
|
"sourceSize": {
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 16
|
"h": 20
|
||||||
},
|
},
|
||||||
"spriteSourceSize": {
|
"spriteSourceSize": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 3,
|
"y": 0,
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 13
|
"h": 13
|
||||||
},
|
},
|
||||||
"frame": {
|
"frame": {
|
||||||
"x": 1,
|
"x": 1,
|
||||||
"y": 1,
|
"y": 1,
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 13
|
"h": 13
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 20
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 12,
|
||||||
|
"h": 14
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 16,
|
||||||
|
"w": 12,
|
||||||
|
"h": 14
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filename": "0003.png",
|
"filename": "0003.png",
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"trimmed": true,
|
"trimmed": true,
|
||||||
"sourceSize": {
|
"sourceSize": {
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 16
|
"h": 20
|
||||||
},
|
},
|
||||||
"spriteSourceSize": {
|
"spriteSourceSize": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 4,
|
"y": 1,
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 12
|
"h": 16
|
||||||
},
|
},
|
||||||
"frame": {
|
"frame": {
|
||||||
"x": 1,
|
"x": 1,
|
||||||
"y": 16,
|
"y": 32,
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 12
|
"h": 16
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -56,41 +77,20 @@
|
|||||||
"rotated": false,
|
"rotated": false,
|
||||||
"trimmed": true,
|
"trimmed": true,
|
||||||
"sourceSize": {
|
"sourceSize": {
|
||||||
"w": 16,
|
"w": 12,
|
||||||
"h": 16
|
"h": 20
|
||||||
},
|
},
|
||||||
"spriteSourceSize": {
|
"spriteSourceSize": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 7,
|
|
||||||
"w": 16,
|
|
||||||
"h": 9
|
|
||||||
},
|
|
||||||
"frame": {
|
|
||||||
"x": 1,
|
|
||||||
"y": 30,
|
|
||||||
"w": 16,
|
|
||||||
"h": 9
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename": "0001.png",
|
|
||||||
"rotated": false,
|
|
||||||
"trimmed": true,
|
|
||||||
"sourceSize": {
|
|
||||||
"w": 16,
|
|
||||||
"h": 16
|
|
||||||
},
|
|
||||||
"spriteSourceSize": {
|
|
||||||
"x": 1,
|
|
||||||
"y": 3,
|
"y": 3,
|
||||||
"w": 14,
|
"w": 12,
|
||||||
"h": 13
|
"h": 17
|
||||||
},
|
},
|
||||||
"frame": {
|
"frame": {
|
||||||
"x": 1,
|
"x": 1,
|
||||||
"y": 41,
|
"y": 50,
|
||||||
"w": 14,
|
"w": 12,
|
||||||
"h": 13
|
"h": 17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -99,6 +99,6 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"app": "https://www.codeandweb.com/texturepacker",
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
"version": "3.0",
|
"version": "3.0",
|
||||||
"smartupdate": "$TexturePacker:SmartUpdate:a9f7ae83758a2dffaacdaba2ee9dc2e2:0ebff9db47ce74a0ec049f5d74d589fa:c64f6b8befc3d5e9f836246d2b9536be$"
|
"smartupdate": "$TexturePacker:SmartUpdate:4f18a8effb8f01eb70f9f25b8294c1bf:ad663a73c51f780bbf45d00a52519553:c64f6b8befc3d5e9f836246d2b9536be$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 375 B |
@ -1,43 +1,42 @@
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import UI from "./ui/ui";
|
import UI from "./ui/ui";
|
||||||
import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from "./phases";
|
import { LevelCapPhase, LoginPhase, MessagePhase, MovePhase, NewBiomeEncounterPhase, NextEncounterPhase, ReturnPhase, SelectBiomePhase, ShowTrainerPhase, SwitchPhase, TitlePhase, TurnInitPhase } from "./phases";
|
||||||
import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
|
||||||
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
|
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species";
|
||||||
import {Constructor, isNullOrUndefined} from "#app/utils";
|
import { Constructor, isNullOrUndefined } from "#app/utils";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
|
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, TerastallizeModifier } from "./modifier/modifier";
|
||||||
import { PokeballType } from "./data/pokeball";
|
import { PokeballType } from "./data/pokeball";
|
||||||
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
|
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
|
||||||
import { Phase } from "./phase";
|
import { Phase } from "./phase";
|
||||||
import { initGameSpeed } from "./system/game-speed";
|
import { initGameSpeed } from "./system/game-speed";
|
||||||
import { Arena, ArenaBase } from "./field/arena";
|
import { Arena, ArenaBase } from "./field/arena";
|
||||||
import { GameData } from "./system/game-data";
|
import { GameData } from "./system/game-data";
|
||||||
import { TextStyle, addTextObject, getTextColor } from "./ui/text";
|
import { addTextObject, getTextColor, TextStyle } from "./ui/text";
|
||||||
import { allMoves } from "./data/move";
|
import { allMoves } from "./data/move";
|
||||||
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue, PokemonHeldItemModifierType } from "./modifier/modifier-type";
|
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue, ModifierPoolType, PokemonHeldItemModifierType } from "./modifier/modifier-type";
|
||||||
import AbilityBar from "./ui/ability-bar";
|
import AbilityBar from "./ui/ability-bar";
|
||||||
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability";
|
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr } from "./data/ability";
|
||||||
import { allAbilities } from "./data/ability";
|
|
||||||
import Battle, { BattleType, FixedBattleConfig } from "./battle";
|
import Battle, { BattleType, FixedBattleConfig } from "./battle";
|
||||||
import { GameMode, GameModes, getGameMode } from "./game-mode";
|
import { GameMode, GameModes, getGameMode } from "./game-mode";
|
||||||
import FieldSpritePipeline from "./pipelines/field-sprite";
|
import FieldSpritePipeline from "./pipelines/field-sprite";
|
||||||
import SpritePipeline from "./pipelines/sprite";
|
import SpritePipeline from "./pipelines/sprite";
|
||||||
import PartyExpBar from "./ui/party-exp-bar";
|
import PartyExpBar from "./ui/party-exp-bar";
|
||||||
import { TrainerSlot, trainerConfigs } from "./data/trainer-config";
|
import { trainerConfigs, TrainerSlot } from "./data/trainer-config";
|
||||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
import Trainer, { TrainerVariant } from "./field/trainer";
|
||||||
import TrainerData from "./system/trainer-data";
|
import TrainerData from "./system/trainer-data";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
|
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
|
||||||
import PokeballTray from "./ui/pokeball-tray";
|
import PokeballTray from "./ui/pokeball-tray";
|
||||||
import InvertPostFX from "./pipelines/invert";
|
import InvertPostFX from "./pipelines/invert";
|
||||||
import { Achv, ModifierAchv, MoneyAchv, achvs } from "./system/achv";
|
import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv";
|
||||||
import { Voucher, vouchers } from "./system/voucher";
|
import { Voucher, vouchers } from "./system/voucher";
|
||||||
import { Gender } from "./data/gender";
|
import { Gender } from "./data/gender";
|
||||||
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
||||||
import { addUiThemeOverrides } from "./ui/ui-theme";
|
import { addUiThemeOverrides } from "./ui/ui-theme";
|
||||||
import PokemonData from "./system/pokemon-data";
|
import PokemonData from "./system/pokemon-data";
|
||||||
import { Nature } from "./data/nature";
|
import { Nature } from "./data/nature";
|
||||||
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from "./data/pokemon-forms";
|
import { pokemonFormChanges, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms";
|
||||||
import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase";
|
import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase";
|
||||||
import { getTypeRgb } from "./data/type";
|
import { getTypeRgb } from "./data/type";
|
||||||
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
|
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
|
||||||
@ -50,8 +49,8 @@ import CandyBar from "./ui/candy-bar";
|
|||||||
import { Variant, variantData } from "./data/variant";
|
import { Variant, variantData } from "./data/variant";
|
||||||
import { Localizable } from "#app/interfaces/locales";
|
import { Localizable } from "#app/interfaces/locales";
|
||||||
import * as Overrides from "./overrides";
|
import * as Overrides from "./overrides";
|
||||||
import {InputsController} from "./inputs-controller";
|
import { InputsController } from "./inputs-controller";
|
||||||
import {UiInputs} from "./ui-inputs";
|
import { UiInputs } from "./ui-inputs";
|
||||||
import { NewArenaEvent } from "./events/battle-scene";
|
import { NewArenaEvent } from "./events/battle-scene";
|
||||||
import ArenaFlyout from "./ui/arena-flyout";
|
import ArenaFlyout from "./ui/arena-flyout";
|
||||||
import { EaseType } from "#enums/ease-type";
|
import { EaseType } from "#enums/ease-type";
|
||||||
@ -68,7 +67,7 @@ import { UiTheme } from "#enums/ui-theme";
|
|||||||
import { TimedEventManager } from "#app/timed-event-manager.js";
|
import { TimedEventManager } from "#app/timed-event-manager.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import IMysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter";
|
import IMysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter";
|
||||||
import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
import { allMysteryEncounters, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
||||||
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
|
||||||
@ -2651,8 +2650,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
return encounter;
|
return encounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common / Uncommon / Rare / Super Rare
|
// See Enum values for base tier weights
|
||||||
const tierWeights = [64, 40, 21, 3];
|
const tierWeights = [MysteryEncounterTier.COMMON, MysteryEncounterTier.GREAT, MysteryEncounterTier.ULTRA, MysteryEncounterTier.ROGUE];
|
||||||
|
|
||||||
// Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run
|
// Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run
|
||||||
this.mysteryEncounterData.encounteredEvents.forEach(val => {
|
this.mysteryEncounterData.encounteredEvents.forEach(val => {
|
||||||
|
@ -206,7 +206,7 @@ export default class Battle {
|
|||||||
getBgmOverride(scene: BattleScene): string {
|
getBgmOverride(scene: BattleScene): string {
|
||||||
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
||||||
if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) {
|
if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) {
|
||||||
if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages()?.length) {
|
if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages().length) {
|
||||||
return `encounter_${this.trainer.getEncounterBgm()}`;
|
return `encounter_${this.trainer.getEncounterBgm()}`;
|
||||||
}
|
}
|
||||||
if (scene.musicPreference === 0) {
|
if (scene.musicPreference === 0) {
|
||||||
|
@ -6,6 +6,7 @@ import * as Utils from "../utils";
|
|||||||
import { BattlerIndex } from "../battle";
|
import { BattlerIndex } from "../battle";
|
||||||
import { Element } from "json-stable-stringify";
|
import { Element } from "json-stable-stringify";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
import { isNullOrUndefined } from "../utils";
|
||||||
//import fs from 'vite-plugin-fs/browser';
|
//import fs from 'vite-plugin-fs/browser';
|
||||||
|
|
||||||
export enum AnimFrameTarget {
|
export enum AnimFrameTarget {
|
||||||
@ -102,6 +103,16 @@ export enum CommonAnim {
|
|||||||
LOCK_ON = 2120
|
LOCK_ON = 2120
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animations used for Mystery Encounters
|
||||||
|
* These are custom animations that may or may not work in any other circumstance
|
||||||
|
* Use at your own risk
|
||||||
|
*/
|
||||||
|
export enum EncounterAnim {
|
||||||
|
MAGMA_BG,
|
||||||
|
MAGMA_SPOUT
|
||||||
|
}
|
||||||
|
|
||||||
export class AnimConfig {
|
export class AnimConfig {
|
||||||
public id: integer;
|
public id: integer;
|
||||||
public graphic: string;
|
public graphic: string;
|
||||||
@ -302,7 +313,7 @@ abstract class AnimTimedEvent {
|
|||||||
this.resourceName = resourceName;
|
this.resourceName = resourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract execute(scene: BattleScene, battleAnim: BattleAnim): integer;
|
abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer;
|
||||||
|
|
||||||
abstract getEventType(): string;
|
abstract getEventType(): string;
|
||||||
}
|
}
|
||||||
@ -320,7 +331,7 @@ class AnimTimedSoundEvent extends AnimTimedEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(scene: BattleScene, battleAnim: BattleAnim): integer {
|
execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer {
|
||||||
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
|
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
|
||||||
if (this.resourceName) {
|
if (this.resourceName) {
|
||||||
try {
|
try {
|
||||||
@ -382,7 +393,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
|
|||||||
super(frameIndex, resourceName, source);
|
super(frameIndex, resourceName, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
|
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
|
||||||
const tweenProps = {};
|
const tweenProps = {};
|
||||||
if (this.bgX !== undefined) {
|
if (this.bgX !== undefined) {
|
||||||
tweenProps["x"] = (this.bgX * 0.5) - 320;
|
tweenProps["x"] = (this.bgX * 0.5) - 320;
|
||||||
@ -412,7 +423,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
|||||||
super(frameIndex, resourceName, source);
|
super(frameIndex, resourceName, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
|
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
|
||||||
if (moveAnim.bgSprite) {
|
if (moveAnim.bgSprite) {
|
||||||
moveAnim.bgSprite.destroy();
|
moveAnim.bgSprite.destroy();
|
||||||
}
|
}
|
||||||
@ -424,7 +435,9 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
|||||||
moveAnim.bgSprite.setAlpha(this.opacity / 255);
|
moveAnim.bgSprite.setAlpha(this.opacity / 255);
|
||||||
scene.field.add(moveAnim.bgSprite);
|
scene.field.add(moveAnim.bgSprite);
|
||||||
const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon();
|
const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon();
|
||||||
if (fieldPokemon?.isOnField()) {
|
if (!isNullOrUndefined(priority)) {
|
||||||
|
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
|
||||||
|
} else if (fieldPokemon?.isOnField()) {
|
||||||
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
|
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,6 +457,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
|||||||
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig]>();
|
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig]>();
|
||||||
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig]>();
|
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig]>();
|
||||||
export const commonAnims = new Map<CommonAnim, AnimConfig>();
|
export const commonAnims = new Map<CommonAnim, AnimConfig>();
|
||||||
|
export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
|
||||||
|
|
||||||
export function initCommonAnims(scene: BattleScene): Promise<void> {
|
export function initCommonAnims(scene: BattleScene): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@ -511,6 +525,28 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches animation configs to be used in a Mystery Encounter
|
||||||
|
* @param scene
|
||||||
|
* @param encounterAnim - one or more animations to fetch
|
||||||
|
*/
|
||||||
|
export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
|
||||||
|
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim];
|
||||||
|
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
|
||||||
|
const encounterAnimIds = Utils.getEnumValues(EncounterAnim);
|
||||||
|
const encounterAnimFetches = [];
|
||||||
|
for (const anim of anims) {
|
||||||
|
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const encounterAnimId = encounterAnimIds[anim];
|
||||||
|
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas))));
|
||||||
|
}
|
||||||
|
await Promise.allSettled(encounterAnimFetches);
|
||||||
|
}
|
||||||
|
|
||||||
export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise<void> {
|
export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (chargeAnims.has(chargeAnim)) {
|
if (chargeAnims.has(chargeAnim)) {
|
||||||
@ -565,6 +601,16 @@ export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): P
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads encounter animation assets to scene
|
||||||
|
* MUST be called after [initEncounterAnims()](./battle-anims.ts) to load all required animations properly
|
||||||
|
* @param scene
|
||||||
|
* @param startLoad
|
||||||
|
*/
|
||||||
|
export async function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> {
|
||||||
|
await loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad);
|
||||||
|
}
|
||||||
|
|
||||||
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> {
|
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
|
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
|
||||||
@ -672,14 +718,16 @@ export abstract class BattleAnim {
|
|||||||
public target: Pokemon;
|
public target: Pokemon;
|
||||||
public sprites: Phaser.GameObjects.Sprite[];
|
public sprites: Phaser.GameObjects.Sprite[];
|
||||||
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
|
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
|
||||||
|
public playOnEmptyField: boolean;
|
||||||
|
|
||||||
private srcLine: number[];
|
private srcLine: number[];
|
||||||
private dstLine: number[];
|
private dstLine: number[];
|
||||||
|
|
||||||
constructor(user: Pokemon, target: Pokemon) {
|
constructor(user: Pokemon, target: Pokemon, playOnEmptyField: boolean = false) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.sprites = [];
|
this.sprites = [];
|
||||||
|
this.playOnEmptyField = playOnEmptyField;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getAnim(): AnimConfig;
|
abstract getAnim(): AnimConfig;
|
||||||
@ -753,7 +801,7 @@ export abstract class BattleAnim {
|
|||||||
const user = !isOppAnim ? this.user : this.target;
|
const user = !isOppAnim ? this.user : this.target;
|
||||||
const target = !isOppAnim ? this.target : this.user;
|
const target = !isOppAnim ? this.target : this.user;
|
||||||
|
|
||||||
if (!target.isOnField()) {
|
if (!target.isOnField() && !this.playOnEmptyField) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -977,13 +1025,183 @@ export abstract class BattleAnim {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getGraphicFrameDataWithoutTarget(frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
|
||||||
|
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
|
||||||
|
[AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
|
||||||
|
[AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
|
||||||
|
[AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
|
||||||
|
]);
|
||||||
|
|
||||||
|
let g = 0;
|
||||||
|
let u = 0;
|
||||||
|
let t = 0;
|
||||||
|
|
||||||
|
for (const frame of frames) {
|
||||||
|
let { x , y } = frame;
|
||||||
|
const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1);
|
||||||
|
const scaleY = (frame.zoomY / 100);
|
||||||
|
x += targetInitialX;
|
||||||
|
y += targetInitialY;
|
||||||
|
const angle = -frame.angle;
|
||||||
|
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
|
||||||
|
ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle });
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param scene
|
||||||
|
* @param targetInitialX
|
||||||
|
* @param targetInitialY
|
||||||
|
* @param frameTimeMult
|
||||||
|
* @param frameTimedEventPriority
|
||||||
|
* - 0 is behind all other sprites (except BG)
|
||||||
|
* - 1 on top of player field
|
||||||
|
* - 3 is on top of both fields
|
||||||
|
* - 5 is on top of player sprite
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) {
|
||||||
|
const spriteCache: SpriteCache = {
|
||||||
|
[AnimFrameTarget.GRAPHIC]: [],
|
||||||
|
[AnimFrameTarget.USER]: [],
|
||||||
|
[AnimFrameTarget.TARGET]: []
|
||||||
|
};
|
||||||
|
const spritePriorities: integer[] = [];
|
||||||
|
|
||||||
|
const cleanUpAndComplete = () => {
|
||||||
|
for (const ms of Object.values(spriteCache).flat()) {
|
||||||
|
if (ms) {
|
||||||
|
ms.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.bgSprite) {
|
||||||
|
this.bgSprite.destroy();
|
||||||
|
}
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!scene.moveAnimations) {
|
||||||
|
return cleanUpAndComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
const anim = this.getAnim();
|
||||||
|
|
||||||
|
this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ];
|
||||||
|
this.dstLine = [ 150, 75, targetInitialX, targetInitialY ];
|
||||||
|
|
||||||
|
let r = anim.frames.length;
|
||||||
|
let f = 0;
|
||||||
|
|
||||||
|
const existingFieldSprites = [...scene.field.getAll()];
|
||||||
|
|
||||||
|
scene.tweens.addCounter({
|
||||||
|
duration: Utils.getFrameMs(3) * frameTimeMult,
|
||||||
|
repeat: anim.frames.length,
|
||||||
|
onRepeat: () => {
|
||||||
|
const spriteFrames = anim.frames[f];
|
||||||
|
const frameData = this.getGraphicFrameDataWithoutTarget(anim.frames[f], targetInitialX, targetInitialY);
|
||||||
|
const u = 0;
|
||||||
|
const t = 0;
|
||||||
|
let g = 0;
|
||||||
|
for (const frame of spriteFrames) {
|
||||||
|
if (frame.target !== AnimFrameTarget.GRAPHIC) {
|
||||||
|
console.log("Encounter animations do not support targets");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
|
||||||
|
if (g === sprites.length) {
|
||||||
|
const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim.graphic, 1);
|
||||||
|
sprites.push(newSprite);
|
||||||
|
scene.field.add(newSprite);
|
||||||
|
spritePriorities.push(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphicIndex = g++;
|
||||||
|
const moveSprite = sprites[graphicIndex];
|
||||||
|
spritePriorities[graphicIndex] = frame.priority;
|
||||||
|
if (!isNullOrUndefined(frame.priority)) {
|
||||||
|
const setSpritePriority = (priority: integer) => {
|
||||||
|
try {
|
||||||
|
if (existingFieldSprites.length > priority) {
|
||||||
|
// Move to specified priority index
|
||||||
|
scene.field.moveTo(moveSprite, scene.field.getIndex(existingFieldSprites[priority]));
|
||||||
|
} else {
|
||||||
|
// Move to top of scene
|
||||||
|
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
|
||||||
|
}
|
||||||
|
} catch (ignored) {
|
||||||
|
console.log("index is no longer valid");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setSpritePriority(frame.priority);
|
||||||
|
}
|
||||||
|
moveSprite.setFrame(frame.graphicFrame);
|
||||||
|
|
||||||
|
const graphicFrameData = frameData.get(frame.target).get(graphicIndex);
|
||||||
|
moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y);
|
||||||
|
moveSprite.setAngle(graphicFrameData.angle);
|
||||||
|
moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY);
|
||||||
|
|
||||||
|
moveSprite.setAlpha(frame.opacity / 255);
|
||||||
|
moveSprite.setVisible(frame.visible);
|
||||||
|
moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE);
|
||||||
|
}
|
||||||
|
if (anim.frameTimedEvents.has(f)) {
|
||||||
|
for (const event of anim.frameTimedEvents.get(f)) {
|
||||||
|
r = Math.max((anim.frames.length - f) + event.execute(scene, this, frameTimedEventPriority), r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const targets = Utils.getEnumValues(AnimFrameTarget);
|
||||||
|
for (const i of targets) {
|
||||||
|
const count = i === AnimFrameTarget.GRAPHIC ? g : i === AnimFrameTarget.USER ? u : t;
|
||||||
|
if (count < spriteCache[i].length) {
|
||||||
|
const spritesToRemove = spriteCache[i].slice(count, spriteCache[i].length);
|
||||||
|
for (const rs of spritesToRemove) {
|
||||||
|
if (!rs.getData("locked") as boolean) {
|
||||||
|
const spriteCacheIndex = spriteCache[i].indexOf(rs);
|
||||||
|
spriteCache[i].splice(spriteCacheIndex, 1);
|
||||||
|
if (i === AnimFrameTarget.GRAPHIC) {
|
||||||
|
spritePriorities.splice(spriteCacheIndex, 1);
|
||||||
|
}
|
||||||
|
rs.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f++;
|
||||||
|
r--;
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
for (const ms of Object.values(spriteCache).flat()) {
|
||||||
|
if (ms && !ms.getData("locked")) {
|
||||||
|
ms.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r) {
|
||||||
|
scene.tweens.addCounter({
|
||||||
|
duration: Utils.getFrameMs(r),
|
||||||
|
onComplete: () => cleanUpAndComplete()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cleanUpAndComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommonBattleAnim extends BattleAnim {
|
export class CommonBattleAnim extends BattleAnim {
|
||||||
public commonAnim: CommonAnim;
|
public commonAnim: CommonAnim;
|
||||||
|
|
||||||
constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) {
|
constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) {
|
||||||
super(user, target || user);
|
super(user, target || user, playOnEmptyField);
|
||||||
|
|
||||||
this.commonAnim = commonAnim;
|
this.commonAnim = commonAnim;
|
||||||
}
|
}
|
||||||
@ -1045,6 +1263,24 @@ export class MoveChargeAnim extends MoveAnim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EncounterBattleAnim extends BattleAnim {
|
||||||
|
public encounterAnim: EncounterAnim;
|
||||||
|
|
||||||
|
constructor(encounterAnim: EncounterAnim, user: Pokemon, target?: Pokemon) {
|
||||||
|
super(user, target || user);
|
||||||
|
|
||||||
|
this.encounterAnim = encounterAnim;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnim(): AnimConfig {
|
||||||
|
return encounterAnims.get(this.encounterAnim);
|
||||||
|
}
|
||||||
|
|
||||||
|
isOppAnim(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function populateAnims() {
|
export async function populateAnims() {
|
||||||
const commonAnimNames = Utils.getEnumKeys(CommonAnim).map(k => k.toLowerCase());
|
const commonAnimNames = Utils.getEnumKeys(CommonAnim).map(k => k.toLowerCase());
|
||||||
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, ""));
|
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, ""));
|
||||||
|
@ -1554,7 +1554,7 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
|
|||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
const mysteryEncounterBattleEffects = pokemon.summonData.mysteryEncounterBattleEffects;
|
const mysteryEncounterBattleEffects = pokemon.mysteryEncounterBattleEffects;
|
||||||
if (mysteryEncounterBattleEffects) {
|
if (mysteryEncounterBattleEffects) {
|
||||||
mysteryEncounterBattleEffects(pokemon);
|
mysteryEncounterBattleEffects(pokemon);
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveE
|
|||||||
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
/** i18n namespace for encounter */
|
/** i18n namespace for encounter */
|
||||||
const namespace = "mysteryEncounter:dark_deal";
|
const namespace = "mysteryEncounter:darkDeal";
|
||||||
|
|
||||||
// Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals
|
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */
|
||||||
const excludedBosses = [
|
const excludedBosses = [
|
||||||
Species.NECROZMA,
|
Species.NECROZMA,
|
||||||
Species.COSMOG,
|
Species.COSMOG,
|
||||||
@ -68,6 +68,11 @@ const excludedBosses = [
|
|||||||
Species.PECHARUNT,
|
Species.PECHARUNT,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Deal encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/61 | GitHub Issue #61}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const DarkDealEncounter: IMysteryEncounter =
|
export const DarkDealEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)
|
||||||
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
@ -86,32 +91,32 @@ export const DarkDealEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
text: `${namespace}_intro_dialogue`,
|
text: `${namespace}:intro_dialogue`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
|
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
|
||||||
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
|
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
|
||||||
.withCatchAllowed(true)
|
.withCatchAllowed(true)
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
text: `${namespace}_option_1_selected`,
|
text: `${namespace}:option:1:selected_dialogue`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_1_selected_message`,
|
text: `${namespace}:option:1:selected_message`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -121,10 +126,7 @@ export const DarkDealEncounter: IMysteryEncounter =
|
|||||||
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
|
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
|
||||||
scene.removePokemonFromPlayerParty(removedPokemon);
|
scene.removePokemonFromPlayerParty(removedPokemon);
|
||||||
|
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken(
|
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.name);
|
||||||
"pokeName",
|
|
||||||
removedPokemon.name
|
|
||||||
);
|
|
||||||
|
|
||||||
// Store removed pokemon types
|
// Store removed pokemon types
|
||||||
scene.currentBattle.mysteryEncounter.misc = [
|
scene.currentBattle.mysteryEncounter.misc = [
|
||||||
@ -153,7 +155,6 @@ export const DarkDealEncounter: IMysteryEncounter =
|
|||||||
pokemonConfig.formIndex = 0;
|
pokemonConfig.formIndex = 0;
|
||||||
}
|
}
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveMultiplier: 0.75,
|
|
||||||
pokemonConfigs: [pokemonConfig],
|
pokemonConfigs: [pokemonConfig],
|
||||||
};
|
};
|
||||||
return initBattleWithEnemyConfig(scene, config);
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
@ -162,12 +163,12 @@ export const DarkDealEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
text: `${namespace}_option_2_selected`,
|
text: `${namespace}:option:2:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -179,7 +180,7 @@ export const DarkDealEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withOutroDialogue([
|
.withOutroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_outro`
|
text: `${namespace}:outro`
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.build();
|
.build();
|
||||||
|
@ -13,8 +13,13 @@ import IMysteryEncounter, {
|
|||||||
} from "../mystery-encounter";
|
} from "../mystery-encounter";
|
||||||
|
|
||||||
/** i18n namespace for encounter */
|
/** i18n namespace for encounter */
|
||||||
const namespace = "mysteryEncounter:department_store_sale";
|
const namespace = "mysteryEncounter:departmentStoreSale";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Department Store Sale encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/33 | GitHub Issue #33}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
@ -36,21 +41,21 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_dialogue`,
|
text: `${namespace}:intro_dialogue`,
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withHideIntroVisuals(false)
|
.withAutoHideIntroVisuals(false)
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Choose TMs
|
// Choose TMs
|
||||||
@ -75,8 +80,8 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Choose Vitamins
|
// Choose Vitamins
|
||||||
@ -99,8 +104,8 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Choose X Items
|
// Choose X Items
|
||||||
@ -123,8 +128,8 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_4_label`,
|
buttonLabel: `${namespace}:option:4:label`,
|
||||||
buttonTooltip: `${namespace}_option_4_tooltip`,
|
buttonTooltip: `${namespace}:option:4:tooltip`,
|
||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Choose Pokeballs
|
// Choose Pokeballs
|
||||||
@ -149,4 +154,9 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
|||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}:outro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
.build();
|
.build();
|
||||||
|
@ -19,8 +19,13 @@ import IMysteryEncounter, {
|
|||||||
} from "../mystery-encounter";
|
} from "../mystery-encounter";
|
||||||
|
|
||||||
/** i18n namespace for the encounter */
|
/** i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:field_trip";
|
const namespace = "mysteryEncounter:fieldTrip";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field Trip encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/17 | GitHub Issue #17}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const FieldTripEncounter: IMysteryEncounter =
|
export const FieldTripEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
@ -44,27 +49,27 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_dialogue`,
|
text: `${namespace}:intro_dialogue`,
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withHideIntroVisuals(false)
|
.withAutoHideIntroVisuals(false)
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
secondOptionPrompt: `${namespace}_second_option_prompt`,
|
secondOptionPrompt: `${namespace}:second_option_prompt`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -82,11 +87,11 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
if (!correctMove) {
|
if (!correctMove) {
|
||||||
encounter.options[0].dialogue.selected = [
|
encounter.options[0].dialogue.selected = [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_incorrect`,
|
text: `${namespace}:incorrect`,
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_lesson_learned`,
|
text: `${namespace}:lesson_learned`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
@ -95,7 +100,7 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
encounter.setDialogueToken("move", move.getName());
|
encounter.setDialogueToken("move", move.getName());
|
||||||
encounter.options[0].dialogue.selected = [
|
encounter.options[0].dialogue.selected = [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(scene, [pokemon.id], 100);
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
@ -133,12 +138,12 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
secondOptionPrompt: `${namespace}_second_option_prompt`,
|
secondOptionPrompt: `${namespace}:second_option_prompt`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -156,11 +161,11 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
if (!correctMove) {
|
if (!correctMove) {
|
||||||
encounter.options[1].dialogue.selected = [
|
encounter.options[1].dialogue.selected = [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_incorrect`,
|
text: `${namespace}:incorrect`,
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_lesson_learned`,
|
text: `${namespace}:lesson_learned`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
@ -169,7 +174,7 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
encounter.setDialogueToken("move", move.getName());
|
encounter.setDialogueToken("move", move.getName());
|
||||||
encounter.options[1].dialogue.selected = [
|
encounter.options[1].dialogue.selected = [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(scene, [pokemon.id], 100);
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
@ -207,12 +212,12 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
secondOptionPrompt: `${namespace}_second_option_prompt`,
|
secondOptionPrompt: `${namespace}:second_option_prompt`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -230,11 +235,11 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
if (!correctMove) {
|
if (!correctMove) {
|
||||||
encounter.options[2].dialogue.selected = [
|
encounter.options[2].dialogue.selected = [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_incorrect`,
|
text: `${namespace}:incorrect`,
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_lesson_learned`,
|
text: `${namespace}:lesson_learned`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(
|
setEncounterExp(
|
||||||
@ -247,7 +252,7 @@ export const FieldTripEncounter: IMysteryEncounter =
|
|||||||
encounter.setDialogueToken("move", move.getName());
|
encounter.setDialogueToken("move", move.getName());
|
||||||
encounter.options[2].dialogue.selected = [
|
encounter.options[2].dialogue.selected = [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(scene, [pokemon.id], 100);
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
@ -0,0 +1,251 @@
|
|||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { applyDamageToPokemon, EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { modifierTypes, } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
|
import { TypeRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Gender } from "#app/data/gender";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { WeatherType } from "#app/data/weather";
|
||||||
|
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:fieryFallout";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage percentage taken when suffering the heat.
|
||||||
|
* Can be a number between `0` - `100`.
|
||||||
|
* The higher the more damage taken (100% = instant KO).
|
||||||
|
*/
|
||||||
|
const DAMAGE_PERCENTAGE: number = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fiery Fallout encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const FieryFalloutEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(40, 180)
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withIntroSpriteConfigs([]) // Set in onInit()
|
||||||
|
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}:intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate boss mons
|
||||||
|
const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA);
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: volcaronaSpecies,
|
||||||
|
isBoss: false,
|
||||||
|
gender: Gender.MALE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: volcaronaSpecies,
|
||||||
|
isBoss: false,
|
||||||
|
gender: Gender.FEMALE
|
||||||
|
}
|
||||||
|
],
|
||||||
|
doubleBattle: true,
|
||||||
|
disableSwitch: true
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
// Load hidden Volcarona sprites
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: volcaronaSpecies.getSpriteId(false),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
repeat: true,
|
||||||
|
hidden: true,
|
||||||
|
hasShadow: true,
|
||||||
|
x: -20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: volcaronaSpecies.getSpriteId(true ),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
repeat: true,
|
||||||
|
hidden: true,
|
||||||
|
hasShadow: true,
|
||||||
|
x: 20
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Load animations/sfx for Volcarona moves
|
||||||
|
initCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]);
|
||||||
|
|
||||||
|
scene.arena.trySetWeather(WeatherType.SUNNY, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
|
// Play animations
|
||||||
|
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon());
|
||||||
|
background.playWithoutTargets(scene, 200, 70, 2, 3);
|
||||||
|
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon());
|
||||||
|
animation.playWithoutTargets(scene, 80, 100, 2);
|
||||||
|
scene.time.delayedCall(600, () => {
|
||||||
|
animation.playWithoutTargets(scene, -20, 100, 2);
|
||||||
|
});
|
||||||
|
scene.time.delayedCall(1200, () => {
|
||||||
|
animation.playWithoutTargets(scene, 140, 150, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}:title`)
|
||||||
|
.withDescription(`${namespace}:description`)
|
||||||
|
.withQuery(`${namespace}:query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:1:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
setEncounterRewards(scene,
|
||||||
|
{ fillRemaining: true },
|
||||||
|
null,
|
||||||
|
() => {
|
||||||
|
giveLeadPokemonCharcoal(scene);
|
||||||
|
});
|
||||||
|
|
||||||
|
encounter.startOfBattleEffects.push(
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.FIRE_SPIN),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
|
targets: [BattlerIndex.PLAYER_2],
|
||||||
|
move: new PokemonMove(Moves.FIRE_SPIN),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.ENEMY],
|
||||||
|
move: new PokemonMove(Moves.QUIVER_DANCE),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
|
targets: [BattlerIndex.ENEMY_2],
|
||||||
|
move: new PokemonMove(Moves.QUIVER_DANCE),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:2:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Damage non-fire types and burn 1 random non-fire type member
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const nonFireTypes = scene.getParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
|
||||||
|
|
||||||
|
for (const pkm of nonFireTypes) {
|
||||||
|
const percentage = DAMAGE_PERCENTAGE / 100;
|
||||||
|
const damage = Math.floor(pkm.getMaxHp() * percentage);
|
||||||
|
applyDamageToPokemon(scene, pkm, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burn random member
|
||||||
|
const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status?.effect === StatusEffect.BURN);
|
||||||
|
if (burnable?.length > 0) {
|
||||||
|
const roll = randSeedInt(burnable.length);
|
||||||
|
const chosenPokemon = burnable[roll];
|
||||||
|
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
||||||
|
// Burn applied
|
||||||
|
encounter.setDialogueToken("burnedPokemon", chosenPokemon.name);
|
||||||
|
queueEncounterMessage(scene, `${namespace}:option:2:target_burned`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No rewards
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3PrimaryName dialogue token automatically
|
||||||
|
.withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3SecondaryName dialogue token automatically
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}:option:3:disabled_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:3:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Fire types help calm the Volcarona
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
setEncounterRewards(scene,
|
||||||
|
{ fillRemaining: true },
|
||||||
|
null,
|
||||||
|
() => {
|
||||||
|
giveLeadPokemonCharcoal(scene);
|
||||||
|
});
|
||||||
|
|
||||||
|
const primary = encounter.options[2].primaryPokemon;
|
||||||
|
const secondary = encounter.options[2].secondaryPokemon[0];
|
||||||
|
|
||||||
|
setEncounterExp(scene, [primary.id, secondary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
function giveLeadPokemonCharcoal(scene: BattleScene) {
|
||||||
|
// Give first party pokemon Charcoal for free at end of battle
|
||||||
|
const leadPokemon = scene.getParty()?.[0];
|
||||||
|
if (leadPokemon) {
|
||||||
|
const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]);
|
||||||
|
scene.addModifier(charcoal.type.newModifier(leadPokemon), true);
|
||||||
|
scene.updateModifiers();
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.name);
|
||||||
|
queueEncounterMessage(scene, `${namespace}:found_charcoal`);
|
||||||
|
}
|
||||||
|
}
|
@ -27,10 +27,16 @@ import IMysteryEncounter, {
|
|||||||
} from "../mystery-encounter";
|
} from "../mystery-encounter";
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:fight_or_flight";
|
const namespace = "mysteryEncounter:fightOrFlight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fight or Flight encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/24 | GitHub Issue #24}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const FightOrFlightEncounter: IMysteryEncounter =
|
export const FightOrFlightEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.FIGHT_OR_FLIGHT
|
MysteryEncounterType.FIGHT_OR_FLIGHT
|
||||||
@ -97,23 +103,23 @@ export const FightOrFlightEncounter: IMysteryEncounter =
|
|||||||
const primaryPokemon = encounter.options[1].primaryPokemon;
|
const primaryPokemon = encounter.options[1].primaryPokemon;
|
||||||
if (primaryPokemon) {
|
if (primaryPokemon) {
|
||||||
// Use primaryPokemon to execute the thievery
|
// Use primaryPokemon to execute the thievery
|
||||||
encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_steal_tooltip`;
|
encounter.options[1].dialogue.buttonTooltip = `${namespace}:option:2:tooltip_special`;
|
||||||
} else {
|
} else {
|
||||||
encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_tooltip`;
|
encounter.options[1].dialogue.buttonTooltip = `${namespace}:option:2:tooltip`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_1_selected_message`,
|
text: `${namespace}:option:1:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -130,8 +136,8 @@ export const FightOrFlightEncounter: IMysteryEncounter =
|
|||||||
.withOptionMode(EncounterOptionMode.DEFAULT_OR_SPECIAL)
|
.withOptionMode(EncounterOptionMode.DEFAULT_OR_SPECIAL)
|
||||||
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
|
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
// Pick steal
|
// Pick steal
|
||||||
@ -143,7 +149,7 @@ export const FightOrFlightEncounter: IMysteryEncounter =
|
|||||||
const primaryPokemon = encounter.options[1].primaryPokemon;
|
const primaryPokemon = encounter.options[1].primaryPokemon;
|
||||||
if (primaryPokemon) {
|
if (primaryPokemon) {
|
||||||
// Use primaryPokemon to execute the thievery
|
// Use primaryPokemon to execute the thievery
|
||||||
await showEncounterText(scene, `${namespace}_option_2_steal_result`);
|
await showEncounterText(scene, `${namespace}:option:2:steal_result`);
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -154,16 +160,16 @@ export const FightOrFlightEncounter: IMysteryEncounter =
|
|||||||
const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0];
|
const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0];
|
||||||
config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
||||||
config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||||
pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name);
|
pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(pokemon));
|
||||||
queueEncounterMessage(pokemon.scene, `${namespace}_boss_enraged`);
|
queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`);
|
||||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
||||||
};
|
};
|
||||||
await showEncounterText(scene, `${namespace}_option_2_bad_result`);
|
await showEncounterText(scene, `${namespace}:option:2:bad_result`);
|
||||||
await initBattleWithEnemyConfig(scene, config);
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
} else {
|
} else {
|
||||||
// Steal item (37.5%)
|
// Steal item (37.5%)
|
||||||
// Display result message then proceed to rewards
|
// Display result message then proceed to rewards
|
||||||
await showEncounterText(scene, `${namespace}_option_2_good_result`);
|
await showEncounterText(scene, `${namespace}:option:2:good_result`);
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -171,11 +177,11 @@ export const FightOrFlightEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_3_selected`,
|
text: `${namespace}:option:3:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -16,12 +16,12 @@ const OPTION_2_REQUIRED_MOVE = Moves.FLY;
|
|||||||
*/
|
*/
|
||||||
const DAMAGE_PERCENTAGE: number = 25;
|
const DAMAGE_PERCENTAGE: number = 25;
|
||||||
/** The i18n namespace for the encounter */
|
/** The i18n namespace for the encounter */
|
||||||
const namepsace = "mysteryEncounter:lostAtSea";
|
const namespace = "mysteryEncounter:lostAtSea";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lost at sea encounter.
|
* Lost at sea encounter.
|
||||||
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9}
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9}
|
||||||
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts)
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
*/
|
*/
|
||||||
export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
|
export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
@ -35,7 +35,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
y: 3,
|
y: 3,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withIntroDialogue([{ text: `${namepsace}:intro` }])
|
.withIntroDialogue([{ text: `${namespace}:intro` }])
|
||||||
.withOnInit((scene: BattleScene) => {
|
.withOnInit((scene: BattleScene) => {
|
||||||
const { mysteryEncounter } = scene.currentBattle;
|
const { mysteryEncounter } = scene.currentBattle;
|
||||||
|
|
||||||
@ -45,22 +45,22 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withTitle(`${namepsace}:title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namepsace}:description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namepsace}:query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(
|
.withOption(
|
||||||
// Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/
|
// Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE)
|
.withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE)
|
||||||
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namepsace}:option:1:label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
disabledButtonLabel: `${namepsace}:option:1:label_disabled`,
|
disabledButtonLabel: `${namespace}:option:1:label_disabled`,
|
||||||
buttonTooltip: `${namepsace}:option:1:tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
disabledButtonTooltip: `${namepsace}:option:1:tooltip_disabled`,
|
disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namepsace}:option:1:selected`,
|
text: `${namespace}:option:1:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -73,13 +73,13 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
.withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE)
|
.withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE)
|
||||||
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namepsace}:option:2:label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
disabledButtonLabel: `${namepsace}:option:2:label_disabled`,
|
disabledButtonLabel: `${namespace}:option:2:label_disabled`,
|
||||||
buttonTooltip: `${namepsace}:option:2:tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
disabledButtonTooltip: `${namepsace}:option:2:tooltip_disabled`,
|
disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namepsace}:option:2:selected`,
|
text: `${namespace}:option:2:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -89,11 +89,11 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
// Option 3: Wander aimlessly
|
// Option 3: Wander aimlessly
|
||||||
{
|
{
|
||||||
buttonLabel: `${namepsace}:option:3:label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namepsace}:option:3:tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namepsace}:option:3:selected`,
|
text: `${namespace}:option:3:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -113,7 +113,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
)
|
)
|
||||||
.withOutroDialogue([
|
.withOutroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namepsace}:outro`,
|
text: `${namespace}:outro`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.build();
|
.build();
|
||||||
|
@ -21,8 +21,13 @@ import IMysteryEncounter, {
|
|||||||
} from "../mystery-encounter";
|
} from "../mystery-encounter";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:mysterious_challengers";
|
const namespace = "mysteryEncounter:mysteriousChallengers";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mysterious Challengers encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/41 | GitHub Issue #41}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const MysteriousChallengersEncounter: IMysteryEncounter =
|
export const MysteriousChallengersEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.MYSTERIOUS_CHALLENGERS
|
MysteryEncounterType.MYSTERIOUS_CHALLENGERS
|
||||||
@ -32,7 +37,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter =
|
|||||||
.withIntroSpriteConfigs([]) // These are set in onInit()
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withOnInit((scene: BattleScene) => {
|
.withOnInit((scene: BattleScene) => {
|
||||||
@ -94,7 +99,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter =
|
|||||||
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
||||||
encounter.enemyPartyConfigs.push({
|
encounter.enemyPartyConfigs.push({
|
||||||
trainerConfig: brutalConfig,
|
trainerConfig: brutalConfig,
|
||||||
levelAdditiveMultiplier: 1.1,
|
levelAdditiveMultiplier: 1,
|
||||||
female: female,
|
female: female,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,16 +126,16 @@ export const MysteriousChallengersEncounter: IMysteryEncounter =
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected_message`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -151,11 +156,11 @@ export const MysteriousChallengersEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected_message`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -176,11 +181,11 @@ export const MysteriousChallengersEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected_message`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -204,7 +209,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withOutroDialogue([
|
.withOutroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_outro_win`,
|
text: `${namespace}:outro`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.build();
|
.build();
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import {
|
import { leaveEncounterWithoutBattle, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
leaveEncounterWithoutBattle,
|
|
||||||
setEncounterRewards
|
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
||||||
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { randSeedInt } from "#app/utils.js";
|
import { randSeedInt } from "#app/utils.js";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "../../../battle-scene";
|
import BattleScene from "../../../battle-scene";
|
||||||
import IMysteryEncounter, {
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
MysteryEncounterBuilder,
|
|
||||||
MysteryEncounterTier,
|
|
||||||
} from "../mystery-encounter";
|
|
||||||
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
|
||||||
|
/** i18n namespace for encounter */
|
||||||
|
const namespace = "mysteryEncounter:mysteriousChest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mysterious Chest encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/32 | GitHub Issue #32}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const MysteriousChestEncounter: IMysteryEncounter =
|
export const MysteriousChestEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST)
|
||||||
MysteryEncounterType.MYSTERIOUS_CHEST
|
|
||||||
)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 2 to 180
|
.withSceneWaveRangeRequirement(10, 180) // waves 2 to 180
|
||||||
.withHideIntroVisuals(false)
|
.withAutoHideIntroVisuals(false)
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
{
|
{
|
||||||
spriteKey: "chest_blue",
|
spriteKey: "chest_blue",
|
||||||
@ -34,21 +34,21 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: "mysteryEncounter:mysterious_chest_intro_message",
|
text: "${namespace}:intro:message",
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withTitle("mysteryEncounter:mysterious_chest_title")
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription("mysteryEncounter:mysterious_chest_description")
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery("mysteryEncounter:mysterious_chest_query")
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label",
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip",
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: "mysteryEncounter:mysterious_chest_option_1_selected_message",
|
text: `${namespace}:option:1:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -73,7 +73,7 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Display result message then proceed to rewards
|
// Display result message then proceed to rewards
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_normal_result");
|
queueEncounterMessage(scene, `${namespace}:option:1:normal`);
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
} else if (roll > 40) {
|
} else if (roll > 40) {
|
||||||
// Choose between 3 ULTRA tier items (20%)
|
// Choose between 3 ULTRA tier items (20%)
|
||||||
@ -85,7 +85,7 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Display result message then proceed to rewards
|
// Display result message then proceed to rewards
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_good_result");
|
queueEncounterMessage(scene, `${namespace}:option:1:good`);
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
} else if (roll > 36) {
|
} else if (roll > 36) {
|
||||||
// Choose between 2 ROGUE tier items (4%)
|
// Choose between 2 ROGUE tier items (4%)
|
||||||
@ -93,7 +93,7 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
||||||
});
|
});
|
||||||
// Display result message then proceed to rewards
|
// Display result message then proceed to rewards
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_great_result");
|
queueEncounterMessage(scene, `${namespace}:option:1:great`);
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
} else if (roll > 35) {
|
} else if (roll > 35) {
|
||||||
// Choose 1 MASTER tier item (1%)
|
// Choose 1 MASTER tier item (1%)
|
||||||
@ -101,7 +101,7 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
guaranteedModifierTiers: [ModifierTier.MASTER],
|
guaranteedModifierTiers: [ModifierTier.MASTER],
|
||||||
});
|
});
|
||||||
// Display result message then proceed to rewards
|
// Display result message then proceed to rewards
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_amazing_result");
|
queueEncounterMessage(scene, `${namespace}:option:1:amazing`);
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
} else {
|
} else {
|
||||||
// Your highest level unfainted Pok<6F>mon gets OHKO. Progress with no rewards (35%)
|
// Your highest level unfainted Pok<6F>mon gets OHKO. Progress with no rewards (35%)
|
||||||
@ -114,7 +114,7 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.name);
|
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.name);
|
||||||
// Show which Pokemon was KOed, then leave encounter with no rewards
|
// Show which Pokemon was KOed, then leave encounter with no rewards
|
||||||
// Does this synchronously so that game over doesn't happen over result message
|
// Does this synchronously so that game over doesn't happen over result message
|
||||||
await showEncounterText(scene, "mysteryEncounter:mysterious_chest_option_1_bad_result").then(() => {
|
await showEncounterText(scene, `${namespace}:option:1:bad`).then(() => {
|
||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -123,11 +123,11 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: "mysteryEncounter:mysterious_chest_option_2_label",
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_chest_option_2_tooltip",
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: "mysteryEncounter:mysterious_chest_option_2_selected_message",
|
text: `${namespace}:option:2:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,6 @@ import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, Myste
|
|||||||
import MysteryEncounterOption, { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption, { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { ScanIvsPhase, SummonPhase, VictoryPhase } from "#app/phases";
|
import { ScanIvsPhase, SummonPhase, VictoryPhase } from "#app/phases";
|
||||||
import i18next from "i18next";
|
|
||||||
import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
|
import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
|
||||||
import { EnemyPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { PokeballType } from "#app/data/pokeball";
|
import { PokeballType } from "#app/data/pokeball";
|
||||||
@ -14,11 +13,19 @@ import { IntegerHolder, randSeedInt } from "#app/utils";
|
|||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterTier, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterTier, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:safari_zone";
|
const namespace = "mysteryEncounter:safariZone";
|
||||||
|
|
||||||
|
const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safari Zone encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/39 | GitHub Issue #39}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const SafariZoneEncounter: IMysteryEncounter =
|
export const SafariZoneEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE)
|
||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
@ -36,32 +43,33 @@ export const SafariZoneEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
.withOption(new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Cost equal to 1 Max Revive
|
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Cost equal to 1 Max Revive
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_1_selected_message`,
|
text: `${namespace}:option:1:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
// Start safari encounter
|
// Start safari encounter
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
encounter.encounterVariant = MysteryEncounterVariant.SAFARI_BATTLE;
|
encounter.encounterVariant = MysteryEncounterVariant.CONTINUOUS_ENCOUNTER;
|
||||||
encounter.misc = {
|
encounter.misc = {
|
||||||
safariPokemonRemaining: 3
|
safariPokemonRemaining: 3
|
||||||
};
|
};
|
||||||
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||||
|
// Load bait/mud assets
|
||||||
scene.loadSe("PRSFX- Bug Bite", "battle_anims");
|
scene.loadSe("PRSFX- Bug Bite", "battle_anims");
|
||||||
scene.loadSe("PRSFX- Sludge Bomb2", "battle_anims");
|
scene.loadSe("PRSFX- Sludge Bomb2", "battle_anims");
|
||||||
scene.loadSe("PRSFX- Taunt2", "battle_anims");
|
scene.loadSe("PRSFX- Taunt2", "battle_anims");
|
||||||
@ -75,11 +83,11 @@ export const SafariZoneEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_2_selected_message`,
|
text: `${namespace}:option:2:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -110,11 +118,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_pokeball_option_label`,
|
buttonLabel: `${namespace}:safari:1:label`,
|
||||||
buttonTooltip: `${namespace}_pokeball_option_tooltip`,
|
buttonTooltip: `${namespace}:safari:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_pokeball_option_selected`,
|
text: `${namespace}:safari:1:selected`,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -144,11 +152,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_bait_option_label`,
|
buttonLabel: `${namespace}:safari:2:label`,
|
||||||
buttonTooltip: `${namespace}_bait_option_tooltip`,
|
buttonTooltip: `${namespace}:safari:2:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_bait_option_selected`,
|
text: `${namespace}:safari:2:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -162,9 +170,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
// 80% chance to increase flee stage +1
|
// 80% chance to increase flee stage +1
|
||||||
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8);
|
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8);
|
||||||
if (!fleeChangeResult) {
|
if (!fleeChangeResult) {
|
||||||
await showEncounterText(scene, i18next.t(`${namespace}_pokemon_busy_eating`, { pokemonName: pokemon.name }), 1500, false );
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:busy_eating`), 1000, false );
|
||||||
} else {
|
} else {
|
||||||
await showEncounterText(scene, i18next.t(`${namespace}_pokemon_eating`, { pokemonName: pokemon.name }), 1500, false);
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:eating`), 1000, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await doEndTurn(scene, 1);
|
await doEndTurn(scene, 1);
|
||||||
@ -174,11 +182,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_mud_option_label`,
|
buttonLabel: `${namespace}:safari:3:label`,
|
||||||
buttonTooltip: `${namespace}_mud_option_tooltip`,
|
buttonTooltip: `${namespace}:safari:3:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_mud_option_selected`,
|
text: `${namespace}:safari:3:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -191,9 +199,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
// 80% chance to decrease catch stage -1
|
// 80% chance to decrease catch stage -1
|
||||||
const catchChangeResult = tryChangeCatchStage(scene, -1, 8);
|
const catchChangeResult = tryChangeCatchStage(scene, -1, 8);
|
||||||
if (!catchChangeResult) {
|
if (!catchChangeResult) {
|
||||||
await showEncounterText(scene, i18next.t(`${namespace}_pokemon_beside_itself_angry`, { pokemonName: pokemon.name }), 1500, false );
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:beside_itself_angry`), 1000, false );
|
||||||
} else {
|
} else {
|
||||||
await showEncounterText(scene, i18next.t(`${namespace}_pokemon_angry`, { pokemonName: pokemon.name }), 1500, false );
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:angry`), 1000, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
await doEndTurn(scene, 2);
|
await doEndTurn(scene, 2);
|
||||||
@ -203,8 +211,8 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_flee_option_label`,
|
buttonLabel: `${namespace}:safari:4:label`,
|
||||||
buttonTooltip: `${namespace}_flee_option_tooltip`,
|
buttonTooltip: `${namespace}:safari:4:tooltip`,
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
// Flee option
|
// Flee option
|
||||||
@ -226,7 +234,8 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
|||||||
async function summonSafariPokemon(scene: BattleScene) {
|
async function summonSafariPokemon(scene: BattleScene) {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
// Message pokemon remaining
|
// Message pokemon remaining
|
||||||
scene.queueMessage(i18next.t(`${namespace}_remaining_count`, { remainingCount: encounter.misc.safariPokemonRemaining}), null, true);
|
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
|
||||||
|
scene.queueMessage(getEncounterText(scene, `${namespace}:safari:remaining_count`), null, true);
|
||||||
|
|
||||||
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
|
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
|
||||||
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
||||||
@ -274,7 +283,8 @@ async function summonSafariPokemon(scene: BattleScene) {
|
|||||||
|
|
||||||
scene.unshiftPhase(new SummonPhase(scene, 0, false));
|
scene.unshiftPhase(new SummonPhase(scene, 0, false));
|
||||||
|
|
||||||
showEncounterText(scene, i18next.t("battle:singleWildAppeared", { pokemonName: pokemon.name }), 1500, false)
|
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
||||||
|
showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared"), 1500, false)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
|
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
|
||||||
if (ivScannerModifier) {
|
if (ivScannerModifier) {
|
||||||
@ -304,16 +314,16 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
|
|||||||
bait.setOrigin(0.5, 0.625);
|
bait.setOrigin(0.5, 0.625);
|
||||||
scene.field.add(bait);
|
scene.field.add(bait);
|
||||||
|
|
||||||
scene.playSound("pb_throw");
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||||
scene.time.delayedCall(512, () => {
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||||
|
scene.playSound("pb_throw");
|
||||||
|
|
||||||
// Trainer throw frames
|
// Trainer throw frames
|
||||||
scene.trainer.setFrame("2");
|
scene.trainer.setFrame("2");
|
||||||
scene.time.delayedCall(256, () => {
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||||
scene.trainer.setFrame("3");
|
scene.trainer.setFrame("3");
|
||||||
scene.time.delayedCall(768, () => {
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -366,20 +376,20 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<bool
|
|||||||
const originalY: number = pokemon.y;
|
const originalY: number = pokemon.y;
|
||||||
|
|
||||||
const fpOffset = pokemon.getFieldPositionOffset();
|
const fpOffset = pokemon.getFieldPositionOffset();
|
||||||
const mud: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 25, "mud", "0001.png");
|
const mud: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 35, "mud", "0001.png");
|
||||||
mud.setOrigin(0.5, 0.625);
|
mud.setOrigin(0.5, 0.625);
|
||||||
scene.field.add(mud);
|
scene.field.add(mud);
|
||||||
|
|
||||||
scene.playSound("pb_throw");
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||||
scene.time.delayedCall(512, () => {
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||||
|
scene.playSound("pb_throw");
|
||||||
|
|
||||||
// Trainer throw frames
|
// Trainer throw frames
|
||||||
scene.trainer.setFrame("2");
|
scene.trainer.setFrame("2");
|
||||||
scene.time.delayedCall(256, () => {
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||||
scene.trainer.setFrame("3");
|
scene.trainer.setFrame("3");
|
||||||
scene.time.delayedCall(768, () => {
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -395,32 +405,39 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<bool
|
|||||||
scene.playSound("PRSFX- Sludge Bomb2");
|
scene.playSound("PRSFX- Sludge Bomb2");
|
||||||
mud.setFrame("0002.png");
|
mud.setFrame("0002.png");
|
||||||
// Mud splat
|
// Mud splat
|
||||||
scene.time.delayedCall(512, () => {
|
scene.time.delayedCall(200, () => {
|
||||||
mud.setFrame("0003.png");
|
mud.setFrame("0003.png");
|
||||||
scene.time.delayedCall(512, () => {
|
scene.time.delayedCall(400, () => {
|
||||||
mud.setFrame("0004.png");
|
mud.setFrame("0004.png");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
scene.time.delayedCall(1536, () => {
|
// Fade mud then angry animation
|
||||||
mud.destroy();
|
scene.tweens.add({
|
||||||
scene.tweens.add({
|
targets: mud,
|
||||||
targets: pokemon,
|
alpha: 0,
|
||||||
duration: 300,
|
ease: "Cubic.easeIn",
|
||||||
ease: "Cubic.easeOut",
|
duration: 1000,
|
||||||
yoyo: true,
|
onComplete: () => {
|
||||||
y: originalY - 20,
|
mud.destroy();
|
||||||
loop: 1,
|
scene.tweens.add({
|
||||||
onStart: () => {
|
targets: pokemon,
|
||||||
scene.playSound("PRSFX- Taunt2");
|
duration: 300,
|
||||||
},
|
ease: "Cubic.easeOut",
|
||||||
onLoop: () => {
|
yoyo: true,
|
||||||
scene.playSound("PRSFX- Taunt2");
|
y: originalY - 20,
|
||||||
},
|
loop: 1,
|
||||||
onComplete: () => {
|
onStart: () => {
|
||||||
resolve(true);
|
scene.playSound("PRSFX- Taunt2");
|
||||||
}
|
},
|
||||||
});
|
onLoop: () => {
|
||||||
|
scene.playSound("PRSFX- Taunt2");
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -456,14 +473,15 @@ function tryChangeCatchStage(scene: BattleScene, change: number, chance?: number
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doEndTurn(scene: BattleScene, cursorIndex: number, message?: string) {
|
async function doEndTurn(scene: BattleScene, cursorIndex: number) {
|
||||||
const pokemon = scene.currentBattle.mysteryEncounter.misc.pokemon;
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
const isFlee = isPokemonFlee(pokemon, scene.currentBattle.mysteryEncounter.misc.fleeStage);
|
const pokemon = encounter.misc.pokemon;
|
||||||
|
const isFlee = isPokemonFlee(pokemon, encounter.misc.fleeStage);
|
||||||
if (isFlee) {
|
if (isFlee) {
|
||||||
// Pokemon flees!
|
// Pokemon flees!
|
||||||
await doPokemonFlee(scene, pokemon);
|
await doPokemonFlee(scene, pokemon);
|
||||||
// Check how many safari pokemon left
|
// Check how many safari pokemon left
|
||||||
if (scene.currentBattle.mysteryEncounter.misc.safariPokemonRemaining > 0) {
|
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||||
await summonSafariPokemon(scene);
|
await summonSafariPokemon(scene);
|
||||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||||
} else {
|
} else {
|
||||||
@ -471,7 +489,7 @@ async function doEndTurn(scene: BattleScene, cursorIndex: number, message?: stri
|
|||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scene.queueMessage(i18next.t(`${namespace}_pokemon_watching`, { pokemonName: pokemon.name }), 0, null, 1000);
|
scene.queueMessage(getEncounterText(scene, `${namespace}:safari:watching`), 0, null, 1000);
|
||||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,20 @@ import { modifierTypes } from "#app/modifier/modifier-type";
|
|||||||
import { randSeedInt } from "#app/utils";
|
import { randSeedInt } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import i18next from "i18next";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
import BattleScene from "../../../battle-scene";
|
||||||
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:shady_vitamin_dealer";
|
const namespace = "mysteryEncounter:shadyVitaminDealer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shady Vitamin Dealer encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/34 | GitHub Issue #34}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.SHADY_VITAMIN_DEALER
|
MysteryEncounterType.SHADY_VITAMIN_DEALER
|
||||||
@ -44,26 +48,26 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_dialogue`,
|
text: `${namespace}:intro_dialogue`,
|
||||||
speaker: `${namespace}_speaker`,
|
speaker: `${namespace}:speaker`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2
|
.withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -90,7 +94,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
if (!meetsReqs) {
|
if (!meetsReqs) {
|
||||||
return i18next.t(`${namespace}_invalid_selection`);
|
return getEncounterText(scene, `${namespace}:invalid_selection`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -125,13 +129,13 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
if (randSeedInt(10) < 8) {
|
if (randSeedInt(10) < 8) {
|
||||||
if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) {
|
if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) {
|
||||||
// Toxic applied
|
// Toxic applied
|
||||||
queueEncounterMessage(scene, `${namespace}_bad_poison`);
|
queueEncounterMessage(scene, `${namespace}:bad_poison`);
|
||||||
} else {
|
} else {
|
||||||
// Pokemon immune or something else prevents status
|
// Pokemon immune or something else prevents status
|
||||||
queueEncounterMessage(scene, `${namespace}_damage_only`);
|
queueEncounterMessage(scene, `${namespace}:damage_only`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
queueEncounterMessage(scene, `${namespace}_damage_only`);
|
queueEncounterMessage(scene, `${namespace}:damage_only`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setEncounterExp(scene, [chosenPokemon.id], 100);
|
setEncounterExp(scene, [chosenPokemon.id], 100);
|
||||||
@ -145,11 +149,11 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
.withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5
|
.withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -176,7 +180,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
if (!meetsReqs) {
|
if (!meetsReqs) {
|
||||||
return i18next.t(`${namespace}_invalid_selection`);
|
return getEncounterText(scene, `${namespace}:invalid_selection`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -207,13 +211,13 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
if (randSeedInt(10) < 2) {
|
if (randSeedInt(10) < 2) {
|
||||||
if (chosenPokemon.trySetStatus(StatusEffect.POISON)) {
|
if (chosenPokemon.trySetStatus(StatusEffect.POISON)) {
|
||||||
// Poison applied
|
// Poison applied
|
||||||
queueEncounterMessage(scene, `${namespace}_poison`);
|
queueEncounterMessage(scene, `${namespace}:poison`);
|
||||||
} else {
|
} else {
|
||||||
// Pokemon immune or something else prevents status
|
// Pokemon immune or something else prevents status
|
||||||
queueEncounterMessage(scene, `${namespace}_no_bad_effects`);
|
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
queueEncounterMessage(scene, `${namespace}_no_bad_effects`);
|
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setEncounterExp(scene, [chosenPokemon.id], 100);
|
setEncounterExp(scene, [chosenPokemon.id], 100);
|
||||||
@ -224,8 +228,8 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
|||||||
)
|
)
|
||||||
.withSimpleOption(
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Leave encounter with no rewards or exp
|
// Leave encounter with no rewards or exp
|
||||||
|
@ -1,165 +0,0 @@
|
|||||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { BerryType } from "#enums/berry-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import * as Utils from "../../../utils";
|
|
||||||
import { getPokemonSpecies } from "../../pokemon-species";
|
|
||||||
import { Status, StatusEffect } from "../../status-effect";
|
|
||||||
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
|
||||||
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
|
||||||
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
||||||
|
|
||||||
/** i18n namespace for the encounter */
|
|
||||||
const namespace = "mysteryEncounter:sleeping_snorlax";
|
|
||||||
|
|
||||||
export const SleepingSnorlaxEncounter: IMysteryEncounter =
|
|
||||||
MysteryEncounterBuilder.withEncounterType(
|
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX
|
|
||||||
)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
|
||||||
.withCatchAllowed(true)
|
|
||||||
.withHideWildIntroMessage(true)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: Species.SNORLAX.toString(),
|
|
||||||
fileRoot: "pokemon",
|
|
||||||
hasShadow: true,
|
|
||||||
tint: 0.25,
|
|
||||||
scale: 1.5,
|
|
||||||
repeat: true,
|
|
||||||
y: 5,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.withIntroDialogue([
|
|
||||||
{
|
|
||||||
text: `${namespace}_intro_message`,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.withOnInit((scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
console.log(encounter);
|
|
||||||
|
|
||||||
// Calculate boss mon
|
|
||||||
const bossSpecies = getPokemonSpecies(Species.SNORLAX);
|
|
||||||
const pokemonConfig: EnemyPokemonConfig = {
|
|
||||||
species: bossSpecies,
|
|
||||||
isBoss: true,
|
|
||||||
status: StatusEffect.SLEEP,
|
|
||||||
};
|
|
||||||
const config: EnemyPartyConfig = {
|
|
||||||
levelAdditiveMultiplier: 2,
|
|
||||||
pokemonConfigs: [pokemonConfig],
|
|
||||||
};
|
|
||||||
encounter.enemyPartyConfigs = [config];
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.withTitle(`${namespace}_title`)
|
|
||||||
.withDescription(`${namespace}_description`)
|
|
||||||
.withQuery(`${namespace}_query`)
|
|
||||||
.withSimpleOption(
|
|
||||||
{
|
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: `${namespace}_option_1_selected_message`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
async (scene: BattleScene) => {
|
|
||||||
// Pick battle
|
|
||||||
// TODO: do we want special rewards for this?
|
|
||||||
// setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true});
|
|
||||||
await initBattleWithEnemyConfig(
|
|
||||||
scene,
|
|
||||||
scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.withSimpleOption(
|
|
||||||
{
|
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: `${namespace}_option_2_selected_message`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
async (scene: BattleScene) => {
|
|
||||||
const instance = scene.currentBattle.mysteryEncounter;
|
|
||||||
let roll: integer;
|
|
||||||
scene.executeWithSeedOffset(() => {
|
|
||||||
roll = Utils.randSeedInt(16, 0);
|
|
||||||
}, scene.currentBattle.waveIndex);
|
|
||||||
|
|
||||||
// Half Snorlax exp to entire party
|
|
||||||
setEncounterExp(
|
|
||||||
scene,
|
|
||||||
scene.getParty().map((p) => p.id),
|
|
||||||
98
|
|
||||||
);
|
|
||||||
|
|
||||||
if (roll > 4) {
|
|
||||||
// Fall asleep and get a sitrus berry (75%)
|
|
||||||
const p = instance.primaryPokemon;
|
|
||||||
p.status = new Status(StatusEffect.SLEEP, 0, 3);
|
|
||||||
p.updateInfo(true);
|
|
||||||
// const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]);
|
|
||||||
const sitrus = generateModifierTypeOption(
|
|
||||||
scene,
|
|
||||||
modifierTypes.BERRY,
|
|
||||||
[BerryType.SITRUS]
|
|
||||||
);
|
|
||||||
|
|
||||||
setEncounterRewards(scene, {
|
|
||||||
guaranteedModifierTypeOptions: [sitrus],
|
|
||||||
fillRemaining: false,
|
|
||||||
});
|
|
||||||
queueEncounterMessage(scene, `${namespace}_option_2_bad_result`);
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
} else {
|
|
||||||
// Heal to full (25%)
|
|
||||||
for (const pokemon of scene.getParty()) {
|
|
||||||
pokemon.hp = pokemon.getMaxHp();
|
|
||||||
pokemon.resetStatus();
|
|
||||||
for (const move of pokemon.moveset) {
|
|
||||||
move.ppUsed = 0;
|
|
||||||
}
|
|
||||||
pokemon.updateInfo(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
queueEncounterMessage(scene, `${namespace}_option_2_good_result`);
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.withOption(
|
|
||||||
new MysteryEncounterOptionBuilder()
|
|
||||||
.withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL)
|
|
||||||
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES))
|
|
||||||
.withDialogue({
|
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
|
||||||
disabledButtonTooltip: `${namespace}_option_3_disabled_tooltip`,
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Steal the Snorlax's Leftovers
|
|
||||||
const instance = scene.currentBattle.mysteryEncounter;
|
|
||||||
setEncounterRewards(scene, {
|
|
||||||
guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS],
|
|
||||||
fillRemaining: false,
|
|
||||||
});
|
|
||||||
queueEncounterMessage(scene, `${namespace}_option_3_good_result`);
|
|
||||||
// Snorlax exp to Pokemon that did the stealing
|
|
||||||
setEncounterExp(scene, [instance.primaryPokemon.id], 189);
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build();
|
|
@ -0,0 +1,148 @@
|
|||||||
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||||
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { PartyHealPhase } from "#app/phases";
|
||||||
|
|
||||||
|
/** i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:slumberingSnorlax";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleeping Snorlax encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/103 | GitHub Issue #103}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const SlumberingSnorlaxEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.SLUMBERING_SNORLAX
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.SNORLAX.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 0.25,
|
||||||
|
scale: 1.5,
|
||||||
|
repeat: true,
|
||||||
|
y: 5,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}:intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
console.log(encounter);
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const bossSpecies = getPokemonSpecies(Species.SNORLAX);
|
||||||
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
|
species: bossSpecies,
|
||||||
|
isBoss: true,
|
||||||
|
status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves
|
||||||
|
moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT]
|
||||||
|
};
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 0.5,
|
||||||
|
pokemonConfigs: [pokemonConfig],
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
// Load animations/sfx for Snorlax fight start moves
|
||||||
|
initCustomMovesForEncounter(scene, [Moves.SNORE]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}:title`)
|
||||||
|
.withDescription(`${namespace}:description`)
|
||||||
|
.withQuery(`${namespace}:query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:1:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true});
|
||||||
|
encounter.startOfBattleEffects.push(
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.SNORE),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.SNORE),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:2:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Fall asleep waiting for Snorlax
|
||||||
|
// Full heal party
|
||||||
|
scene.unshiftPhase(new PartyHealPhase(scene, true));
|
||||||
|
queueEncounterMessage(scene, `${namespace}:option:2:rest_result`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES))
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}:option:3:disabled_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:3:selected`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Steal the Snorlax's Leftovers
|
||||||
|
const instance = scene.currentBattle.mysteryEncounter;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false });
|
||||||
|
// Snorlax exp to Pokemon that did the stealing
|
||||||
|
setEncounterExp(scene, instance.primaryPokemon.id, getPokemonSpecies(Species.SNORLAX).baseExp);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
@ -19,8 +19,13 @@ import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-e
|
|||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
|
||||||
/** The i18n namespace for the encounter */
|
/** The i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:training_session";
|
const namespace = "mysteryEncounter:trainingSession";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Training Session encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/43 | GitHub Issue #43}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
export const TrainingSessionEncounter: IMysteryEncounter =
|
export const TrainingSessionEncounter: IMysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.TRAINING_SESSION
|
MysteryEncounterType.TRAINING_SESSION
|
||||||
@ -40,21 +45,21 @@ export const TrainingSessionEncounter: IMysteryEncounter =
|
|||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
{
|
{
|
||||||
text: `${namespace}_intro_message`,
|
text: `${namespace}:intro`,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.withTitle(`${namespace}_title`)
|
.withTitle(`${namespace}:title`)
|
||||||
.withDescription(`${namespace}_description`)
|
.withDescription(`${namespace}:description`)
|
||||||
.withQuery(`${namespace}_query`)
|
.withQuery(`${namespace}:query`)
|
||||||
.withOption(
|
.withOption(
|
||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_1_label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
buttonTooltip: `${namespace}_option_1_tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected_message`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -165,7 +170,7 @@ export const TrainingSessionEncounter: IMysteryEncounter =
|
|||||||
scene.addModifier(mod, true, false, false, true);
|
scene.addModifier(mod, true, false, false, true);
|
||||||
}
|
}
|
||||||
scene.updateModifiers(true);
|
scene.updateModifiers(true);
|
||||||
queueEncounterMessage(scene, `${namespace}_battle_finished_1`);
|
queueEncounterMessage(scene, `${namespace}:option:1:finished`);
|
||||||
};
|
};
|
||||||
|
|
||||||
setEncounterRewards(
|
setEncounterRewards(
|
||||||
@ -183,12 +188,12 @@ export const TrainingSessionEncounter: IMysteryEncounter =
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_2_label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
buttonTooltip: `${namespace}_option_2_tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
secondOptionPrompt: `${namespace}_option_2_select_prompt`,
|
secondOptionPrompt: `${namespace}:option:2:select_prompt`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected_message`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -237,7 +242,7 @@ export const TrainingSessionEncounter: IMysteryEncounter =
|
|||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
const onBeforeRewardsPhase = () => {
|
||||||
queueEncounterMessage(scene, `${namespace}_battle_finished_2`);
|
queueEncounterMessage(scene, `${namespace}:option:2:finished`);
|
||||||
// Add the pokemon back to party with Nature change
|
// Add the pokemon back to party with Nature change
|
||||||
playerPokemon.setNature(encounter.misc.chosenNature);
|
playerPokemon.setNature(encounter.misc.chosenNature);
|
||||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
@ -265,12 +270,12 @@ export const TrainingSessionEncounter: IMysteryEncounter =
|
|||||||
new MysteryEncounterOptionBuilder()
|
new MysteryEncounterOptionBuilder()
|
||||||
.withOptionMode(EncounterOptionMode.DEFAULT)
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}_option_3_label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namespace}_option_3_tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
secondOptionPrompt: `${namespace}_option_3_select_prompt`,
|
secondOptionPrompt: `${namespace}:option:3:select_prompt`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namespace}_option_selected_message`,
|
text: `${namespace}:option:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -332,7 +337,7 @@ export const TrainingSessionEncounter: IMysteryEncounter =
|
|||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
const onBeforeRewardsPhase = () => {
|
||||||
queueEncounterMessage(scene, `${namespace}_battle_finished_3`);
|
queueEncounterMessage(scene, `${namespace}:option:3:finished`);
|
||||||
// Add the pokemon back to party with ability change
|
// Add the pokemon back to party with ability change
|
||||||
const abilityIndex = encounter.misc.abilityIndex;
|
const abilityIndex = encounter.misc.abilityIndex;
|
||||||
if (!!playerPokemon.getFusionSpeciesForm()) {
|
if (!!playerPokemon.getFusionSpeciesForm()) {
|
||||||
|
@ -23,12 +23,6 @@ export class EncounterOptionsDialogue {
|
|||||||
options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options
|
options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MysteryEncounterDialogue {
|
|
||||||
intro?: TextDisplay[];
|
|
||||||
encounterOptionsDialogue?: EncounterOptionsDialogue;
|
|
||||||
outro?: TextDisplay[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example MysteryEncounterDialogue object:
|
* Example MysteryEncounterDialogue object:
|
||||||
*
|
*
|
||||||
@ -72,3 +66,9 @@ export default class MysteryEncounterDialogue {
|
|||||||
}
|
}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
export default class MysteryEncounterDialogue {
|
||||||
|
intro?: TextDisplay[];
|
||||||
|
encounterOptionsDialogue?: EncounterOptionsDialogue;
|
||||||
|
outro?: TextDisplay[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -103,12 +103,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
||||||
super();
|
super();
|
||||||
if (timeOfDay instanceof Array) {
|
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay];
|
||||||
this.requiredTimeOfDay = timeOfDay;
|
|
||||||
} else {
|
|
||||||
this.requiredTimeOfDay = [];
|
|
||||||
this.requiredTimeOfDay.push(timeOfDay);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -130,12 +125,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
constructor(weather: WeatherType | WeatherType[]) {
|
constructor(weather: WeatherType | WeatherType[]) {
|
||||||
super();
|
super();
|
||||||
if (weather instanceof Array) {
|
this.requiredWeather = Array.isArray(weather) ? weather : [weather];
|
||||||
this.requiredWeather = weather;
|
|
||||||
} else {
|
|
||||||
this.requiredWeather = [];
|
|
||||||
this.requiredWeather.push(weather);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -185,12 +175,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
|||||||
requiredItems?: ModifierType[]; // TODO: not implemented
|
requiredItems?: ModifierType[]; // TODO: not implemented
|
||||||
constructor(item: ModifierType | ModifierType[]) {
|
constructor(item: ModifierType | ModifierType[]) {
|
||||||
super();
|
super();
|
||||||
if (item instanceof Array) {
|
this.requiredItems = Array.isArray(item) ? item : [item];
|
||||||
this.requiredItems = item;
|
|
||||||
} else {
|
|
||||||
this.requiredItems = [];
|
|
||||||
this.requiredItems.push(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -251,12 +236,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (species instanceof Array) {
|
this.requiredSpecies = Array.isArray(species) ? species : [species];
|
||||||
this.requiredSpecies = species;
|
|
||||||
} else {
|
|
||||||
this.requiredSpecies = [];
|
|
||||||
this.requiredSpecies.push(species);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -294,12 +274,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (nature instanceof Array) {
|
this.requiredNature = Array.isArray(nature) ? nature : [nature];
|
||||||
this.requiredNature = nature;
|
|
||||||
} else {
|
|
||||||
this.requiredNature = [];
|
|
||||||
this.requiredNature.push(nature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -338,12 +313,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeFainted = excludeFainted;
|
this.excludeFainted = excludeFainted;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (type instanceof Array) {
|
this.requiredType = Array.isArray(type) ? type : [type];
|
||||||
this.requiredType = type;
|
|
||||||
} else {
|
|
||||||
this.requiredType = [];
|
|
||||||
this.requiredType.push(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -388,12 +358,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (moves instanceof Array) {
|
this.requiredMoves = Array.isArray(moves) ? moves : [moves];
|
||||||
this.requiredMoves = moves;
|
|
||||||
} else {
|
|
||||||
this.requiredMoves = [];
|
|
||||||
this.requiredMoves.push(moves);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -437,12 +402,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (learnableMove instanceof Array) {
|
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove];
|
||||||
this.requiredMoves = learnableMove;
|
|
||||||
} else {
|
|
||||||
this.requiredMoves = [];
|
|
||||||
this.requiredMoves.push(learnableMove);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -482,12 +442,7 @@ export class EvolutionTargetSpeciesRequirement extends EncounterPokemonRequireme
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (evolutionTargetSpecies instanceof Array) {
|
this.requiredEvolutionTargetSpecies = Array.isArray(evolutionTargetSpecies) ? evolutionTargetSpecies : [evolutionTargetSpecies];
|
||||||
this.requiredEvolutionTargetSpecies = evolutionTargetSpecies;
|
|
||||||
} else {
|
|
||||||
this.requiredEvolutionTargetSpecies = [];
|
|
||||||
this.requiredEvolutionTargetSpecies.push(evolutionTargetSpecies);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -526,12 +481,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (abilities instanceof Array) {
|
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities];
|
||||||
this.requiredAbilities = abilities;
|
|
||||||
} else {
|
|
||||||
this.requiredAbilities = [];
|
|
||||||
this.requiredAbilities.push(abilities);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -571,12 +521,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (statusEffect instanceof Array) {
|
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect];
|
||||||
this.requiredStatusEffect = statusEffect;
|
|
||||||
} else {
|
|
||||||
this.requiredStatusEffect = [];
|
|
||||||
this.requiredStatusEffect.push(statusEffect);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -646,12 +591,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (formChangeItem instanceof Array) {
|
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem];
|
||||||
this.requiredFormChangeItem = formChangeItem;
|
|
||||||
} else {
|
|
||||||
this.requiredFormChangeItem = [];
|
|
||||||
this.requiredFormChangeItem.push(formChangeItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -703,12 +643,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (evolutionItems instanceof Array) {
|
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems];
|
||||||
this.requiredEvolutionItem = evolutionItems;
|
|
||||||
} else {
|
|
||||||
this.requiredEvolutionItem = [];
|
|
||||||
this.requiredEvolutionItem.push(evolutionItems);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -757,12 +692,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
if (heldItem instanceof Array) {
|
this.requiredHeldItemModifier = Array.isArray(heldItem) ? heldItem : [heldItem];
|
||||||
this.requiredHeldItemModifier = heldItem;
|
|
||||||
} else {
|
|
||||||
this.requiredHeldItemModifier = [];
|
|
||||||
this.requiredHeldItemModifier.push(heldItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "../../battle-scene";
|
import BattleScene from "../../battle-scene";
|
||||||
@ -18,6 +18,8 @@ import {
|
|||||||
StatusEffectRequirement,
|
StatusEffectRequirement,
|
||||||
WaveRangeRequirement
|
WaveRangeRequirement
|
||||||
} from "./mystery-encounter-requirements";
|
} from "./mystery-encounter-requirements";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { EncounterAnim } from "#app/data/battle-anims";
|
||||||
|
|
||||||
export enum MysteryEncounterVariant {
|
export enum MysteryEncounterVariant {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
@ -25,15 +27,28 @@ export enum MysteryEncounterVariant {
|
|||||||
WILD_BATTLE,
|
WILD_BATTLE,
|
||||||
BOSS_BATTLE,
|
BOSS_BATTLE,
|
||||||
NO_BATTLE,
|
NO_BATTLE,
|
||||||
SAFARI_BATTLE
|
/** For spawning new encounter queries instead of continuing to next wave */
|
||||||
|
CONTINUOUS_ENCOUNTER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum values are base spawn weights of each tier
|
||||||
|
*/
|
||||||
export enum MysteryEncounterTier {
|
export enum MysteryEncounterTier {
|
||||||
COMMON,
|
COMMON = 64,
|
||||||
GREAT,
|
GREAT = 40,
|
||||||
ULTRA,
|
ULTRA = 21,
|
||||||
ROGUE,
|
ROGUE = 3,
|
||||||
MASTER // Not currently used
|
MASTER = 0 // Not currently used
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StartOfBattleEffect {
|
||||||
|
sourcePokemon?: Pokemon;
|
||||||
|
sourceBattlerIndex?: BattlerIndex;
|
||||||
|
targets: BattlerIndex[];
|
||||||
|
move: PokemonMove;
|
||||||
|
ignorePp: boolean;
|
||||||
|
followUp?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default interface IMysteryEncounter {
|
export default interface IMysteryEncounter {
|
||||||
@ -47,13 +62,23 @@ export default interface IMysteryEncounter {
|
|||||||
* Optional params
|
* Optional params
|
||||||
*/
|
*/
|
||||||
encounterTier?: MysteryEncounterTier;
|
encounterTier?: MysteryEncounterTier;
|
||||||
|
encounterAnimations?: EncounterAnim[];
|
||||||
hideBattleIntroMessage?: boolean;
|
hideBattleIntroMessage?: boolean;
|
||||||
hideIntroVisuals?: boolean;
|
autoHideIntroVisuals?: boolean;
|
||||||
catchAllowed?: boolean;
|
catchAllowed?: boolean;
|
||||||
maxAllowedEncounters?: number;
|
maxAllowedEncounters?: number;
|
||||||
doEncounterExp?: (scene: BattleScene) => boolean;
|
|
||||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
/**
|
||||||
|
* Event callback functions
|
||||||
|
*/
|
||||||
|
/** Event when Encounter is first loaded, use it for data conditioning */
|
||||||
onInit?: (scene: BattleScene) => boolean;
|
onInit?: (scene: BattleScene) => boolean;
|
||||||
|
/** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */
|
||||||
|
onVisualsStart?: (scene: BattleScene) => boolean;
|
||||||
|
/** Will provide the player party EXP before rewards are displayed for that wave */
|
||||||
|
doEncounterExp?: (scene: BattleScene) => boolean;
|
||||||
|
/** Will provide the player a rewards shop for that wave */
|
||||||
|
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requirements
|
* Requirements
|
||||||
@ -112,20 +137,23 @@ export default interface IMysteryEncounter {
|
|||||||
* Will be set to false after a shop is shown (so can't reroll same rarity items for free)
|
* Will be set to false after a shop is shown (so can't reroll same rarity items for free)
|
||||||
*/
|
*/
|
||||||
lockEncounterRewardTiers?: boolean;
|
lockEncounterRewardTiers?: boolean;
|
||||||
|
/**
|
||||||
|
* Will be set automatically, indicates special moves in startOfBattleEffects are complete (so will not repeat)
|
||||||
|
*/
|
||||||
|
startOfBattleEffectsComplete?: boolean;
|
||||||
/**
|
/**
|
||||||
* Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases
|
* Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases
|
||||||
*/
|
*/
|
||||||
selectedOption?: MysteryEncounterOption;
|
selectedOption?: MysteryEncounterOption;
|
||||||
|
/**
|
||||||
|
* Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases
|
||||||
|
*/
|
||||||
|
startOfBattleEffects?: StartOfBattleEffect[];
|
||||||
/**
|
/**
|
||||||
* Can be set higher or lower based on the type of battle or exp gained for an option/encounter
|
* Can be set higher or lower based on the type of battle or exp gained for an option/encounter
|
||||||
* Defaults to 1
|
* Defaults to 1
|
||||||
*/
|
*/
|
||||||
expMultiplier?: number;
|
expMultiplier?: number;
|
||||||
/**
|
|
||||||
* Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave
|
|
||||||
* You should never need to modify this
|
|
||||||
*/
|
|
||||||
seedOffset?: any;
|
|
||||||
/**
|
/**
|
||||||
* Generic property to set any custom data required for the encounter
|
* Generic property to set any custom data required for the encounter
|
||||||
* Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase
|
* Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase
|
||||||
@ -139,6 +167,12 @@ export default interface IMysteryEncounter {
|
|||||||
* Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class
|
* Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class
|
||||||
*/
|
*/
|
||||||
export default class IMysteryEncounter implements IMysteryEncounter {
|
export default class IMysteryEncounter implements IMysteryEncounter {
|
||||||
|
/**
|
||||||
|
* Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave
|
||||||
|
* You should only need to interact via getter/update methods
|
||||||
|
*/
|
||||||
|
private seedOffset?: any;
|
||||||
|
|
||||||
constructor(encounter: IMysteryEncounter) {
|
constructor(encounter: IMysteryEncounter) {
|
||||||
if (!isNullOrUndefined(encounter)) {
|
if (!isNullOrUndefined(encounter)) {
|
||||||
Object.assign(this, encounter);
|
Object.assign(this, encounter);
|
||||||
@ -150,9 +184,11 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
|||||||
this.encounterVariant = MysteryEncounterVariant.DEFAULT;
|
this.encounterVariant = MysteryEncounterVariant.DEFAULT;
|
||||||
this.requirements = this.requirements ? this.requirements : [];
|
this.requirements = this.requirements ? this.requirements : [];
|
||||||
this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false;
|
this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false;
|
||||||
this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true;
|
this.autoHideIntroVisuals = !isNullOrUndefined(this.autoHideIntroVisuals) ? this.autoHideIntroVisuals : true;
|
||||||
|
this.startOfBattleEffects = this.startOfBattleEffects ?? [];
|
||||||
|
|
||||||
// Reset any dirty flags or encounter data
|
// Reset any dirty flags or encounter data
|
||||||
|
this.startOfBattleEffectsComplete = false;
|
||||||
this.lockEncounterRewardTiers = true;
|
this.lockEncounterRewardTiers = true;
|
||||||
this.dialogueTokens = {};
|
this.dialogueTokens = {};
|
||||||
this.enemyPartyConfigs = [];
|
this.enemyPartyConfigs = [];
|
||||||
@ -172,13 +208,6 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
|||||||
const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary
|
const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary
|
||||||
const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
||||||
|
|
||||||
// console.log("-------" + MysteryEncounterType[this.encounterType] + " Encounter Check -------");
|
|
||||||
// console.log(this);
|
|
||||||
// console.log( "sceneCheck: " + sceneReq);
|
|
||||||
// console.log( "primaryCheck: " + priReqs);
|
|
||||||
// console.log( "secondaryCheck: " + secReqs);
|
|
||||||
// console.log(MysteryEncounterTier[this.encounterTier]);
|
|
||||||
|
|
||||||
return sceneReq && secReqs && priReqs;
|
return sceneReq && secReqs && priReqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +372,27 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
|||||||
this.dialogueTokens[key] = value;
|
this.dialogueTokens[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an encounter uses {@link MysteryEncounterVariant.CONTINUOUS_ENCOUNTER},
|
||||||
|
* should rely on this value for seed offset instead of wave index.
|
||||||
|
*
|
||||||
|
* This offset is incremented for each new {@link MysteryEncounterPhase} that occurs,
|
||||||
|
* so multi-encounter RNG will be consistent on resets and not be affected by number of turns, move RNG, etc.
|
||||||
|
*/
|
||||||
|
getSeedOffset?() {
|
||||||
|
return this.seedOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintains seed offset for RNG consistency
|
||||||
|
* Increments if the same MysteryEncounter has multiple option select cycles
|
||||||
|
* @param scene
|
||||||
|
*/
|
||||||
|
updateSeedOffset?(scene: BattleScene) {
|
||||||
|
const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000;
|
||||||
|
this.seedOffset = currentOffset + 512;
|
||||||
|
}
|
||||||
|
|
||||||
private capitalizeFirstLetter?(str: string) {
|
private capitalizeFirstLetter?(str: string) {
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
}
|
}
|
||||||
@ -355,14 +405,18 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
|
|
||||||
dialogue?: MysteryEncounterDialogue;
|
dialogue?: MysteryEncounterDialogue;
|
||||||
encounterTier?: MysteryEncounterTier;
|
encounterTier?: MysteryEncounterTier;
|
||||||
|
encounterAnimations?: EncounterAnim[];
|
||||||
requirements?: EncounterSceneRequirement[] = [];
|
requirements?: EncounterSceneRequirement[] = [];
|
||||||
primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
|
primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
|
||||||
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
|
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
|
||||||
excludePrimaryFromSupportRequirements?: boolean;
|
excludePrimaryFromSupportRequirements?: boolean;
|
||||||
dialogueTokens?: Record<string, string>;
|
dialogueTokens?: Record<string, string>;
|
||||||
|
|
||||||
doEncounterExp?: (scene: BattleScene) => boolean;
|
doEncounterExp?: (scene: BattleScene) => boolean;
|
||||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||||
onInit?: (scene: BattleScene) => boolean;
|
onInit?: (scene: BattleScene) => boolean;
|
||||||
|
onVisualsStart?: (scene: BattleScene) => boolean;
|
||||||
|
|
||||||
hideBattleIntroMessage?: boolean;
|
hideBattleIntroMessage?: boolean;
|
||||||
hideIntroVisuals?: boolean;
|
hideIntroVisuals?: boolean;
|
||||||
enemyPartyConfigs?: EnemyPartyConfig[] = [];
|
enemyPartyConfigs?: EnemyPartyConfig[] = [];
|
||||||
@ -410,7 +464,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
* @param callback - {@linkcode OptionPhaseCallback}
|
* @param callback - {@linkcode OptionPhaseCallback}
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback) {
|
withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick<IMysteryEncounter, "options"> {
|
||||||
return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build());
|
return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +507,18 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
return Object.assign(this, { encounterTier: encounterTier });
|
return Object.assign(this, { encounterTier: encounterTier });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines any EncounterAnim animations that are intended to be used during the encounter
|
||||||
|
* EncounterAnims can be played at any point during an encounter or callback
|
||||||
|
* They just need to be specified here so that resources are loaded on encounter init
|
||||||
|
* @param encounterAnimations
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withAnimations(...encounterAnimations: EncounterAnim[]): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
|
||||||
|
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations];
|
||||||
|
return Object.assign(this, { encounterAnimations: animations });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the maximum number of times that an encounter can spawn in a given Classic run
|
* Sets the maximum number of times that an encounter can spawn in a given Classic run
|
||||||
* @param maxAllowedEncounters
|
* @param maxAllowedEncounters
|
||||||
@ -582,6 +648,16 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
return Object.assign(this, { onInit: onInit });
|
return Object.assign(this, { onInit: onInit });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in
|
||||||
|
*
|
||||||
|
* @param onVisualsStart - synchronous callback function to perform as soon as the enemy field finishes sliding in
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
|
||||||
|
return Object.assign(this, { onVisualsStart: onVisualsStart });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines any enemies to use for a battle from the mystery encounter
|
* Defines any enemies to use for a battle from the mystery encounter
|
||||||
* @param enemyPartyConfig
|
* @param enemyPartyConfig
|
||||||
@ -611,11 +687,11 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param hideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter
|
* @param autoHideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withHideIntroVisuals(hideIntroVisuals: boolean): this & Required<Pick<IMysteryEncounter, "hideIntroVisuals">> {
|
withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required<Pick<IMysteryEncounter, "autoHideIntroVisuals">> {
|
||||||
return Object.assign(this, { hideIntroVisuals: hideIntroVisuals });
|
return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,10 +8,11 @@ import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter";
|
|||||||
import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter";
|
import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter";
|
||||||
import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter";
|
import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter";
|
||||||
import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter";
|
import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter";
|
||||||
import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter";
|
import { SlumberingSnorlaxEncounter } from "./encounters/slumbering-snorlax-encounter";
|
||||||
import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
|
import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
|
||||||
import IMysteryEncounter from "./mystery-encounter";
|
import IMysteryEncounter from "./mystery-encounter";
|
||||||
import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter";
|
import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter";
|
||||||
|
import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter";
|
||||||
|
|
||||||
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
||||||
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
||||||
@ -157,14 +158,15 @@ const anyBiomeEncounters: MysteryEncounterType[] = [
|
|||||||
*/
|
*/
|
||||||
export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
[Biome.TOWN, []],
|
[Biome.TOWN, []],
|
||||||
[Biome.PLAINS, []],
|
[Biome.PLAINS, [
|
||||||
|
MysteryEncounterType.SLUMBERING_SNORLAX
|
||||||
|
]],
|
||||||
[Biome.GRASS, [
|
[Biome.GRASS, [
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX,
|
MysteryEncounterType.SLUMBERING_SNORLAX,
|
||||||
]],
|
]],
|
||||||
[Biome.TALL_GRASS, []],
|
[Biome.TALL_GRASS, []],
|
||||||
[Biome.METROPOLIS, []],
|
[Biome.METROPOLIS, []],
|
||||||
[Biome.FOREST, [
|
[Biome.FOREST, [
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX,
|
|
||||||
MysteryEncounterType.SAFARI_ZONE
|
MysteryEncounterType.SAFARI_ZONE
|
||||||
]],
|
]],
|
||||||
|
|
||||||
@ -177,18 +179,16 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
|||||||
[Biome.BEACH, []],
|
[Biome.BEACH, []],
|
||||||
[Biome.LAKE, []],
|
[Biome.LAKE, []],
|
||||||
[Biome.SEABED, []],
|
[Biome.SEABED, []],
|
||||||
[Biome.MOUNTAIN, [
|
[Biome.MOUNTAIN, []],
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX
|
|
||||||
]],
|
|
||||||
[Biome.BADLANDS, []],
|
[Biome.BADLANDS, []],
|
||||||
[Biome.CAVE, [
|
[Biome.CAVE, []],
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX
|
|
||||||
]],
|
|
||||||
[Biome.DESERT, []],
|
[Biome.DESERT, []],
|
||||||
[Biome.ICE_CAVE, []],
|
[Biome.ICE_CAVE, []],
|
||||||
[Biome.MEADOW, []],
|
[Biome.MEADOW, []],
|
||||||
[Biome.POWER_PLANT, []],
|
[Biome.POWER_PLANT, []],
|
||||||
[Biome.VOLCANO, []],
|
[Biome.VOLCANO, [
|
||||||
|
MysteryEncounterType.FIERY_FALLOUT
|
||||||
|
]],
|
||||||
[Biome.GRAVEYARD, []],
|
[Biome.GRAVEYARD, []],
|
||||||
[Biome.DOJO, []],
|
[Biome.DOJO, []],
|
||||||
[Biome.FACTORY, []],
|
[Biome.FACTORY, []],
|
||||||
@ -214,12 +214,13 @@ export function initMysteryEncounters() {
|
|||||||
allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter;
|
allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightEncounter;
|
allMysteryEncounters[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionEncounter;
|
allMysteryEncounters[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter;
|
allMysteryEncounters[MysteryEncounterType.SLUMBERING_SNORLAX] = SlumberingSnorlaxEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter;
|
allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter;
|
allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter;
|
allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.SAFARI_ZONE] = SafariZoneEncounter;
|
allMysteryEncounters[MysteryEncounterType.SAFARI_ZONE] = SafariZoneEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter;
|
allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter;
|
||||||
|
allMysteryEncounters[MysteryEncounterType.FIERY_FALLOUT] = FieryFalloutEncounter;
|
||||||
|
|
||||||
// Add extreme encounters to biome map
|
// Add extreme encounters to biome map
|
||||||
extremeBiomeEncounters.forEach(encounter => {
|
extremeBiomeEncounters.forEach(encounter => {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { BattleType } from "#app/battle";
|
import { BattlerIndex, BattleType } from "#app/battle";
|
||||||
import { biomeLinks } from "#app/data/biomes";
|
import { biomeLinks } from "#app/data/biomes";
|
||||||
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import * as Overrides from "#app/overrides";
|
import * as Overrides from "#app/overrides";
|
||||||
import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases";
|
import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, MovePhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases";
|
||||||
import { MysteryEncounterBattlePhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase";
|
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
@ -26,29 +26,63 @@ import PokemonSpecies from "../../pokemon-species";
|
|||||||
import { Status, StatusEffect } from "../../status-effect";
|
import { Status, StatusEffect } from "../../status-effect";
|
||||||
import { TrainerConfig, trainerConfigs, TrainerSlot } from "../../trainer-config";
|
import { TrainerConfig, trainerConfigs, TrainerSlot } from "../../trainer-config";
|
||||||
import { MysteryEncounterVariant } from "../mystery-encounter";
|
import { MysteryEncounterVariant } from "../mystery-encounter";
|
||||||
|
import { Gender } from "#app/data/gender";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
||||||
|
|
||||||
export class EnemyPokemonConfig {
|
/**
|
||||||
|
* Animates exclamation sprite over trainer's head at start of encounter
|
||||||
|
* @param scene
|
||||||
|
*/
|
||||||
|
export function doTrainerExclamation(scene: BattleScene) {
|
||||||
|
const exclamationSprite = scene.addFieldSprite(0, 0, "exclaim");
|
||||||
|
exclamationSprite.setName("exclamation");
|
||||||
|
scene.field.add(exclamationSprite);
|
||||||
|
scene.field.moveTo(exclamationSprite, scene.field.getAll().length - 1);
|
||||||
|
exclamationSprite.setVisible(true);
|
||||||
|
exclamationSprite.setPosition(110, 68);
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: exclamationSprite,
|
||||||
|
y: "-=25",
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
duration: 300,
|
||||||
|
yoyo: true,
|
||||||
|
onComplete: () => {
|
||||||
|
scene.time.delayedCall(800, () => {
|
||||||
|
scene.field.remove(exclamationSprite, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.playSound("GEN8- Exclaim.wav", { volume: 0.8 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnemyPokemonConfig {
|
||||||
species: PokemonSpecies;
|
species: PokemonSpecies;
|
||||||
isBoss: boolean = false;
|
isBoss: boolean;
|
||||||
bossSegments?: number;
|
bossSegments?: number;
|
||||||
bossSegmentModifier?: number; // Additive to the determined segment number
|
bossSegmentModifier?: number; // Additive to the determined segment number
|
||||||
formIndex?: number;
|
formIndex?: number;
|
||||||
level?: number;
|
level?: number;
|
||||||
modifierTypes?: PokemonHeldItemModifierType[];
|
gender?: Gender;
|
||||||
dataSource?: PokemonData;
|
|
||||||
tags?: BattlerTagType[];
|
|
||||||
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
|
||||||
status?: StatusEffect;
|
|
||||||
passive?: boolean;
|
passive?: boolean;
|
||||||
|
moveSet?: Moves[];
|
||||||
|
/** Can set just the status, or pass a timer on the status turns */
|
||||||
|
status?: StatusEffect | [StatusEffect, number];
|
||||||
|
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
||||||
|
modifierTypes?: PokemonHeldItemModifierType[];
|
||||||
|
tags?: BattlerTagType[];
|
||||||
|
dataSource?: PokemonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EnemyPartyConfig {
|
export interface EnemyPartyConfig {
|
||||||
levelAdditiveMultiplier?: number = 0; // Formula for enemy: level += waveIndex / 10 * levelAdditive
|
levelAdditiveMultiplier?: number; // Formula for enemy: level += waveIndex / 10 * levelAdditive
|
||||||
doubleBattle?: boolean = false;
|
doubleBattle?: boolean;
|
||||||
trainerType?: TrainerType; // Generates trainer battle solely off trainer type
|
trainerType?: TrainerType; // Generates trainer battle solely off trainer type
|
||||||
trainerConfig?: TrainerConfig; // More customizable option for configuring trainer battle
|
trainerConfig?: TrainerConfig; // More customizable option for configuring trainer battle
|
||||||
pokemonConfigs?: EnemyPokemonConfig[];
|
pokemonConfigs?: EnemyPokemonConfig[];
|
||||||
female?: boolean; // True for female trainer, false for male
|
female?: boolean; // True for female trainer, false for male
|
||||||
|
disableSwitch?: boolean; // True will prevent player from switching
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,24 +211,45 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set Status
|
// Set Status
|
||||||
if (partyConfig.pokemonConfigs[e].status) {
|
const statusEffects = partyConfig.pokemonConfigs[e].status;
|
||||||
|
if (statusEffects) {
|
||||||
// Default to cureturn 3 for sleep
|
// Default to cureturn 3 for sleep
|
||||||
const cureTurn = partyConfig.pokemonConfigs[e].status === StatusEffect.SLEEP ? 3 : null;
|
const status = Array.isArray(statusEffects) ? statusEffects[0] : statusEffects;
|
||||||
enemyPokemon.status = new Status(partyConfig.pokemonConfigs[e].status, 0, cureTurn);
|
const cureTurn = Array.isArray(statusEffects) ? statusEffects[1] : statusEffects === StatusEffect.SLEEP ? 3 : null;
|
||||||
|
enemyPokemon.status = new Status(status, 0, cureTurn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set summon data fields
|
||||||
|
|
||||||
|
// Set gender
|
||||||
|
if (!isNullOrUndefined(config.gender)) {
|
||||||
|
enemyPokemon.gender = config.gender;
|
||||||
|
enemyPokemon.summonData.gender = config.gender;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set moves
|
||||||
|
if (config?.moveSet?.length > 0) {
|
||||||
|
const moves = config.moveSet.map(m => new PokemonMove(m));
|
||||||
|
enemyPokemon.moveset = moves;
|
||||||
|
enemyPokemon.summonData.moveset = moves;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set tags
|
// Set tags
|
||||||
if (config.tags?.length > 0) {
|
if (config.tags?.length > 0) {
|
||||||
const tags = config.tags;
|
const tags = config.tags;
|
||||||
tags.forEach(tag => enemyPokemon.addTag(tag));
|
tags.forEach(tag => enemyPokemon.addTag(tag));
|
||||||
// mysteryEncounterBattleEffects can be used IFF MYSTERY_ENCOUNTER_POST_SUMMON tag is applied
|
|
||||||
enemyPokemon.summonData.mysteryEncounterBattleEffects = config.mysteryEncounterBattleEffects;
|
|
||||||
|
|
||||||
// Requires re-priming summon data so that tags are not cleared on SummonPhase
|
|
||||||
enemyPokemon.primeSummonData(enemyPokemon.summonData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mysteryEncounterBattleEffects will only be used IFF MYSTERY_ENCOUNTER_POST_SUMMON tag is applied
|
||||||
|
if (config.mysteryEncounterBattleEffects) {
|
||||||
|
enemyPokemon.mysteryEncounterBattleEffects = config.mysteryEncounterBattleEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires re-priming summon data to update everything properly
|
||||||
|
enemyPokemon.primeSummonData(enemyPokemon.summonData);
|
||||||
|
|
||||||
enemyPokemon.initBattleInfo();
|
enemyPokemon.initBattleInfo();
|
||||||
|
enemyPokemon.getBattleInfo().initInfo(enemyPokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadEnemyAssets.push(enemyPokemon.loadAssets());
|
loadEnemyAssets.push(enemyPokemon.loadAssets());
|
||||||
@ -202,7 +257,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||||||
console.log(enemyPokemon.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
|
console.log(enemyPokemon.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
|
||||||
});
|
});
|
||||||
|
|
||||||
scene.pushPhase(new MysteryEncounterBattlePhase(scene));
|
scene.pushPhase(new MysteryEncounterBattlePhase(scene, partyConfig.disableSwitch));
|
||||||
|
|
||||||
await Promise.all(loadEnemyAssets);
|
await Promise.all(loadEnemyAssets);
|
||||||
battle.enemyParty.forEach((enemyPokemon_2, e_1) => {
|
battle.enemyParty.forEach((enemyPokemon_2, e_1) => {
|
||||||
@ -222,6 +277,20 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load special move animations/sfx for hard-coded encounter-specific moves that a pokemon uses at the start of an encounter
|
||||||
|
* See: [startOfBattleEffects](IMysteryEncounter.startOfBattleEffects) for more details
|
||||||
|
*
|
||||||
|
* This promise does not need to be awaited on if called in an encounter onInit (will just load lazily)
|
||||||
|
* @param scene
|
||||||
|
* @param moves
|
||||||
|
*/
|
||||||
|
export function initCustomMovesForEncounter(scene: BattleScene, moves: Moves | Moves[]) {
|
||||||
|
moves = Array.isArray(moves) ? moves : [moves];
|
||||||
|
return Promise.all(moves.map(move => initMoveAnim(scene, move)))
|
||||||
|
.then(() => loadMoveAnimAssets(scene, moves));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will update player money, and animate change (sound optional)
|
* Will update player money, and animate change (sound optional)
|
||||||
* @param scene - Battle Scene
|
* @param scene - Battle Scene
|
||||||
@ -353,10 +422,10 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
|
|||||||
* Can have shop displayed or skipped
|
* Can have shop displayed or skipped
|
||||||
* @param scene - Battle Scene
|
* @param scene - Battle Scene
|
||||||
* @param customShopRewards - adds a shop phase with the specified rewards / reward tiers
|
* @param customShopRewards - adds a shop phase with the specified rewards / reward tiers
|
||||||
* @param nonShopRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop)
|
* @param nonShopPlayerItemRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop)
|
||||||
* @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase)
|
* @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase)
|
||||||
*/
|
*/
|
||||||
export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) {
|
export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopPlayerItemRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) {
|
||||||
scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => {
|
scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => {
|
||||||
if (preRewardsCallback) {
|
if (preRewardsCallback) {
|
||||||
preRewardsCallback();
|
preRewardsCallback();
|
||||||
@ -368,8 +437,8 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust
|
|||||||
scene.tryRemovePhase(p => p instanceof SelectModifierPhase);
|
scene.tryRemovePhase(p => p instanceof SelectModifierPhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonShopRewards?.length > 0) {
|
if (nonShopPlayerItemRewards?.length > 0) {
|
||||||
nonShopRewards.forEach((reward) => {
|
nonShopPlayerItemRewards.forEach((reward) => {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, reward));
|
scene.unshiftPhase(new ModifierRewardPhase(scene, reward));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -411,7 +480,8 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int
|
|||||||
const nonFaintedPartyMembers = party.filter(p => p.hp);
|
const nonFaintedPartyMembers = party.filter(p => p.hp);
|
||||||
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel());
|
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel());
|
||||||
const partyMemberExp = [];
|
const partyMemberExp = [];
|
||||||
let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1);
|
// EXP value calculation is based off Pokemon.getExpValue
|
||||||
|
let expValue = Math.floor(baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1);
|
||||||
|
|
||||||
if (participantIds?.length > 0) {
|
if (participantIds?.length > 0) {
|
||||||
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) {
|
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) {
|
||||||
@ -530,8 +600,10 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.SAFARI_BATTLE) {
|
// If in repeated encounter variant, do nothing
|
||||||
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
// Variant must eventually be swapped in order to handle "true" end of the encounter
|
||||||
|
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.CONTINUOUS_ENCOUNTER) {
|
||||||
|
return;
|
||||||
} else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) {
|
} else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) {
|
||||||
scene.pushPhase(new EggLapsePhase(scene));
|
scene.pushPhase(new EggLapsePhase(scene));
|
||||||
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
||||||
@ -547,23 +619,40 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hideMysteryEncounterIntroVisuals(scene: BattleScene): Promise<boolean> {
|
/**
|
||||||
|
*
|
||||||
|
* @param scene
|
||||||
|
* @param hide - If true, performs ease out and hide visuals. If false, eases in visuals. Defaults to true
|
||||||
|
* @param destroy - If true, will destroy visuals ONLY ON HIDE TRANSITION. Does nothing on show. Defaults to true
|
||||||
|
* @param duration
|
||||||
|
*/
|
||||||
|
export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: boolean = true, destroy: boolean = true, duration: number = 750): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const introVisuals = scene.currentBattle.mysteryEncounter.introVisuals;
|
const introVisuals = scene.currentBattle.mysteryEncounter.introVisuals;
|
||||||
if (introVisuals) {
|
if (introVisuals) {
|
||||||
// Hide
|
if (!hide) {
|
||||||
|
// Make sure visuals are in proper state for showing
|
||||||
|
introVisuals.setVisible(true);
|
||||||
|
introVisuals.x += 16;
|
||||||
|
introVisuals.y -= 16;
|
||||||
|
introVisuals.alpha = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transition
|
||||||
scene.tweens.add({
|
scene.tweens.add({
|
||||||
targets: introVisuals,
|
targets: introVisuals,
|
||||||
x: "+=16",
|
x: `${hide? "+" : "-"}=16`,
|
||||||
y: "-=16",
|
y: `${hide ? "-" : "+"}=16`,
|
||||||
alpha: 0,
|
alpha: hide ? 0 : 1,
|
||||||
ease: "Sine.easeInOut",
|
ease: "Sine.easeInOut",
|
||||||
duration: 750,
|
duration,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
scene.field.remove(introVisuals);
|
if (hide && destroy) {
|
||||||
introVisuals.setVisible(false);
|
scene.field.remove(introVisuals);
|
||||||
introVisuals.destroy();
|
introVisuals.setVisible(false);
|
||||||
scene.currentBattle.mysteryEncounter.introVisuals = null;
|
introVisuals.destroy();
|
||||||
|
scene.currentBattle.mysteryEncounter.introVisuals = null;
|
||||||
|
}
|
||||||
resolve(true);
|
resolve(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -573,6 +662,44 @@ export function hideMysteryEncounterIntroVisuals(scene: BattleScene): Promise<bo
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will queue moves for any pokemon to use before the first CommandPhase of a battle
|
||||||
|
* Mostly useful for allowing MysteryEncounter enemies to "cheat" and use moves before the first turn
|
||||||
|
* @param scene
|
||||||
|
*/
|
||||||
|
export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle?.mysteryEncounter;
|
||||||
|
if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter.encounterVariant !== MysteryEncounterVariant.NO_BATTLE && !encounter.startOfBattleEffectsComplete) {
|
||||||
|
const effects = encounter.startOfBattleEffects;
|
||||||
|
effects.forEach(effect => {
|
||||||
|
let source;
|
||||||
|
if (effect.sourcePokemon) {
|
||||||
|
source = effect.sourcePokemon;
|
||||||
|
} else if (!isNullOrUndefined(effect.sourceBattlerIndex)) {
|
||||||
|
if (effect.sourceBattlerIndex === BattlerIndex.ATTACKER) {
|
||||||
|
source = scene.getEnemyField()[0];
|
||||||
|
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY) {
|
||||||
|
source = scene.getEnemyField()[0];
|
||||||
|
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY_2) {
|
||||||
|
source = scene.getEnemyField()[1];
|
||||||
|
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER) {
|
||||||
|
source = scene.getPlayerField()[0];
|
||||||
|
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER_2) {
|
||||||
|
source = scene.getPlayerField()[1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
source = scene.getEnemyField()[0];
|
||||||
|
}
|
||||||
|
scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.ignorePp));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pseudo turn end phase to reset flinch states, Endure, etc.
|
||||||
|
scene.pushPhase(new MysteryEncounterBattleStartCleanupPhase(scene));
|
||||||
|
|
||||||
|
encounter.startOfBattleEffectsComplete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: remove once encounter spawn rate is finalized
|
* TODO: remove once encounter spawn rate is finalized
|
||||||
* Just a helper function to calculate aggregate stats for MEs in a Classic run
|
* Just a helper function to calculate aggregate stats for MEs in a Classic run
|
||||||
|
@ -98,8 +98,8 @@ export function getLowestLevelPlayerPokemon(scene: BattleScene, unfainted: boole
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[]): Species {
|
export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[]): Species {
|
||||||
let min = starterTiers instanceof Array ? starterTiers[0] : starterTiers;
|
let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers;
|
||||||
let max = starterTiers instanceof Array ? starterTiers[1] : starterTiers;
|
let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers;
|
||||||
|
|
||||||
let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters)
|
let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters)
|
||||||
.map(s => [parseInt(s) as Species, speciesStarters[s] as number])
|
.map(s => [parseInt(s) as Species, speciesStarters[s] as number])
|
||||||
@ -167,7 +167,6 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
|
|||||||
pokeball.setOrigin(0.5, 0.625);
|
pokeball.setOrigin(0.5, 0.625);
|
||||||
scene.field.add(pokeball);
|
scene.field.add(pokeball);
|
||||||
|
|
||||||
scene.playSound("pb_throw");
|
|
||||||
scene.time.delayedCall(300, () => {
|
scene.time.delayedCall(300, () => {
|
||||||
scene.field.moveBelow(pokeball as Phaser.GameObjects.GameObject, pokemon);
|
scene.field.moveBelow(pokeball as Phaser.GameObjects.GameObject, pokemon);
|
||||||
});
|
});
|
||||||
@ -175,6 +174,8 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||||
scene.time.delayedCall(512, () => {
|
scene.time.delayedCall(512, () => {
|
||||||
|
scene.playSound("pb_throw");
|
||||||
|
|
||||||
// Trainer throw frames
|
// Trainer throw frames
|
||||||
scene.trainer.setFrame("2");
|
scene.trainer.setFrame("2");
|
||||||
scene.time.delayedCall(256, () => {
|
scene.time.delayedCall(256, () => {
|
||||||
@ -404,8 +405,9 @@ function removePb(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise<void> {
|
export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
|
scene.playSound("flee");
|
||||||
// Ease pokemon out
|
// Ease pokemon out
|
||||||
scene.tweens.add({
|
scene.tweens.add({
|
||||||
targets: pokemon,
|
targets: pokemon,
|
||||||
|
@ -3,11 +3,12 @@ export enum MysteryEncounterType {
|
|||||||
MYSTERIOUS_CHEST,
|
MYSTERIOUS_CHEST,
|
||||||
DARK_DEAL,
|
DARK_DEAL,
|
||||||
FIGHT_OR_FLIGHT,
|
FIGHT_OR_FLIGHT,
|
||||||
SLEEPING_SNORLAX,
|
SLUMBERING_SNORLAX,
|
||||||
TRAINING_SESSION,
|
TRAINING_SESSION,
|
||||||
DEPARTMENT_STORE_SALE,
|
DEPARTMENT_STORE_SALE,
|
||||||
SHADY_VITAMIN_DEALER,
|
SHADY_VITAMIN_DEALER,
|
||||||
FIELD_TRIP,
|
FIELD_TRIP,
|
||||||
SAFARI_ZONE,
|
SAFARI_ZONE,
|
||||||
LOST_AT_SEA //might be generalized later on
|
LOST_AT_SEA, //might be generalized later on
|
||||||
|
FIERY_FALLOUT
|
||||||
}
|
}
|
||||||
|
@ -317,7 +317,7 @@ export class Arena {
|
|||||||
this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft));
|
this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft));
|
||||||
|
|
||||||
if (this.weather) {
|
if (this.weather) {
|
||||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1), true));
|
||||||
this.scene.queueMessage(getWeatherStartMessage(weather));
|
this.scene.queueMessage(getWeatherStartMessage(weather));
|
||||||
} else {
|
} else {
|
||||||
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
|
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
|
||||||
|
@ -32,6 +32,8 @@ export class MysteryEncounterSpriteConfig {
|
|||||||
disableAnimation?: boolean = false;
|
disableAnimation?: boolean = false;
|
||||||
/** Repeat the animation. Defaults to `false` */
|
/** Repeat the animation. Defaults to `false` */
|
||||||
repeat?: boolean = false;
|
repeat?: boolean = false;
|
||||||
|
/** Hidden at start of encounter. Defaults to `false` */
|
||||||
|
hidden?: boolean = false;
|
||||||
/** Tint color. `0` - `1`. Higher means darker tint. */
|
/** Tint color. `0` - `1`. Higher means darker tint. */
|
||||||
tint?: number;
|
tint?: number;
|
||||||
/** X offset */
|
/** X offset */
|
||||||
@ -105,6 +107,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||||||
tintSprite = getItemSprite(spriteKey);
|
tintSprite = getItemSprite(spriteKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sprite.setVisible(!config.hidden);
|
||||||
tintSprite.setVisible(false);
|
tintSprite.setVisible(false);
|
||||||
|
|
||||||
if (scale) {
|
if (scale) {
|
||||||
@ -345,6 +348,17 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||||||
this.untint(tintSprite, duration, ease);
|
this.untint(tintSprite, duration, ease);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets container and all child sprites to visible
|
||||||
|
* @param value - true for visible, false for hidden
|
||||||
|
*/
|
||||||
|
setVisible(value: boolean): this {
|
||||||
|
this.getSprites().forEach(sprite => {
|
||||||
|
sprite.setVisible(value);
|
||||||
|
});
|
||||||
|
return super.setVisible(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default interface MysteryEncounterIntroVisuals {
|
export default interface MysteryEncounterIntroVisuals {
|
||||||
|
@ -101,6 +101,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
public battleSummonData: PokemonBattleSummonData;
|
public battleSummonData: PokemonBattleSummonData;
|
||||||
public turnData: PokemonTurnData;
|
public turnData: PokemonTurnData;
|
||||||
|
|
||||||
|
/** Used by Mystery Encounters to execute pokemon-specific logic (such as stat boosts) at start of battle */
|
||||||
|
public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null;
|
||||||
|
|
||||||
public fieldPosition: FieldPosition;
|
public fieldPosition: FieldPosition;
|
||||||
|
|
||||||
public maskEnabled: boolean;
|
public maskEnabled: boolean;
|
||||||
@ -3931,7 +3934,6 @@ export class PokemonSummonData {
|
|||||||
public moveset: PokemonMove[];
|
public moveset: PokemonMove[];
|
||||||
// If not initialized this value will not be populated from save data.
|
// If not initialized this value will not be populated from save data.
|
||||||
public types: Type[] = null;
|
public types: Type[] = null;
|
||||||
public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PokemonBattleData {
|
export class PokemonBattleData {
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
import { lostAtSea } from "./mystery-encounters/lost-at-sea";
|
import { lostAtSeaDialogue } from "./mystery-encounters/lost-at-sea-dialogue";
|
||||||
|
import { mysteriousChestDialogue } from "#app/locales/en/mystery-encounters/mysterious-chest-dialogue";
|
||||||
|
import { mysteriousChallengersDialogue } from "#app/locales/en/mystery-encounters/mysterious-challengers-dialogue";
|
||||||
|
import { darkDealDialogue } from "#app/locales/en/mystery-encounters/dark-deal-dialogue";
|
||||||
|
import { departmentStoreSaleDialogue } from "#app/locales/en/mystery-encounters/department-store-sale-dialogue";
|
||||||
|
import { fieldTripDialogue } from "#app/locales/en/mystery-encounters/field-trip-dialogue";
|
||||||
|
import { fieryFalloutDialogue } from "#app/locales/en/mystery-encounters/fiery-fallout-dialogue";
|
||||||
|
import { fightOrFlightDialogue } from "#app/locales/en/mystery-encounters/fight-or-flight-dialogue";
|
||||||
|
import { safariZoneDialogue } from "#app/locales/en/mystery-encounters/safari-zone-dialogue";
|
||||||
|
import { shadyVitaminDealerDialogue } from "#app/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue";
|
||||||
|
import { slumberingSnorlaxDialogue } from "#app/locales/en/mystery-encounters/slumbering-snorlax-dialogue";
|
||||||
|
import { trainingSessionDialogue } from "#app/locales/en/mystery-encounters/training-session-dialogue";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patterns that can be used:
|
* Patterns that can be used:
|
||||||
@ -21,224 +32,16 @@ export const mysteryEncounter = {
|
|||||||
"paid_money": "You paid ₽{{amount, number}}.",
|
"paid_money": "You paid ₽{{amount, number}}.",
|
||||||
"receive_money": "You received ₽{{amount, number}}!",
|
"receive_money": "You received ₽{{amount, number}}!",
|
||||||
|
|
||||||
// Mystery Encounters -- Common Tier
|
mysteriousChallengers: mysteriousChallengersDialogue,
|
||||||
|
mysteriousChest: mysteriousChestDialogue,
|
||||||
"mysterious_chest_intro_message": "You found...@d{32} a chest?",
|
darkDeal: darkDealDialogue,
|
||||||
"mysterious_chest_title": "The Mysterious Chest",
|
fightOrFlight: fightOrFlightDialogue,
|
||||||
"mysterious_chest_description": "A beautifully ornamented chest stands on the ground. There must be something good inside... right?",
|
slumberingSnorlax: slumberingSnorlaxDialogue,
|
||||||
"mysterious_chest_query": "Will you open it?",
|
trainingSession: trainingSessionDialogue,
|
||||||
"mysterious_chest_option_1_label": "Open it",
|
departmentStoreSale: departmentStoreSaleDialogue,
|
||||||
"mysterious_chest_option_1_tooltip": "@[SUMMARY_BLUE]{(35%) Something terrible}\n@[SUMMARY_GREEN]{(40%) Okay Rewards}\n@[SUMMARY_GREEN]{(20%) Good Rewards}\n@[SUMMARY_GREEN]{(4%) Great Rewards}\n@[SUMMARY_GREEN]{(1%) Amazing Rewards}",
|
shadyVitaminDealer: shadyVitaminDealerDialogue,
|
||||||
"mysterious_chest_option_2_label": "It's too risky, leave",
|
fieldTrip: fieldTripDialogue,
|
||||||
"mysterious_chest_option_2_tooltip": "(-) No Rewards",
|
safariZone: safariZoneDialogue,
|
||||||
"mysterious_chest_option_1_selected_message": "You open the chest to find...",
|
lostAtSea: lostAtSeaDialogue,
|
||||||
"mysterious_chest_option_2_selected_message": "You hurry along your way,\nwith a slight feeling of regret.",
|
fieryFallout: fieryFalloutDialogue,
|
||||||
"mysterious_chest_option_1_normal_result": "Just some normal tools and items.",
|
|
||||||
"mysterious_chest_option_1_good_result": "Some pretty nice tools and items.",
|
|
||||||
"mysterious_chest_option_1_great_result": "A couple great tools and items!",
|
|
||||||
"mysterious_chest_option_1_amazing_result": "Whoa! An amazing item!",
|
|
||||||
"mysterious_chest_option_1_bad_result": `Oh no!@d{32}\nThe chest was trapped!
|
|
||||||
$Your {{pokeName}} jumps in front of you\nbut is KOed in the process.`,
|
|
||||||
|
|
||||||
"fight_or_flight_intro_message": "Something shiny is sparkling\non the ground near that Pokémon!",
|
|
||||||
"fight_or_flight_title": "Fight or Flight",
|
|
||||||
"fight_or_flight_description": "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but this Pokémon looks strong. You could also try to sneak around, though the Pokémon might catch you.",
|
|
||||||
"fight_or_flight_query": "What will you do?",
|
|
||||||
"fight_or_flight_option_1_label": "Battle the Pokémon",
|
|
||||||
"fight_or_flight_option_1_tooltip": "(-) Hard Battle\n(+) New Item",
|
|
||||||
"fight_or_flight_option_2_label": "Steal the item",
|
|
||||||
"fight_or_flight_option_2_tooltip": "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}",
|
|
||||||
"fight_or_flight_option_2_steal_tooltip": "(+) {{option2PrimaryName}} uses {{option2PrimaryMove}}",
|
|
||||||
"fight_or_flight_option_3_label": "Leave",
|
|
||||||
"fight_or_flight_option_3_tooltip": "(-) No Rewards",
|
|
||||||
"fight_or_flight_option_1_selected_message": "You approach the\nPokémon without fear.",
|
|
||||||
"fight_or_flight_option_2_good_result": `.@d{32}.@d{32}.@d{32}
|
|
||||||
$You manage to sneak your way\npast and grab the item!`,
|
|
||||||
"fight_or_flight_option_2_steal_result": `.@d{32}.@d{32}.@d{32}
|
|
||||||
$Your {{option2PrimaryName}} helps you out and uses {{option2PrimaryMove}}!
|
|
||||||
$ You nabbed the item!`,
|
|
||||||
"fight_or_flight_option_2_bad_result": `.@d{32}.@d{32}.@d{32}
|
|
||||||
$The Pokémon catches you\nas you try to sneak around!`,
|
|
||||||
"fight_or_flight_boss_enraged": "The opposing {{enemyPokemon}} has become enraged!",
|
|
||||||
"fight_or_flight_option_3_selected": "You leave the strong Pokémon\nwith its prize and continue on.",
|
|
||||||
|
|
||||||
"department_store_sale_intro_message": "It's a lady with a ton of shopping bags.",
|
|
||||||
"department_store_sale_speaker": "Shopper",
|
|
||||||
"department_store_sale_intro_dialogue": `Hello! Are you here for\nthe amazing sales too?
|
|
||||||
$There's a special coupon that you can\nredeem for a free item during the sale!
|
|
||||||
$I have an extra one. Here you go!`,
|
|
||||||
"department_store_sale_title": "Department Store Sale",
|
|
||||||
"department_store_sale_description": "There is merchandise in every direction! It looks like there are 4 counters where you can redeem the coupon for various items. The possibilities are endless!",
|
|
||||||
"department_store_sale_query": "Which counter will you go to?",
|
|
||||||
"department_store_sale_option_1_label": "TM Counter",
|
|
||||||
"department_store_sale_option_1_tooltip": "(+) TM Shop",
|
|
||||||
"department_store_sale_option_2_label": "Vitamin Counter",
|
|
||||||
"department_store_sale_option_2_tooltip": "(+) Vitamin Shop",
|
|
||||||
"department_store_sale_option_3_label": "Battle Item Counter",
|
|
||||||
"department_store_sale_option_3_tooltip": "(+) X Item Shop",
|
|
||||||
"department_store_sale_option_4_label": "Pokéball Counter",
|
|
||||||
"department_store_sale_option_4_tooltip": "(+) Pokéball Shop",
|
|
||||||
"department_store_sale_outro": "What a deal! You should shop there more often.",
|
|
||||||
|
|
||||||
"shady_vitamin_dealer_intro_message": "A man in a dark coat approaches you.",
|
|
||||||
"shady_vitamin_dealer_speaker": "Shady Salesman",
|
|
||||||
"shady_vitamin_dealer_intro_dialogue": `.@d{16}.@d{16}.@d{16}
|
|
||||||
$I've got the goods if you've got the money.
|
|
||||||
$Make sure your Pokémon can handle it though.`,
|
|
||||||
"shady_vitamin_dealer_title": "The Vitamin Dealer",
|
|
||||||
"shady_vitamin_dealer_description": "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.",
|
|
||||||
"shady_vitamin_dealer_query": "Which deal will choose?",
|
|
||||||
"shady_vitamin_dealer_invalid_selection": "Pokémon must be healthy enough.",
|
|
||||||
"shady_vitamin_dealer_option_1_label": "The Cheap Deal",
|
|
||||||
"shady_vitamin_dealer_option_1_tooltip": "(-) Pay {{option1Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
|
||||||
"shady_vitamin_dealer_option_2_label": "The Pricey Deal",
|
|
||||||
"shady_vitamin_dealer_option_2_tooltip": "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
|
||||||
"shady_vitamin_dealer_option_selected": `The man hands you two bottles and quickly disappears.
|
|
||||||
\${{selectedPokemon}} gained {{boost1}} and {{boost2}} boosts!`,
|
|
||||||
"shady_vitamin_dealer_damage_only": `But the medicine had some side effects!
|
|
||||||
$Your {{selectedPokemon}} takes some damage...`,
|
|
||||||
"shady_vitamin_dealer_bad_poison": `But the medicine had some side effects!
|
|
||||||
$Your {{selectedPokemon}} takes some damage\nand becomes badly poisoned...`,
|
|
||||||
"shady_vitamin_dealer_poison": `But the medicine had some side effects!
|
|
||||||
$Your {{selectedPokemon}} becomes poisoned...`,
|
|
||||||
"shady_vitamin_dealer_no_bad_effects": "Looks like there were no side-effects this time.",
|
|
||||||
"shady_vitamin_dealer_option_3_label": "Leave",
|
|
||||||
"shady_vitamin_dealer_option_3_tooltip": "(-) No Rewards",
|
|
||||||
|
|
||||||
"field_trip_intro_message": "It's a teacher and some school children!",
|
|
||||||
"field_trip_speaker": "Teacher",
|
|
||||||
"field_trip_intro_dialogue": `Hello, there! Would you be able to\nspare a minute for my students?
|
|
||||||
$I'm teaching them about Pokémon moves\nand would love to show them a demonstration.
|
|
||||||
$Would you mind showing us one of\nthe moves your Pokémon can use?`,
|
|
||||||
"field_trip_title": "Field Trip",
|
|
||||||
"field_trip_description": "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.",
|
|
||||||
"field_trip_query": "Which move category will you show off?",
|
|
||||||
"field_trip_option_1_label": "A Physical Move",
|
|
||||||
"field_trip_option_1_tooltip": "(+) Physical Item Rewards",
|
|
||||||
"field_trip_option_2_label": "A Special Move",
|
|
||||||
"field_trip_option_2_tooltip": "(+) Special Item Rewards",
|
|
||||||
"field_trip_option_3_label": "A Status Move",
|
|
||||||
"field_trip_option_3_tooltip": "(+) Status Item Rewards",
|
|
||||||
"field_trip_second_option_prompt": "Choose a move for your Pokémon to use.",
|
|
||||||
"field_trip_option_selected": "{{pokeName}} shows off an awesome display of {{move}}!",
|
|
||||||
"field_trip_option_incorrect": `...
|
|
||||||
$That isn't a {{moveCategory}} move!
|
|
||||||
$I'm sorry, but I can't give you anything.`,
|
|
||||||
"field_trip_lesson_learned": `Looks like you learned a valuable lesson?
|
|
||||||
$Your Pokémon also gained some knowledge.`,
|
|
||||||
"field_trip_outro_good": "Thank you so much for your kindness!\nI hope the items I had were helpful!",
|
|
||||||
"field_trip_outro_bad": "Come along children, we'll\nfind a better demonstration elsewhere.",
|
|
||||||
|
|
||||||
// Mystery Encounters -- Great Tier
|
|
||||||
|
|
||||||
"mysterious_challengers_intro_message": "Mysterious challengers have appeared!",
|
|
||||||
"mysterious_challengers_title": "Mysterious Challengers",
|
|
||||||
"mysterious_challengers_description": "If you defeat a challenger, you might impress them enough to receive a boon. But some look tough, are you up to the challenge?",
|
|
||||||
"mysterious_challengers_query": "Who will you battle?",
|
|
||||||
"mysterious_challengers_option_1_label": "A clever, mindful foe",
|
|
||||||
"mysterious_challengers_option_1_tooltip": "(-) Standard Battle\n(+) Move Item Rewards",
|
|
||||||
"mysterious_challengers_option_2_label": "A strong foe",
|
|
||||||
"mysterious_challengers_option_2_tooltip": "(-) Hard Battle\n(+) Good Rewards",
|
|
||||||
"mysterious_challengers_option_3_label": "The mightiest foe",
|
|
||||||
"mysterious_challengers_option_3_tooltip": "(-) Brutal Battle\n(+) Great Rewards",
|
|
||||||
"mysterious_challengers_option_selected_message": "The trainer steps forward...",
|
|
||||||
"mysterious_challengers_outro_win": "The mysterious challenger was defeated!",
|
|
||||||
|
|
||||||
"safari_zone_intro_message": "It's a safari zone!",
|
|
||||||
"safari_zone_title": "The Safari Zone",
|
|
||||||
"safari_zone_description": "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of 3 wild encounters where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!",
|
|
||||||
"safari_zone_query": "Would you like to enter?",
|
|
||||||
"safari_zone_option_1_label": "Enter",
|
|
||||||
"safari_zone_option_1_tooltip": "(-) Pay {{option1Money, money}}\n@[SUMMARY_GREEN]{(?) Safari Zone}",
|
|
||||||
"safari_zone_option_2_label": "Leave",
|
|
||||||
"safari_zone_option_2_tooltip": "(-) No Rewards",
|
|
||||||
"safari_zone_option_1_selected_message": "Time to test your luck!",
|
|
||||||
"safari_zone_option_2_selected_message": "You hurry along your way,\nwith a slight feeling of regret.",
|
|
||||||
"safari_zone_pokeball_option_label": "Throw a Pokéball",
|
|
||||||
"safari_zone_pokeball_option_tooltip": "(+) Throw a Pokéball",
|
|
||||||
"safari_zone_pokeball_option_selected": "You throw a Pokéball!",
|
|
||||||
"safari_zone_bait_option_label": "Throw bait",
|
|
||||||
"safari_zone_bait_option_tooltip": "(+) Increases Capture Rate\n(-) Chance to Increase Flee Rate",
|
|
||||||
"safari_zone_bait_option_selected": "You throw some bait!",
|
|
||||||
"safari_zone_mud_option_label": "Throw mud",
|
|
||||||
"safari_zone_mud_option_tooltip": "(+) Decreases Flee Rate\n(-) Chance to Decrease Capture Rate",
|
|
||||||
"safari_zone_mud_option_selected": "You throw some mud!",
|
|
||||||
"safari_zone_flee_option_label": "Flee",
|
|
||||||
"safari_zone_flee_option_tooltip": "(?) Flee from this Pokémon",
|
|
||||||
"safari_zone_pokemon_watching": "{{pokemonName}} is watching carefully!",
|
|
||||||
"safari_zone_pokemon_eating": "{{pokemonName}} is eating!",
|
|
||||||
"safari_zone_pokemon_busy_eating": "{{pokemonName}} is busy eating!",
|
|
||||||
"safari_zone_pokemon_angry": "{{pokemonName}} is angry!",
|
|
||||||
"safari_zone_pokemon_beside_itself_angry": "{{pokemonName}} is beside itself with anger!",
|
|
||||||
"safari_zone_remaining_count": "{{remainingCount}} Pokémon remaining!",
|
|
||||||
|
|
||||||
// Mystery Encounters -- Ultra Tier
|
|
||||||
|
|
||||||
"training_session_intro_message": "You've come across some\ntraining tools and supplies.",
|
|
||||||
"training_session_title": "Training Session",
|
|
||||||
"training_session_description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.",
|
|
||||||
"training_session_query": "How should you train?",
|
|
||||||
"training_session_option_1_label": "Light Training",
|
|
||||||
"training_session_option_1_tooltip": "(-) Light Battle\n(+) Improve 2 Random IVs of Pokémon",
|
|
||||||
"training_session_option_2_label": "Moderate Training",
|
|
||||||
"training_session_option_2_tooltip": "(-) Moderate Battle\n(+) Change Pokémon's Nature",
|
|
||||||
"training_session_option_2_select_prompt": "Select a new nature\nto train your Pokémon in.",
|
|
||||||
"training_session_option_3_label": "Heavy Training",
|
|
||||||
"training_session_option_3_tooltip": "(-) Harsh Battle\n(+) Change Pokémon's Ability",
|
|
||||||
"training_session_option_3_select_prompt": "Select a new ability\nto train your Pokémon in.",
|
|
||||||
"training_session_option_selected_message": "{{selectedPokemon}} moves across\nthe clearing to face you...",
|
|
||||||
"training_session_battle_finished_1": `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
|
|
||||||
$Its {{stat1}} and {{stat2}} IVs were improved!`,
|
|
||||||
"training_session_battle_finished_2": `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
|
|
||||||
$Its nature was changed to {{nature}}!`,
|
|
||||||
"training_session_battle_finished_3": `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
|
|
||||||
$Its ability was changed to {{ability}}!`,
|
|
||||||
"training_session_outro_win": "That was a successful training session!",
|
|
||||||
|
|
||||||
// Mystery Encounters -- Rogue Tier
|
|
||||||
|
|
||||||
"dark_deal_intro_message": "A strange man in a tattered coat\nstands in your way...",
|
|
||||||
"dark_deal_speaker": "Shady Guy",
|
|
||||||
"dark_deal_intro_dialogue": `Hey, you!
|
|
||||||
$I've been working on a new device\nto bring out a Pokémon's latent power!
|
|
||||||
$It completely rebinds the Pokémon's atoms\nat a molecular level into a far more powerful form.
|
|
||||||
$Hehe...@d{64} I just need some sac-@d{32}\nErr, test subjects, to prove it works.`,
|
|
||||||
"dark_deal_title": "Dark Deal",
|
|
||||||
"dark_deal_description": "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"",
|
|
||||||
"dark_deal_query": "What will you do?",
|
|
||||||
"dark_deal_option_1_label": "Accept",
|
|
||||||
"dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon",
|
|
||||||
"dark_deal_option_2_label": "Refuse",
|
|
||||||
"dark_deal_option_2_tooltip": "(-) No Rewards",
|
|
||||||
"dark_deal_option_1_selected": `Let's see, that {{pokeName}} will do nicely!
|
|
||||||
$Remember, I'm not responsible\nif anything bad happens!@d{32} Hehe...`,
|
|
||||||
"dark_deal_option_1_selected_message": `The man hands you 5 Rogue Balls.
|
|
||||||
\${{pokeName}} hops into the strange machine...
|
|
||||||
$Flashing lights and weird noises\nstart coming from the machine!
|
|
||||||
$...@d{96} Something emerges\nfrom the device, raging wildly!`,
|
|
||||||
"dark_deal_option_2_selected": "Not gonna help a poor fellow out?\nPah!",
|
|
||||||
"dark_deal_outro": "After the harrowing encounter,\nyou collect yourself and depart.",
|
|
||||||
|
|
||||||
"sleeping_snorlax_intro_message": `As you walk down a narrow pathway, you see a towering silhouette blocking your path.
|
|
||||||
$You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`,
|
|
||||||
"sleeping_snorlax_title": "Sleeping Snorlax",
|
|
||||||
"sleeping_snorlax_description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...",
|
|
||||||
"sleeping_snorlax_query": "What will you do?",
|
|
||||||
"sleeping_snorlax_option_1_label": "Fight it",
|
|
||||||
"sleeping_snorlax_option_1_tooltip": "(-) Fight Sleeping Snorlax",
|
|
||||||
"sleeping_snorlax_option_2_label": "Wait for it to move",
|
|
||||||
"sleeping_snorlax_option_2_tooltip": "@[SUMMARY_BLUE]{(75%) Wait a short time}\n@[SUMMARY_BLUE]{(25%) Wait a long time}",
|
|
||||||
"sleeping_snorlax_option_3_label": "Steal its item",
|
|
||||||
"sleeping_snorlax_option_3_tooltip": "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Leftovers",
|
|
||||||
"sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this",
|
|
||||||
"sleeping_snorlax_option_1_selected_message": "You approach the\nPokémon without fear.",
|
|
||||||
"sleeping_snorlax_option_2_selected_message": `.@d{32}.@d{32}.@d{32}
|
|
||||||
$You wait for a time, but the Snorlax's yawns make your party sleepy.`,
|
|
||||||
"sleeping_snorlax_option_2_good_result": "When you all awaken, the Snorlax is no where to be found - but your Pokémon are all healed!",
|
|
||||||
"sleeping_snorlax_option_2_bad_result": `Your {{primaryName}} is still asleep...
|
|
||||||
$But on the bright side, the Snorlax left something behind...
|
|
||||||
$@s{item_fanfare}You gained a Berry!`,
|
|
||||||
"sleeping_snorlax_option_3_good_result": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!",
|
|
||||||
|
|
||||||
lostAtSea,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
29
src/locales/en/mystery-encounters/dark-deal-dialogue.ts
Normal file
29
src/locales/en/mystery-encounters/dark-deal-dialogue.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export const darkDealDialogue = {
|
||||||
|
intro: "A strange man in a tattered coat\nstands in your way...",
|
||||||
|
speaker: "Shady Guy",
|
||||||
|
intro_dialogue: `Hey, you!
|
||||||
|
$I've been working on a new device\nto bring out a Pokémon's latent power!
|
||||||
|
$It completely rebinds the Pokémon's atoms\nat a molecular level into a far more powerful form.
|
||||||
|
$Hehe...@d{64} I just need some sac-@d{32}\nErr, test subjects, to prove it works.`,
|
||||||
|
title: "Dark Deal",
|
||||||
|
description: "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"",
|
||||||
|
query: "What will you do?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Accept",
|
||||||
|
tooltip: "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon",
|
||||||
|
selected_dialogue: `Let's see, that {{pokeName}} will do nicely!
|
||||||
|
$Remember, I'm not responsible\nif anything bad happens!@d{32} Hehe...`,
|
||||||
|
selected_message: `The man hands you 5 Rogue Balls.
|
||||||
|
\${{pokeName}} hops into the strange machine...
|
||||||
|
$Flashing lights and weird noises\nstart coming from the machine!
|
||||||
|
$...@d{96} Something emerges\nfrom the device, raging wildly!`
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Refuse",
|
||||||
|
tooltip: "(-) No Rewards",
|
||||||
|
selected: "Not gonna help a poor fellow out?\nPah!",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outro: "After the harrowing encounter,\nyou collect yourself and depart."
|
||||||
|
};
|
@ -0,0 +1,29 @@
|
|||||||
|
export const departmentStoreSaleDialogue = {
|
||||||
|
intro: "It's a lady with a ton of shopping bags.",
|
||||||
|
speaker: "Shopper",
|
||||||
|
intro_dialogue: `Hello! Are you here for\nthe amazing sales too?
|
||||||
|
$There's a special coupon that you can\nredeem for a free item during the sale!
|
||||||
|
$I have an extra one. Here you go!`,
|
||||||
|
title: "Department Store Sale",
|
||||||
|
description: "There is merchandise in every direction! It looks like there are 4 counters where you can redeem the coupon for various items. The possibilities are endless!",
|
||||||
|
query: "Which counter will you go to?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "TM Counter",
|
||||||
|
tooltip: "(+) TM Shop",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Vitamin Counter",
|
||||||
|
tooltip: "(+) Vitamin Shop",
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Battle Item Counter",
|
||||||
|
tooltip: "(+) X Item Shop",
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
label: "Pokéball Counter",
|
||||||
|
tooltip: "(+) Pokéball Shop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
outro: "What a deal! You should shop there more often."
|
||||||
|
};
|
33
src/locales/en/mystery-encounters/field-trip-dialogue.ts
Normal file
33
src/locales/en/mystery-encounters/field-trip-dialogue.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
export const fieldTripDialogue = {
|
||||||
|
intro: "It's a teacher and some school children!",
|
||||||
|
speaker: "Teacher",
|
||||||
|
intro_dialogue: `Hello, there! Would you be able to\nspare a minute for my students?
|
||||||
|
$I'm teaching them about Pokémon moves\nand would love to show them a demonstration.
|
||||||
|
$Would you mind showing us one of\nthe moves your Pokémon can use?`,
|
||||||
|
title: "Field Trip",
|
||||||
|
description: "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.",
|
||||||
|
query: "Which move category will you show off?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "A Physical Move",
|
||||||
|
tooltip: "(+) Physical Item Rewards",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "A Special Move",
|
||||||
|
tooltip: "(+) Special Item Rewards",
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "A Status Move",
|
||||||
|
tooltip: "(+) Status Item Rewards",
|
||||||
|
},
|
||||||
|
selected: "{{pokeName}} shows off an awesome display of {{move}}!",
|
||||||
|
incorrect: `...
|
||||||
|
$That isn't a {{moveCategory}} move!
|
||||||
|
$I'm sorry, but I can't give you anything.`,
|
||||||
|
lesson_learned: `Looks like you learned a valuable lesson?
|
||||||
|
$Your Pokémon also gained some knowledge.`
|
||||||
|
},
|
||||||
|
second_option_prompt: "Choose a move for your Pokémon to use.",
|
||||||
|
outro_good: "Thank you so much for your kindness!\nI hope the items I had were helpful!",
|
||||||
|
outro_bad: "Come along children, we'll\nfind a better demonstration elsewhere."
|
||||||
|
};
|
30
src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts
Normal file
30
src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export const fieryFalloutDialogue = {
|
||||||
|
intro: "You encounter a blistering storm of smoke and ash!",
|
||||||
|
title: "Fiery Fallout",
|
||||||
|
description: "The whirling ash and embers have cut visibility to nearly zero. It seems like there might be some... source that is causing these conditions. But what could be behind a phenomenon of this magnitude?",
|
||||||
|
query: "What will you do?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Find the source",
|
||||||
|
tooltip: "(?) Discover the source\n(-) Hard Battle",
|
||||||
|
selected: `You push through the storm, and find two Volcarona in the middle of a mating dance!
|
||||||
|
$They don't take kindly to the interruption and attack!`
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Hunker down",
|
||||||
|
tooltip: "(-) Suffer the effects of the weather",
|
||||||
|
selected: `The weather effects cause significant\nharm as you struggle to find shelter!
|
||||||
|
$Your party takes 20% Max HP damage!`,
|
||||||
|
target_burned: "Your {{burnedPokemon}} also became burned!"
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Your Fire types help",
|
||||||
|
tooltip: "(+) End the conditions\n(+) Gain a Charcoal",
|
||||||
|
disabled_tooltip: "You need at least 2 Fire Type Pokémon to choose this",
|
||||||
|
selected: `Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance!
|
||||||
|
$Thankfully, your Pokémon are able to calm them,\nand they depart without issue.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
found_charcoal: `After the weather clears,\nyour {{leadPokemon}} spots something on the ground.
|
||||||
|
$@s{item_fanfare}{{leadPokemon}} gained a Charcoal!`
|
||||||
|
};
|
@ -0,0 +1,31 @@
|
|||||||
|
export const fightOrFlightDialogue = {
|
||||||
|
intro: "Something shiny is sparkling\non the ground near that Pokémon!",
|
||||||
|
title: "Fight or Flight",
|
||||||
|
description: "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but this Pokémon looks strong. You could also try to sneak around, though the Pokémon might catch you.",
|
||||||
|
query: "What will you do?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Battle the Pokémon",
|
||||||
|
tooltip: "(-) Hard Battle\n(+) New Item",
|
||||||
|
selected: "You approach the\nPokémon without fear.",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "{{option2PrimaryName}} can help",
|
||||||
|
tooltip: "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}",
|
||||||
|
tooltip_special: "(+) {{option2PrimaryName}} uses {{option2PrimaryMove}}",
|
||||||
|
good_result: `.@d{32}.@d{32}.@d{32}
|
||||||
|
$You manage to sneak your way\npast and grab the item!`,
|
||||||
|
special_result: `.@d{32}.@d{32}.@d{32}
|
||||||
|
$Your {{option2PrimaryName}} helps you out and uses {{option2PrimaryMove}}!
|
||||||
|
$ You nabbed the item!`,
|
||||||
|
bad_result: `.@d{32}.@d{32}.@d{32}
|
||||||
|
$The Pokémon catches you\nas you try to sneak around!`,
|
||||||
|
boss_enraged: "The opposing {{enemyPokemon}} has become enraged!"
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Leave",
|
||||||
|
tooltip: "(-) No Rewards",
|
||||||
|
selected: "You leave the strong Pokémon\nwith its prize and continue on.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
31
src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts
Normal file
31
src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
export const lostAtSeaDialogue = {
|
||||||
|
intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.",
|
||||||
|
title: "Lost at Sea",
|
||||||
|
description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?",
|
||||||
|
query: "What will you do?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "{{option1PrimaryName}} can help",
|
||||||
|
label_disabled: "Can't {{option1RequiredMove}}",
|
||||||
|
tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP",
|
||||||
|
tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on",
|
||||||
|
selected: `{{option1PrimaryName}} swims ahead, guiding you back on track.
|
||||||
|
\${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "{{option2PrimaryName}} can help",
|
||||||
|
label_disabled: "Can't {{option2RequiredMove}}",
|
||||||
|
tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP",
|
||||||
|
tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with",
|
||||||
|
selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.
|
||||||
|
\${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`,
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Wander aimlessly",
|
||||||
|
tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP",
|
||||||
|
selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember.
|
||||||
|
$You and your Pokémon are fatigued from the whole ordeal.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
outro: "You are back on track."
|
||||||
|
};
|
@ -1,31 +0,0 @@
|
|||||||
export const lostAtSea = {
|
|
||||||
intro: "Wandering aimlessly, you effectively get nowhere.",
|
|
||||||
title: "Lost at sea",
|
|
||||||
description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?",
|
|
||||||
query: "What will you do?",
|
|
||||||
option: {
|
|
||||||
1: {
|
|
||||||
label: "{{option1PrimaryName}} can help",
|
|
||||||
label_disabled: "Can't {{option1RequiredMove}}",
|
|
||||||
tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.",
|
|
||||||
tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on",
|
|
||||||
selected:
|
|
||||||
"{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.",
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
label: "{{option2PrimaryName}} can help",
|
|
||||||
label_disabled: "Can't {{option2RequiredMove}}",
|
|
||||||
tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.",
|
|
||||||
tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with",
|
|
||||||
selected:
|
|
||||||
"{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.",
|
|
||||||
},
|
|
||||||
3: {
|
|
||||||
label: "Wander aimlessly",
|
|
||||||
tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.",
|
|
||||||
selected: `You float about in the boat, steering it aimlessly until you finally get back on track.
|
|
||||||
$You and your Pokémon get very fatigued during the whole ordeal.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
outro: "You are back on track."
|
|
||||||
};
|
|
@ -0,0 +1,22 @@
|
|||||||
|
export const mysteriousChallengersDialogue = {
|
||||||
|
intro: "Mysterious challengers have appeared!",
|
||||||
|
title: "Mysterious Challengers",
|
||||||
|
description: "If you defeat a challenger, you might impress them enough to receive a boon. But some look tough, are you up to the challenge?",
|
||||||
|
query: "Who will you battle?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "A clever, mindful foe",
|
||||||
|
tooltip: "(-) Standard Battle\n(+) Move Item Rewards",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "A strong foe",
|
||||||
|
tooltip: "(-) Hard Battle\n(+) Good Rewards",
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "The mightiest foe",
|
||||||
|
tooltip: "(-) Brutal Battle\n(+) Great Rewards",
|
||||||
|
},
|
||||||
|
selected: "The trainer steps forward...",
|
||||||
|
},
|
||||||
|
outro: "The mysterious challenger was defeated!"
|
||||||
|
};
|
@ -0,0 +1,27 @@
|
|||||||
|
export const mysteriousChestDialogue = {
|
||||||
|
intro: "You found...@d{32} a chest?",
|
||||||
|
title: "The Mysterious Chest",
|
||||||
|
description: "A beautifully ornamented chest stands on the ground. There must be something good inside... right?",
|
||||||
|
query: "Will you open it?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Open it",
|
||||||
|
tooltip: "@[SUMMARY_BLUE]{(35%) Something terrible}\n@[SUMMARY_GREEN]{(40%) Okay Rewards}\n@[SUMMARY_GREEN]{(20%) Good Rewards}\n@[SUMMARY_GREEN]{(4%) Great Rewards}\n@[SUMMARY_GREEN]{(1%) Amazing Rewards}",
|
||||||
|
selected: "You open the chest to find...",
|
||||||
|
normal: "Just some normal tools and items.",
|
||||||
|
good: "Some pretty nice tools and items.",
|
||||||
|
great: "A couple great tools and items!",
|
||||||
|
amazing: "Whoa! An amazing item!",
|
||||||
|
bad: `Oh no!@d{32}\nThe chest was trapped!
|
||||||
|
$Your {{pokeName}} jumps in front of you\nbut is KOed in the process.`,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "It's too risky, leave",
|
||||||
|
tooltip: "(-) No Rewards",
|
||||||
|
selected: "You hurry along your way,\nwith a slight feeling of regret.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
46
src/locales/en/mystery-encounters/safari-zone-dialogue.ts
Normal file
46
src/locales/en/mystery-encounters/safari-zone-dialogue.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
export const safariZoneDialogue = {
|
||||||
|
intro: "It's a safari zone!",
|
||||||
|
title: "The Safari Zone",
|
||||||
|
description: "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of 3 wild encounters where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!",
|
||||||
|
query: "Would you like to enter?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Enter",
|
||||||
|
tooltip: "(-) Pay {{option1Money, money}}\n@[SUMMARY_GREEN]{(?) Safari Zone}",
|
||||||
|
selected: "Time to test your luck!",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Leave",
|
||||||
|
tooltip: "(-) No Rewards",
|
||||||
|
selected: "You hurry along your way,\nwith a slight feeling of regret.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
safari: {
|
||||||
|
1: {
|
||||||
|
label: "Throw a Pokéball",
|
||||||
|
tooltip: "(+) Throw a Pokéball",
|
||||||
|
selected: "You throw a Pokéball!",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Throw bait",
|
||||||
|
tooltip: "(+) Increases Capture Rate\n(-) Chance to Increase Flee Rate",
|
||||||
|
selected: "You throw some bait!",
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Throw mud",
|
||||||
|
tooltip: "(+) Decreases Flee Rate\n(-) Chance to Decrease Capture Rate",
|
||||||
|
selected: "You throw some mud!",
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
label: "Flee",
|
||||||
|
tooltip: "(?) Flee from this Pokémon",
|
||||||
|
},
|
||||||
|
watching: "{{pokemonName}} is watching carefully!",
|
||||||
|
eating: "{{pokemonName}} is eating!",
|
||||||
|
busy_eating: "{{pokemonName}} is busy eating!",
|
||||||
|
angry: "{{pokemonName}} is angry!",
|
||||||
|
beside_itself_angry: "{{pokemonName}} is beside itself with anger!",
|
||||||
|
remaining_count: "{{remainingCount}} Pokémon remaining!",
|
||||||
|
},
|
||||||
|
outro: "That was a fun little excursion!"
|
||||||
|
};
|
@ -0,0 +1,40 @@
|
|||||||
|
export const shadyVitaminDealerDialogue = {
|
||||||
|
intro: "A man in a dark coat approaches you.",
|
||||||
|
speaker: "Shady Salesman",
|
||||||
|
intro_dialogue: `.@d{16}.@d{16}.@d{16}
|
||||||
|
$I've got the goods if you've got the money.
|
||||||
|
$Make sure your Pokémon can handle it though.`,
|
||||||
|
title: "The Vitamin Dealer",
|
||||||
|
description: "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.",
|
||||||
|
query: "Which deal will choose?",
|
||||||
|
invalid_selection: "Pokémon must be healthy enough.",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "The Cheap Deal",
|
||||||
|
tooltip: "(-) Pay {{option1Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
||||||
|
selected: `{{option1PrimaryName}} swims ahead, guiding you back on track.
|
||||||
|
\${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "The Pricey Deal",
|
||||||
|
tooltip: "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
||||||
|
selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.
|
||||||
|
\${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`,
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Leave",
|
||||||
|
tooltip: "(-) No Rewards",
|
||||||
|
selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember.
|
||||||
|
$You and your Pokémon are fatigued from the whole ordeal.`,
|
||||||
|
},
|
||||||
|
selected: `The man hands you two bottles and quickly disappears.
|
||||||
|
\${{selectedPokemon}} gained {{boost1}} and {{boost2}} boosts!`
|
||||||
|
},
|
||||||
|
damage_only: `But the medicine had some side effects!
|
||||||
|
$Your {{selectedPokemon}} takes some damage...`,
|
||||||
|
bad_poison: `But the medicine had some side effects!
|
||||||
|
$Your {{selectedPokemon}} takes some damage\nand becomes badly poisoned...`,
|
||||||
|
poison: `But the medicine had some side effects!
|
||||||
|
$Your {{selectedPokemon}} becomes poisoned...`,
|
||||||
|
no_bad_effects: "Looks like there were no side-effects this time.",
|
||||||
|
};
|
@ -0,0 +1,28 @@
|
|||||||
|
export const slumberingSnorlaxDialogue = {
|
||||||
|
intro: `As you walk down a narrow pathway, you see a towering silhouette blocking your path.
|
||||||
|
$You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`,
|
||||||
|
title: "Slumbering Snorlax",
|
||||||
|
description: "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...",
|
||||||
|
query: "What will you do?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Battle it",
|
||||||
|
tooltip: "(-) Fight Sleeping Snorlax\n(+) Special Reward",
|
||||||
|
selected: "You approach the\nPokémon without fear.",
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Wait for it to move",
|
||||||
|
tooltip: "(-) Wait a Long Time\n(+) Recover Party",
|
||||||
|
selected: `.@d{32}.@d{32}.@d{32}
|
||||||
|
$You wait for a time, but the Snorlax's yawns make your party sleepy...`,
|
||||||
|
rest_result: "When you all awaken, the Snorlax is no where to be found -\nbut your Pokémon are all healed!",
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Steal its item",
|
||||||
|
tooltip: "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Special Reward",
|
||||||
|
disabled_tooltip: "Your Pokémon need to know certain moves to choose this",
|
||||||
|
selected: `Your {{option3PrimaryName}} uses {{option3PrimaryMove}}!
|
||||||
|
$@s{item_fanfare}It steals Leftovers off the sleeping\nSnorlax and you make out like bandits!`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,30 @@
|
|||||||
|
export const trainingSessionDialogue = {
|
||||||
|
intro: "You've come across some\ntraining tools and supplies.",
|
||||||
|
title: "Training Session",
|
||||||
|
description: "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.",
|
||||||
|
query: "How should you train?",
|
||||||
|
option: {
|
||||||
|
1: {
|
||||||
|
label: "Light Training",
|
||||||
|
tooltip: "(-) Light Battle\n(+) Improve 2 Random IVs of Pokémon",
|
||||||
|
finished: `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
|
||||||
|
$Its {{stat1}} and {{stat2}} IVs were improved!`,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
label: "Moderate Training",
|
||||||
|
tooltip: "(-) Moderate Battle\n(+) Change Pokémon's Nature",
|
||||||
|
select_prompt: "Select a new nature\nto train your Pokémon in.",
|
||||||
|
finished: `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
|
||||||
|
$Its nature was changed to {{nature}}!`,
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
label: "Heavy Training",
|
||||||
|
tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP",
|
||||||
|
select_prompt: "Select a new ability\nto train your Pokémon in.",
|
||||||
|
finished: `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
|
||||||
|
$Its ability was changed to {{ability}}!`,
|
||||||
|
},
|
||||||
|
selected: "{{selectedPokemon}} moves across\nthe clearing to face you...",
|
||||||
|
},
|
||||||
|
outro: "That was a successful training session!",
|
||||||
|
};
|
@ -1,15 +1,16 @@
|
|||||||
import BattleScene, { bypassLogin } from "./battle-scene";
|
import BattleScene, { bypassLogin } from "./battle-scene";
|
||||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
|
import { DamageResult, default as Pokemon, EnemyPokemon, FieldPosition, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./field/pokemon";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, OneHitKOAccuracyAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr } from "./data/move";
|
import { isNullOrUndefined } from "./utils";
|
||||||
|
import { allMoves, applyFilteredMoveAttrs, applyMoveAttrs, AttackMove, BypassRedirectAttr, BypassSleepAttr, ChargeAttr, CopyMoveAttr, FixedDamageAttr, ForceSwitchOutAttr, getMoveTargets, HealStatusEffectAttr, HitsTagAttr, IgnoreOpponentStatChangesAttr, IncrementMovePriorityAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveEffectTrigger, MoveFlags, MoveTarget, MoveTargetSet, MultiHitAttr, NoEffectAttr, OneHitKOAccuracyAttr, OverrideMoveEffectAttr, PostVictoryStatChangeAttr, PreMoveMessageAttr, SelfStatusMove, VariableAccuracyAttr, VariableTargetAttr } from "./data/move";
|
||||||
import { Mode } from "./ui/ui";
|
import { Mode } from "./ui/ui";
|
||||||
import { Command } from "./ui/command-ui-handler";
|
import { Command } from "./ui/command-ui-handler";
|
||||||
import { Stat } from "./data/pokemon-stat";
|
import { Stat } from "./data/pokemon-stat";
|
||||||
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, PokemonMoveAccuracyBoosterModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier } from "./modifier/modifier";
|
import { BerryModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, IvScannerModifier, LapsingPersistentModifier, LapsingPokemonHeldItemModifier, MapModifier, Modifier, MoneyInterestModifier, MoneyMultiplierModifier, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier } from "./modifier/modifier";
|
||||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
||||||
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
|
import { CommonAnim, CommonBattleAnim, initEncounterAnims, initMoveAnim, loadEncounterAnimAssets, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims";
|
||||||
import { StatusEffect, getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./data/status-effect";
|
import { getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText, StatusEffect } from "./data/status-effect";
|
||||||
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
||||||
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
||||||
import { EvolutionPhase } from "./evolution-phase";
|
import { EvolutionPhase } from "./evolution-phase";
|
||||||
@ -17,21 +18,21 @@ import { Phase } from "./phase";
|
|||||||
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat";
|
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat";
|
||||||
import { biomeLinks, getBiomeName } from "./data/biomes";
|
import { biomeLinks, getBiomeName } from "./data/biomes";
|
||||||
import { ModifierTier } from "./modifier/modifier-tier";
|
import { ModifierTier } from "./modifier/modifier-tier";
|
||||||
import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, RememberMoveModifierType, TmModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds, CustomModifierSettings } from "./modifier/modifier-type";
|
import { CustomModifierSettings, FusePokemonModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, modifierTypes, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, regenerateModifierPoolThresholds, RememberMoveModifierType, TmModifierType } from "./modifier/modifier-type";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, ProtectedTag, SemiInvulnerableTag, TrappedTag, MysteryEncounterPostSummonTag } from "./data/battler-tags";
|
import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, MysteryEncounterPostSummonTag, ProtectedTag, SemiInvulnerableTag, TrappedTag } from "./data/battler-tags";
|
||||||
import { getPokemonMessage, getPokemonNameWithAffix } from "./messages";
|
import { getPokemonMessage, getPokemonNameWithAffix } from "./messages";
|
||||||
import { Starter } from "./ui/starter-select-ui-handler";
|
import { Starter } from "./ui/starter-select-ui-handler";
|
||||||
import { Gender } from "./data/gender";
|
import { Gender } from "./data/gender";
|
||||||
import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
|
import { getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage, Weather, WeatherType } from "./data/weather";
|
||||||
import { TempBattleStat } from "./data/temp-battle-stat";
|
import { TempBattleStat } from "./data/temp-battle-stat";
|
||||||
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
|
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
|
||||||
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, WonderSkinAbAttr, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability";
|
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, WonderSkinAbAttr, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability";
|
||||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||||
import { getBiomeKey } from "./field/arena";
|
import { getBiomeKey } from "./field/arena";
|
||||||
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
|
import { BattlerIndex, BattleType, TurnCommand } from "./battle";
|
||||||
import { ChallengeAchv, HealAchv, LevelAchv, achvs } from "./system/achv";
|
import { achvs, ChallengeAchv, HealAchv, LevelAchv } from "./system/achv";
|
||||||
import { TrainerSlot, trainerConfigs } from "./data/trainer-config";
|
import { trainerConfigs, TrainerSlot } from "./data/trainer-config";
|
||||||
import { EggHatchPhase } from "./egg-hatch-phase";
|
import { EggHatchPhase } from "./egg-hatch-phase";
|
||||||
import { Egg } from "./data/egg";
|
import { Egg } from "./data/egg";
|
||||||
import { vouchers } from "./system/voucher";
|
import { vouchers } from "./system/voucher";
|
||||||
@ -41,7 +42,7 @@ import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims
|
|||||||
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
|
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
|
||||||
import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue";
|
import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue";
|
||||||
import { SettingKeys } from "./system/settings/settings";
|
import { SettingKeys } from "./system/settings/settings";
|
||||||
import { Tutorial, handleTutorial } from "./tutorial";
|
import { handleTutorial, Tutorial } from "./tutorial";
|
||||||
import { TerrainType } from "./data/terrain";
|
import { TerrainType } from "./data/terrain";
|
||||||
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
|
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
|
||||||
import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler";
|
import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler";
|
||||||
@ -50,7 +51,7 @@ import { GameMode, GameModes, getGameMode } from "./game-mode";
|
|||||||
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
|
||||||
import i18next from "./plugins/i18n";
|
import i18next from "./plugins/i18n";
|
||||||
import * as Overrides from "./overrides";
|
import * as Overrides from "./overrides";
|
||||||
import { TextStyle, addTextObject } from "./ui/text";
|
import { addTextObject, TextStyle } from "./ui/text";
|
||||||
import { Type } from "./data/type";
|
import { Type } from "./data/type";
|
||||||
import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene";
|
import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
@ -65,10 +66,9 @@ import { PlayerGender } from "#enums/player-gender";
|
|||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter";
|
import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase";
|
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import { handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { doTrainerExclamation, handleMysteryEncounterBattleStartEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler";
|
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler";
|
||||||
import { isNullOrUndefined } from "./utils";
|
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
|
||||||
const { t } = i18next;
|
const { t } = i18next;
|
||||||
@ -832,6 +832,11 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
mysteryEncounter.populateDialogueTokensFromRequirements(this.scene);
|
mysteryEncounter.populateDialogueTokensFromRequirements(this.scene);
|
||||||
}, this.scene.currentBattle.waveIndex);
|
}, this.scene.currentBattle.waveIndex);
|
||||||
|
|
||||||
|
// Add any special encounter animations to load
|
||||||
|
if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) {
|
||||||
|
loadEnemyAssets.push(initEncounterAnims(this.scene, mysteryEncounter.encounterAnimations).then(() => loadEncounterAnimAssets(this.scene, true)));
|
||||||
|
}
|
||||||
|
|
||||||
// Add intro visuals for mystery encounter
|
// Add intro visuals for mystery encounter
|
||||||
mysteryEncounter.initIntroVisuals(this.scene);
|
mysteryEncounter.initIntroVisuals(this.scene);
|
||||||
this.scene.field.add(mysteryEncounter.introVisuals);
|
this.scene.field.add(mysteryEncounter.introVisuals);
|
||||||
@ -896,6 +901,15 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
battle.mysteryEncounter = newEncounter;
|
battle.mysteryEncounter = newEncounter;
|
||||||
}
|
}
|
||||||
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter.introVisuals.initSprite()));
|
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter.introVisuals.initSprite()));
|
||||||
|
// Load Mystery Encounter Exclamation bubble and sfx
|
||||||
|
loadEnemyAssets.push(new Promise<void>(resolve => {
|
||||||
|
this.scene.loadSe("GEN8- Exclaim.wav", "battle_anims", "GEN8- Exclaim.wav");
|
||||||
|
this.scene.loadAtlas("exclaim", "mystery-encounters");
|
||||||
|
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
|
||||||
|
if (!this.scene.load.isLoading()) {
|
||||||
|
this.scene.load.start();
|
||||||
|
}
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
// This block only applies for double battles to init the boss segments (idk why it's split up like this)
|
// This block only applies for double battles to init the boss segments (idk why it's split up like this)
|
||||||
if (battle.enemyParty.filter(p => p.isBoss()).length > 1) {
|
if (battle.enemyParty.filter(p => p.isBoss()).length > 1) {
|
||||||
@ -1076,14 +1090,18 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
const introVisuals = this.scene.currentBattle.mysteryEncounter.introVisuals;
|
const introVisuals = this.scene.currentBattle.mysteryEncounter.introVisuals;
|
||||||
introVisuals.playAnim();
|
introVisuals.playAnim();
|
||||||
|
|
||||||
|
if (this.scene.currentBattle.mysteryEncounter.onVisualsStart) {
|
||||||
|
this.scene.currentBattle.mysteryEncounter.onVisualsStart(this.scene);
|
||||||
|
}
|
||||||
|
|
||||||
const doEncounter = () => {
|
const doEncounter = () => {
|
||||||
this.scene.playBgm(undefined);
|
this.scene.playBgm(undefined);
|
||||||
|
|
||||||
const doShowEncounterOptions = () => {
|
const doShowEncounterOptions = () => {
|
||||||
this.scene.ui.clearText();
|
this.scene.ui.clearText();
|
||||||
this.scene.ui.getMessageHandler().hideNameText();
|
this.scene.ui.getMessageHandler().hideNameText();
|
||||||
this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene));
|
|
||||||
|
|
||||||
|
this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene));
|
||||||
this.end();
|
this.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1116,6 +1134,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
if (!encounterMessage) {
|
if (!encounterMessage) {
|
||||||
doEncounter();
|
doEncounter();
|
||||||
} else {
|
} else {
|
||||||
|
doTrainerExclamation(this.scene);
|
||||||
this.scene.ui.showDialogue(encounterMessage, "???", null, () => {
|
this.scene.ui.showDialogue(encounterMessage, "???", null, () => {
|
||||||
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doEncounter()));
|
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doEncounter()));
|
||||||
});
|
});
|
||||||
@ -2034,6 +2053,8 @@ export class TurnInitPhase extends FieldPhase {
|
|||||||
//this.scene.pushPhase(new MoveAnimTestPhase(this.scene));
|
//this.scene.pushPhase(new MoveAnimTestPhase(this.scene));
|
||||||
this.scene.eventTarget.dispatchEvent(new TurnInitEvent());
|
this.scene.eventTarget.dispatchEvent(new TurnInitEvent());
|
||||||
|
|
||||||
|
handleMysteryEncounterBattleStartEffects(this.scene);
|
||||||
|
|
||||||
this.scene.getField().forEach((pokemon, i) => {
|
this.scene.getField().forEach((pokemon, i) => {
|
||||||
if (pokemon?.isActive()) {
|
if (pokemon?.isActive()) {
|
||||||
if (pokemon.isPlayer()) {
|
if (pokemon.isPlayer()) {
|
||||||
@ -2694,12 +2715,14 @@ export class NewBattlePhase extends BattlePhase {
|
|||||||
export class CommonAnimPhase extends PokemonPhase {
|
export class CommonAnimPhase extends PokemonPhase {
|
||||||
private anim: CommonAnim;
|
private anim: CommonAnim;
|
||||||
private targetIndex: integer;
|
private targetIndex: integer;
|
||||||
|
private playOnEmptyField: boolean;
|
||||||
|
|
||||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim) {
|
constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim, playOnEmptyField: boolean = false) {
|
||||||
super(scene, battlerIndex);
|
super(scene, battlerIndex);
|
||||||
|
|
||||||
this.anim = anim;
|
this.anim = anim;
|
||||||
this.targetIndex = targetIndex;
|
this.targetIndex = targetIndex;
|
||||||
|
this.playOnEmptyField = playOnEmptyField;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAnimation(anim: CommonAnim) {
|
setAnimation(anim: CommonAnim) {
|
||||||
@ -2707,7 +2730,8 @@ export class CommonAnimPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
new CommonBattleAnim(this.anim, this.getPokemon(), this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon()).play(this.scene, () => {
|
const target = this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon();
|
||||||
|
new CommonBattleAnim(this.anim, this.getPokemon(), target, this.playOnEmptyField).play(this.scene, () => {
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -5361,10 +5385,10 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
return true;
|
return true;
|
||||||
case 1:
|
case 1:
|
||||||
if (typeOptions.length === 0) {
|
if (typeOptions.length === 0) {
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.clearText();
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
super.end();
|
super.end();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
modifierType = typeOptions[cursor].type;
|
modifierType = typeOptions[cursor].type;
|
||||||
break;
|
break;
|
||||||
|
@ -2,7 +2,7 @@ import i18next from "i18next";
|
|||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import { Phase } from "../phase";
|
import { Phase } from "../phase";
|
||||||
import { Mode } from "../ui/ui";
|
import { Mode } from "../ui/ui";
|
||||||
import { hideMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils";
|
import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases";
|
import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases";
|
||||||
import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter";
|
import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter";
|
||||||
@ -14,6 +14,7 @@ import { IvScannerModifier } from "../modifier/modifier";
|
|||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { isNullOrUndefined } from "../utils";
|
import { isNullOrUndefined } from "../utils";
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will handle (in order):
|
* Will handle (in order):
|
||||||
@ -27,6 +28,8 @@ import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-d
|
|||||||
export class MysteryEncounterPhase extends Phase {
|
export class MysteryEncounterPhase extends Phase {
|
||||||
optionSelectSettings: OptionSelectSettings;
|
optionSelectSettings: OptionSelectSettings;
|
||||||
|
|
||||||
|
private FIRST_DIALOGUE_PROMPT_DELAY = 300;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param scene
|
* @param scene
|
||||||
@ -45,9 +48,7 @@ export class MysteryEncounterPhase extends Phase {
|
|||||||
this.scene.clearPhaseQueue();
|
this.scene.clearPhaseQueue();
|
||||||
this.scene.clearPhaseQueueSplice();
|
this.scene.clearPhaseQueueSplice();
|
||||||
|
|
||||||
// Generates seed offset for RNG consistency, but incremented if the same MysteryEncounter has multiple option select cycles
|
this.scene.currentBattle.mysteryEncounter.updateSeedOffset(this.scene);
|
||||||
const offset = this.scene.currentBattle.mysteryEncounter.seedOffset ?? this.scene.currentBattle.waveIndex * 1000;
|
|
||||||
this.scene.currentBattle.mysteryEncounter.seedOffset = offset + 512;
|
|
||||||
|
|
||||||
if (!this.optionSelectSettings) {
|
if (!this.optionSelectSettings) {
|
||||||
// Sets flag that ME was encountered, only if this is not a followup option select phase
|
// Sets flag that ME was encountered, only if this is not a followup option select phase
|
||||||
@ -78,7 +79,7 @@ export class MysteryEncounterPhase extends Phase {
|
|||||||
this.continueEncounter();
|
this.continueEncounter();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||||
} else {
|
} else {
|
||||||
this.continueEncounter();
|
this.continueEncounter();
|
||||||
}
|
}
|
||||||
@ -108,9 +109,9 @@ export class MysteryEncounterPhase extends Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 750 : 0);
|
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||||
} else {
|
} else {
|
||||||
this.scene.ui.showText(text, null, nextAction, i === 0 ? 750 : 0, true);
|
this.scene.ui.showText(text, null, nextAction, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
};
|
};
|
||||||
@ -147,24 +148,46 @@ export class MysteryEncounterOptionSelectedPhase extends Phase {
|
|||||||
|
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
if (this.scene.currentBattle.mysteryEncounter.hideIntroVisuals) {
|
if (this.scene.currentBattle.mysteryEncounter.autoHideIntroVisuals) {
|
||||||
hideMysteryEncounterIntroVisuals(this.scene).then(() => {
|
transitionMysteryEncounterIntroVisuals(this.scene).then(() => {
|
||||||
this.scene.executeWithSeedOffset(() => {
|
this.scene.executeWithSeedOffset(() => {
|
||||||
this.onOptionSelect(this.scene).finally(() => {
|
this.onOptionSelect(this.scene).finally(() => {
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.scene.executeWithSeedOffset(() => {
|
this.scene.executeWithSeedOffset(() => {
|
||||||
this.onOptionSelect(this.scene).finally(() => {
|
this.onOptionSelect(this.scene).finally(() => {
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs at the beginning of an Encounter's battle
|
||||||
|
* Will cleanup any residual flinches, Endure, etc. that are left over from startOfBattleEffects
|
||||||
|
* See [TurnEndPhase](../phases.ts) for more details
|
||||||
|
*/
|
||||||
|
export class MysteryEncounterBattleStartCleanupPhase extends Phase {
|
||||||
|
constructor(scene: BattleScene) {
|
||||||
|
super(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
const field = this.scene.getField(true).filter(p => p.summonData);
|
||||||
|
field.forEach(pokemon => {
|
||||||
|
pokemon.lapseTags(BattlerTagLapseType.TURN_END);
|
||||||
|
});
|
||||||
|
|
||||||
|
super.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will handle (in order):
|
* Will handle (in order):
|
||||||
* - Setting BGM
|
* - Setting BGM
|
||||||
@ -173,8 +196,11 @@ export class MysteryEncounterOptionSelectedPhase extends Phase {
|
|||||||
* - Queue the SummonPhases, PostSummonPhases, etc., required to initialize the phase queue for a battle
|
* - Queue the SummonPhases, PostSummonPhases, etc., required to initialize the phase queue for a battle
|
||||||
*/
|
*/
|
||||||
export class MysteryEncounterBattlePhase extends Phase {
|
export class MysteryEncounterBattlePhase extends Phase {
|
||||||
constructor(scene: BattleScene) {
|
disableSwitch: boolean;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene, disableSwitch = false) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
this.disableSwitch = disableSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
@ -219,7 +245,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) {
|
if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) {
|
||||||
scene.ui.showText(this.getBattleMessage(scene), null, () => this.endBattleSetup(scene), 1500);
|
scene.ui.showText(this.getBattleMessage(scene), null, () => this.endBattleSetup(scene), 0);
|
||||||
} else {
|
} else {
|
||||||
this.endBattleSetup(scene);
|
this.endBattleSetup(scene);
|
||||||
}
|
}
|
||||||
@ -240,7 +266,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
this.endBattleSetup(scene);
|
this.endBattleSetup(scene);
|
||||||
};
|
};
|
||||||
if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) {
|
if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) {
|
||||||
scene.ui.showText(this.getBattleMessage(scene), null, doTrainerSummon, 1500, true);
|
scene.ui.showText(this.getBattleMessage(scene), null, doTrainerSummon, 1000, true);
|
||||||
} else {
|
} else {
|
||||||
doTrainerSummon();
|
doTrainerSummon();
|
||||||
}
|
}
|
||||||
@ -253,7 +279,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
} else {
|
} else {
|
||||||
const trainer = this.scene.currentBattle.trainer;
|
const trainer = this.scene.currentBattle.trainer;
|
||||||
let message: string;
|
let message: string;
|
||||||
scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.seedOffset);
|
scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||||
|
|
||||||
const showDialogueAndSummon = () => {
|
const showDialogueAndSummon = () => {
|
||||||
scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => {
|
scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => {
|
||||||
@ -302,7 +328,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
scene.pushPhase(new ToggleDoublePositionPhase(scene, false));
|
scene.pushPhase(new ToggleDoublePositionPhase(scene, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE && (scene.currentBattle.waveIndex > 1 || !scene.gameMode.isDaily)) {
|
if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE && !this.disableSwitch) {
|
||||||
const minPartySize = scene.currentBattle.double ? 2 : 1;
|
const minPartySize = scene.currentBattle.double ? 2 : 1;
|
||||||
if (availablePartyMembers.length > minPartySize) {
|
if (availablePartyMembers.length > minPartySize) {
|
||||||
scene.pushPhase(new CheckSwitchPhase(scene, 0, scene.currentBattle.double));
|
scene.pushPhase(new CheckSwitchPhase(scene, 0, scene.currentBattle.double));
|
||||||
@ -412,7 +438,7 @@ export class PostMysteryEncounterPhase extends Phase {
|
|||||||
this.continueEncounter();
|
this.continueEncounter();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||||
} else {
|
} else {
|
||||||
this.continueEncounter();
|
this.continueEncounter();
|
||||||
}
|
}
|
@ -1,17 +1,26 @@
|
|||||||
import { Button } from "#app/enums/buttons";
|
import { Button } from "#app/enums/buttons";
|
||||||
import { MessagePhase } from "#app/phases";
|
import { CommandPhase, MessagePhase, VictoryPhase } from "#app/phases";
|
||||||
import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase";
|
import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler";
|
import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import GameManager from "../utils/gameManager";
|
import GameManager from "../utils/gameManager";
|
||||||
|
import MessageUiHandler from "#app/ui/message-ui-handler";
|
||||||
|
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a MysteryEncounter to either the start of a battle, or to the MysteryEncounterRewardsPhase, depending on the option selected
|
||||||
|
* @param game
|
||||||
|
* @param optionNo - human number, not index
|
||||||
|
* @param isBattle - if selecting option should lead to battle, set to true
|
||||||
|
*/
|
||||||
|
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, isBattle: boolean = false) {
|
||||||
|
// Handle any eventual queued messages (e.g. weather phase, etc.)
|
||||||
|
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
|
||||||
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
|
uiHandler.processInput(Button.ACTION);
|
||||||
|
});
|
||||||
|
|
||||||
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) {
|
|
||||||
if (game.isCurrentPhase(MessagePhase)) {
|
if (game.isCurrentPhase(MessagePhase)) {
|
||||||
// Handle eventual weather messages (e.g. a downpour started!)
|
|
||||||
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
|
|
||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
|
||||||
uiHandler.processInput(Button.ACTION);
|
|
||||||
});
|
|
||||||
await game.phaseInterceptor.run(MessagePhase);
|
await game.phaseInterceptor.run(MessagePhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,35 +29,77 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterPhase, true);
|
||||||
|
|
||||||
// select the desired option
|
// select the desired option
|
||||||
game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => {
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that
|
||||||
uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that
|
|
||||||
|
|
||||||
switch (optionNo) {
|
switch (optionNo) {
|
||||||
case 1:
|
case 1:
|
||||||
// no movement needed. Default cursor position
|
// no movement needed. Default cursor position
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
uiHandler.processInput(Button.RIGHT);
|
uiHandler.processInput(Button.RIGHT);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
uiHandler.processInput(Button.DOWN);
|
uiHandler.processInput(Button.DOWN);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
uiHandler.processInput(Button.RIGHT);
|
uiHandler.processInput(Button.RIGHT);
|
||||||
uiHandler.processInput(Button.DOWN);
|
uiHandler.processInput(Button.DOWN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
|
||||||
await game.phaseInterceptor.run(MysteryEncounterPhase);
|
|
||||||
|
|
||||||
// run the selected options phase
|
// run the selected options phase
|
||||||
game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => {
|
game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => {
|
||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
|
||||||
|
// If a battle is started, fast forward to end of the battle
|
||||||
|
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||||
|
game.scene.clearPhaseQueue();
|
||||||
|
game.scene.clearPhaseQueueSplice();
|
||||||
|
game.scene.unshiftPhase(new VictoryPhase(game.scene, 0));
|
||||||
|
game.endPhase();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle end of battle trainer messages
|
||||||
|
game.onNextPrompt("TrainerVictoryPhase", Mode.MESSAGE, () => {
|
||||||
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
|
uiHandler.processInput(Button.ACTION);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle egg hatch dialogue
|
||||||
|
game.onNextPrompt("EggLapsePhase", Mode.MESSAGE, () => {
|
||||||
|
const uiHandler = game.scene.ui.getHandler<MessageUiHandler>();
|
||||||
|
uiHandler.processInput(Button.ACTION);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isBattle) {
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
} else {
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For any MysteryEncounter that has a battle, can call this to skip battle and proceed to MysteryEncounterRewardsPhase
|
||||||
|
* @param game
|
||||||
|
*/
|
||||||
|
export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager) {
|
||||||
|
game.scene.clearPhaseQueue();
|
||||||
|
game.scene.clearPhaseQueueSplice();
|
||||||
|
game.scene.getEnemyParty().forEach(p => {
|
||||||
|
p.hp = 0;
|
||||||
|
p.status = new Status(StatusEffect.FAINT);
|
||||||
|
game.scene.field.remove(p);
|
||||||
|
});
|
||||||
|
game.scene.pushPhase(new VictoryPhase(game.scene, 0));
|
||||||
|
game.phaseInterceptor.superEndPhase();
|
||||||
|
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, true);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,281 @@
|
|||||||
|
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { Biome } from "#app/enums/biome";
|
||||||
|
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#app/enums/species";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter";
|
||||||
|
import Battle from "#app/battle";
|
||||||
|
import { Gender } from "#app/data/gender";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import * as BattleAnims from "#app/data/battle-anims";
|
||||||
|
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounterTestUtils";
|
||||||
|
import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||||
|
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
|
const namespace = "mysteryEncounter:fieryFallout";
|
||||||
|
/** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */
|
||||||
|
const defaultParty = [Species.ARCANINE, Species.NINETALES, Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||||
|
const defaultBiome = Biome.VOLCANO;
|
||||||
|
const defaultWave = 45;
|
||||||
|
|
||||||
|
describe("Fiery Fallout - Mystery Encounter", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
let scene: BattleScene;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
scene = game.scene;
|
||||||
|
game.override.mysteryEncounterChance(100);
|
||||||
|
game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||||
|
game.override.startingWave(defaultWave);
|
||||||
|
game.override.startingBiome(defaultBiome);
|
||||||
|
game.override.disableTrainerWave(true);
|
||||||
|
|
||||||
|
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||||
|
new Map<Biome, MysteryEncounterType[]>([
|
||||||
|
[Biome.VOLCANO, [MysteryEncounterType.FIERY_FALLOUT]],
|
||||||
|
[Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
|
||||||
|
expect(FieryFalloutEncounter.encounterType).toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
expect(FieryFalloutEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||||
|
expect(FieryFalloutEncounter.dialogue).toBeDefined();
|
||||||
|
expect(FieryFalloutEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||||
|
expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
|
||||||
|
expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
|
||||||
|
expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
|
||||||
|
expect(FieryFalloutEncounter.options.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not spawn outside of volcano biome", async () => {
|
||||||
|
game.override.startingBiome(Biome.MOUNTAIN);
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run below wave 41", async () => {
|
||||||
|
game.override.startingWave(38);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run above wave 179", async () => {
|
||||||
|
game.override.startingWave(181);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should initialize fully ", async () => {
|
||||||
|
vi.spyOn(scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle);
|
||||||
|
const weatherSpy = vi.spyOn(scene.arena, "trySetWeather").mockReturnValue(true);
|
||||||
|
const moveInitSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
|
||||||
|
const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
|
||||||
|
|
||||||
|
const { onInit } = FieryFalloutEncounter;
|
||||||
|
|
||||||
|
expect(FieryFalloutEncounter.onInit).toBeDefined();
|
||||||
|
|
||||||
|
const onInitResult = onInit(scene);
|
||||||
|
|
||||||
|
expect(FieryFalloutEncounter.enemyPartyConfigs).toEqual([
|
||||||
|
{
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.VOLCARONA),
|
||||||
|
isBoss: false,
|
||||||
|
gender: Gender.MALE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.VOLCARONA),
|
||||||
|
isBoss: false,
|
||||||
|
gender: Gender.FEMALE
|
||||||
|
}
|
||||||
|
],
|
||||||
|
doubleBattle: true,
|
||||||
|
disableSwitch: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
expect(weatherSpy).toHaveBeenCalledTimes(1);
|
||||||
|
await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled());
|
||||||
|
await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled());
|
||||||
|
expect(onInitResult).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 1 - Fight 2 Volcarona", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option1 = FieryFalloutEncounter.options[0];
|
||||||
|
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||||
|
expect(option1.dialogue).toBeDefined();
|
||||||
|
expect(option1.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:1:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start battle against 2 Volcarona", async () => {
|
||||||
|
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
await runSelectMysteryEncounterOption(game, 1, true);
|
||||||
|
|
||||||
|
const enemyField = scene.getEnemyField();
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||||
|
expect(enemyField.length).toBe(2);
|
||||||
|
expect(enemyField[0].species.speciesId).toBe(Species.VOLCARONA);
|
||||||
|
expect(enemyField[1].species.speciesId).toBe(Species.VOLCARONA);
|
||||||
|
expect(enemyField[0].gender).not.toEqual(enemyField[1].gender); // Should be opposite gender
|
||||||
|
|
||||||
|
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
|
||||||
|
expect(movePhases.length).toBe(4);
|
||||||
|
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.FIRE_SPIN).length).toBe(2); // Fire spin used twice before battle
|
||||||
|
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.QUIVER_DANCE).length).toBe(2); // Quiver Dance used twice before battle
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should give charcoal to lead pokemon", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
await runSelectMysteryEncounterOption(game, 1, true);
|
||||||
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
|
const leadPokemonId = scene.getParty()?.[0].id;
|
||||||
|
const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[];
|
||||||
|
const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal");
|
||||||
|
expect(charcoal).toBeDefined;
|
||||||
|
}, 100000000);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 2 - Suffer the weather", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option1 = FieryFalloutEncounter.options[1];
|
||||||
|
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||||
|
expect(option1.dialogue).toBeDefined();
|
||||||
|
expect(option1.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:2:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should damage all non-fire party PKM by 20% and randomly burn 1", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
|
||||||
|
const party = scene.getParty();
|
||||||
|
const lapras = party.find((pkm) => pkm.species.speciesId === Species.LAPRAS);
|
||||||
|
lapras.status = new Status(StatusEffect.POISON);
|
||||||
|
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA);
|
||||||
|
vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false);
|
||||||
|
|
||||||
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
|
const burnablePokemon = party.filter((pkm) => pkm.isAllowedInBattle() && !pkm.getTypes().includes(Type.FIRE));
|
||||||
|
const notBurnablePokemon = party.filter((pkm) => !pkm.isAllowedInBattle() || pkm.getTypes().includes(Type.FIRE));
|
||||||
|
expect(scene.currentBattle.mysteryEncounter.dialogueTokens["burnedPokemon"]).toBe("Gengar");
|
||||||
|
burnablePokemon.forEach((pkm) => {
|
||||||
|
expect(pkm.hp, `${pkm.name} should have received 20% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.2));
|
||||||
|
});
|
||||||
|
expect(burnablePokemon.some(pkm => pkm?.status?.effect === StatusEffect.BURN)).toBeTruthy();
|
||||||
|
notBurnablePokemon.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp()));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 3 - use FIRE types", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option1 = FieryFalloutEncounter.options[2];
|
||||||
|
expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_SPECIAL);
|
||||||
|
expect(option1.dialogue).toBeDefined();
|
||||||
|
expect(option1.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}:option:3:disabled_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}:option:3:selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should give charcoal to lead pokemon", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
await runSelectMysteryEncounterOption(game, 3);
|
||||||
|
// await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
|
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
|
const leadPokemonId = scene.getParty()?.[0].id;
|
||||||
|
const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[];
|
||||||
|
const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal");
|
||||||
|
expect(charcoal).toBeDefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
|
||||||
|
await runSelectMysteryEncounterOption(game, 3);
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -9,11 +9,11 @@ import { Moves } from "#app/enums/moves";
|
|||||||
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||||
import { Species } from "#app/enums/species";
|
import { Species } from "#app/enums/species";
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
import { workaround_reInitSceneWithOverrides } from "#app/test/utils/testUtils";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { runSelectMysteryEncounterOption } from "../encounterTestUtils";
|
import { runSelectMysteryEncounterOption } from "../encounterTestUtils";
|
||||||
|
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
const namepsace = "mysteryEncounter:lostAtSea";
|
const namespace = "mysteryEncounter:lostAtSea";
|
||||||
/** Blastoise for surf. Pidgeot for fly. Abra for none. */
|
/** Blastoise for surf. Pidgeot for fly. Abra for none. */
|
||||||
const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA];
|
const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA];
|
||||||
const defaultBiome = Biome.SEA;
|
const defaultBiome = Biome.SEA;
|
||||||
@ -30,8 +30,10 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.mysteryEncounterChance(100);
|
game.override.mysteryEncounterChance(100);
|
||||||
game.override.startingBiome(defaultBiome);
|
|
||||||
game.override.startingWave(defaultWave);
|
game.override.startingWave(defaultWave);
|
||||||
|
game.override.startingBiome(defaultBiome);
|
||||||
|
game.override.disableTrainerWave(true);
|
||||||
|
|
||||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||||
new Map<Biome, MysteryEncounterType[]>([
|
new Map<Biome, MysteryEncounterType[]>([
|
||||||
[Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]],
|
[Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]],
|
||||||
@ -45,28 +47,28 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should have the correct properties", async () => {
|
it("should have the correct properties", async () => {
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
|
|
||||||
expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA);
|
expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA);
|
||||||
|
expect(LostAtSeaEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||||
expect(LostAtSeaEncounter.dialogue).toBeDefined();
|
expect(LostAtSeaEncounter.dialogue).toBeDefined();
|
||||||
expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namepsace}:intro` }]);
|
expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||||
expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namepsace}:title`);
|
expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
|
||||||
expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namepsace}:description`);
|
expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
|
||||||
expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namepsace}:query`);
|
expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
|
||||||
expect(LostAtSeaEncounter.options.length).toBe(3);
|
expect(LostAtSeaEncounter.options.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not spawn outside of sea biome", async () => {
|
it("should not spawn outside of sea biome", async () => {
|
||||||
game.override.startingBiome(Biome.MOUNTAIN);
|
game.override.startingBiome(Biome.MOUNTAIN);
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
|
||||||
await game.runToMysteryEncounter();
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA);
|
expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not run below wave 11", async () => {
|
it("should not run below wave 11", async () => {
|
||||||
game.override.startingWave(10);
|
game.override.startingWave(9);
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not run above wave 179", async () => {
|
it("should not run above wave 179", async () => {
|
||||||
game.override.startingWave(180);
|
game.override.startingWave(181);
|
||||||
|
|
||||||
await game.runToMysteryEncounter();
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
@ -97,18 +99,22 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Option 1 - Surf", () => {
|
describe("Option 1 - Surf", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||||
|
});
|
||||||
|
|
||||||
it("should have the correct properties", () => {
|
it("should have the correct properties", () => {
|
||||||
const option1 = LostAtSeaEncounter.options[0];
|
const option1 = LostAtSeaEncounter.options[0];
|
||||||
expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
|
expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||||
expect(option1.dialogue).toBeDefined();
|
expect(option1.dialogue).toBeDefined();
|
||||||
expect(option1.dialogue).toStrictEqual({
|
expect(option1.dialogue).toStrictEqual({
|
||||||
buttonLabel: `${namepsace}:option:1:label`,
|
buttonLabel: `${namespace}:option:1:label`,
|
||||||
disabledButtonLabel: `${namepsace}:option:1:label_disabled`,
|
disabledButtonLabel: `${namespace}:option:1:label_disabled`,
|
||||||
buttonTooltip: `${namepsace}:option:1:tooltip`,
|
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||||
disabledButtonTooltip: `${namepsace}:option:1:tooltip_disabled`,
|
disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namepsace}:option:1:selected`,
|
text: `${namespace}:option:1:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -117,23 +123,21 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
it("should award exp to surfable PKM (Blastoise)", async () => {
|
it("should award exp to surfable PKM (Blastoise)", async () => {
|
||||||
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
|
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
|
||||||
|
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
|
||||||
const party = game.scene.getParty();
|
const party = game.scene.getParty();
|
||||||
const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT);
|
const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT);
|
||||||
const expBefore = blastoise.exp;
|
const expBefore = blastoise.exp;
|
||||||
|
|
||||||
await runSelectMysteryEncounterOption(game, 2);
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
expect(blastoise.exp).toBe(expBefore + laprasSpecies.baseExp * defaultWave);
|
expect(blastoise.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should leave encounter without battle", async () => {
|
it("should leave encounter without battle", async () => {
|
||||||
game.override.startingWave(33);
|
game.override.startingWave(33);
|
||||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
|
||||||
await runSelectMysteryEncounterOption(game, 1);
|
await runSelectMysteryEncounterOption(game, 1);
|
||||||
|
|
||||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
@ -145,19 +149,23 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Option 2 - Fly", () => {
|
describe("Option 2 - Fly", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||||
|
});
|
||||||
|
|
||||||
it("should have the correct properties", () => {
|
it("should have the correct properties", () => {
|
||||||
const option2 = LostAtSeaEncounter.options[1];
|
const option2 = LostAtSeaEncounter.options[1];
|
||||||
|
|
||||||
expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
|
expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||||
expect(option2.dialogue).toBeDefined();
|
expect(option2.dialogue).toBeDefined();
|
||||||
expect(option2.dialogue).toStrictEqual({
|
expect(option2.dialogue).toStrictEqual({
|
||||||
buttonLabel: `${namepsace}:option:2:label`,
|
buttonLabel: `${namespace}:option:2:label`,
|
||||||
disabledButtonLabel: `${namepsace}:option:2:label_disabled`,
|
disabledButtonLabel: `${namespace}:option:2:label_disabled`,
|
||||||
buttonTooltip: `${namepsace}:option:2:tooltip`,
|
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||||
disabledButtonTooltip: `${namepsace}:option:2:tooltip_disabled`,
|
disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namepsace}:option:2:selected`,
|
text: `${namespace}:option:2:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -168,23 +176,21 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
const wave = 33;
|
const wave = 33;
|
||||||
game.override.startingWave(wave);
|
game.override.startingWave(wave);
|
||||||
|
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
|
||||||
const party = game.scene.getParty();
|
const party = game.scene.getParty();
|
||||||
const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT);
|
const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT);
|
||||||
const expBefore = pidgeot.exp;
|
const expBefore = pidgeot.exp;
|
||||||
|
|
||||||
await runSelectMysteryEncounterOption(game, 2);
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
expect(pidgeot.exp).toBe(expBefore + laprasBaseExp * wave);
|
expect(pidgeot.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should leave encounter without battle", async () => {
|
it("should leave encounter without battle", async () => {
|
||||||
game.override.startingWave(33);
|
game.override.startingWave(33);
|
||||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
|
||||||
await runSelectMysteryEncounterOption(game, 2);
|
await runSelectMysteryEncounterOption(game, 2);
|
||||||
|
|
||||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
@ -196,17 +202,21 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Option 3 - Wander aimlessy", () => {
|
describe("Option 3 - Wander aimlessy", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||||
|
});
|
||||||
|
|
||||||
it("should have the correct properties", () => {
|
it("should have the correct properties", () => {
|
||||||
const option3 = LostAtSeaEncounter.options[2];
|
const option3 = LostAtSeaEncounter.options[2];
|
||||||
|
|
||||||
expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||||
expect(option3.dialogue).toBeDefined();
|
expect(option3.dialogue).toBeDefined();
|
||||||
expect(option3.dialogue).toStrictEqual({
|
expect(option3.dialogue).toStrictEqual({
|
||||||
buttonLabel: `${namepsace}:option:3:label`,
|
buttonLabel: `${namespace}:option:3:label`,
|
||||||
buttonTooltip: `${namepsace}:option:3:tooltip`,
|
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||||
selected: [
|
selected: [
|
||||||
{
|
{
|
||||||
text: `${namepsace}:option:3:selected`,
|
text: `${namespace}:option:3:selected`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -215,8 +225,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
it("should damage all (allowed in battle) party PKM by 25%", async () => {
|
it("should damage all (allowed in battle) party PKM by 25%", async () => {
|
||||||
game.override.startingWave(33);
|
game.override.startingWave(33);
|
||||||
|
|
||||||
await workaround_reInitSceneWithOverrides(game);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
|
||||||
|
|
||||||
const party = game.scene.getParty();
|
const party = game.scene.getParty();
|
||||||
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA);
|
const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA);
|
||||||
@ -237,8 +246,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||||||
game.override.startingWave(33);
|
game.override.startingWave(33);
|
||||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
workaround_reInitSceneWithOverrides(game);
|
await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty);
|
||||||
await game.runToMysteryEncounter(defaultParty);
|
|
||||||
await runSelectMysteryEncounterOption(game, 3);
|
await runSelectMysteryEncounterOption(game, 3);
|
||||||
|
|
||||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
@ -36,16 +36,12 @@ describe("Mystery Encounter Utils", () => {
|
|||||||
describe("getRandomPlayerPokemon", () => {
|
describe("getRandomPlayerPokemon", () => {
|
||||||
it("gets a random pokemon from player party", () => {
|
it("gets a random pokemon from player party", () => {
|
||||||
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
||||||
scene.waveSeed = "random";
|
game.override.seed("random");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
let result = getRandomPlayerPokemon(scene);
|
let result = getRandomPlayerPokemon(scene);
|
||||||
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
||||||
|
|
||||||
scene.waveSeed = "random2";
|
game.override.seed("random2");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
result = getRandomPlayerPokemon(scene);
|
result = getRandomPlayerPokemon(scene);
|
||||||
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
||||||
@ -60,16 +56,12 @@ describe("Mystery Encounter Utils", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
||||||
scene.waveSeed = "random";
|
game.override.seed("random");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
let result = getRandomPlayerPokemon(scene);
|
let result = getRandomPlayerPokemon(scene);
|
||||||
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
||||||
|
|
||||||
scene.waveSeed = "random2";
|
game.override.seed("random2");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
result = getRandomPlayerPokemon(scene);
|
result = getRandomPlayerPokemon(scene);
|
||||||
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
||||||
@ -83,16 +75,12 @@ describe("Mystery Encounter Utils", () => {
|
|||||||
party[0].updateInfo();
|
party[0].updateInfo();
|
||||||
|
|
||||||
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
||||||
scene.waveSeed = "random";
|
game.override.seed("random");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
let result = getRandomPlayerPokemon(scene, true);
|
let result = getRandomPlayerPokemon(scene, true);
|
||||||
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
||||||
|
|
||||||
scene.waveSeed = "random2";
|
game.override.seed("random2");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
result = getRandomPlayerPokemon(scene, true);
|
result = getRandomPlayerPokemon(scene, true);
|
||||||
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
||||||
@ -106,16 +94,12 @@ describe("Mystery Encounter Utils", () => {
|
|||||||
party[0].updateInfo();
|
party[0].updateInfo();
|
||||||
|
|
||||||
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
||||||
scene.waveSeed = "random";
|
game.override.seed("random");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
let result = getRandomPlayerPokemon(scene, true, false);
|
let result = getRandomPlayerPokemon(scene, true, false);
|
||||||
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
||||||
|
|
||||||
scene.waveSeed = "random2";
|
game.override.seed("random2");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
result = getRandomPlayerPokemon(scene, true, false);
|
result = getRandomPlayerPokemon(scene, true, false);
|
||||||
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
expect(result.species.speciesId).toBe(Species.MANAPHY);
|
||||||
@ -129,16 +113,12 @@ describe("Mystery Encounter Utils", () => {
|
|||||||
party[0].updateInfo();
|
party[0].updateInfo();
|
||||||
|
|
||||||
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
// Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal)
|
||||||
scene.waveSeed = "random";
|
game.override.seed("random");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
let result = getRandomPlayerPokemon(scene, true, true);
|
let result = getRandomPlayerPokemon(scene, true, true);
|
||||||
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
||||||
|
|
||||||
scene.waveSeed = "random2";
|
game.override.seed("random2");
|
||||||
Phaser.Math.RND.sow([scene.waveSeed]);
|
|
||||||
scene.rngCounter = 0;
|
|
||||||
|
|
||||||
result = getRandomPlayerPokemon(scene, true, true);
|
result = getRandomPlayerPokemon(scene, true, true);
|
||||||
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
expect(result.species.speciesId).toBe(Species.ARCEUS);
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { afterEach, beforeAll, beforeEach, expect, describe, it, vi } from "vitest";
|
import { afterEach, beforeAll, beforeEach, expect, describe, it } from "vitest";
|
||||||
import * as overrides from "../../overrides";
|
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase";
|
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
|
||||||
describe("Mystery Encounters", () => {
|
describe("Mystery Encounters", () => {
|
||||||
@ -22,33 +21,21 @@ describe("Mystery Encounters", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256);
|
game.override.startingWave(11);
|
||||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11);
|
game.override.mysteryEncounterChance(100);
|
||||||
vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
||||||
|
game.override.disableTrainerWave(true);
|
||||||
// Seed guarantees wild encounter to be replaced by ME
|
|
||||||
vi.spyOn(game.scene, "resetSeed").mockImplementation(() => {
|
|
||||||
game.scene.waveSeed = "test";
|
|
||||||
Phaser.Math.RND.sow([game.scene.waveSeed]);
|
|
||||||
game.scene.rngCounter = 0;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Spawns a mystery encounter", async () => {
|
it("Spawns a mystery encounter", async () => {
|
||||||
await game.runToMysteryEncounter([
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]);
|
||||||
Species.CHARIZARD,
|
|
||||||
Species.VOLCARONA
|
|
||||||
]);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("", async () => {
|
it("", async () => {
|
||||||
await game.runToMysteryEncounter([
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]);
|
||||||
Species.CHARIZARD,
|
|
||||||
Species.VOLCARONA
|
|
||||||
]);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import {afterEach, beforeAll, beforeEach, expect, describe, it, vi} from "vitest";
|
import {afterEach, beforeAll, beforeEach, expect, describe, it, vi } from "vitest";
|
||||||
import * as overrides from "../../overrides";
|
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import {Species} from "#enums/species";
|
import {Species} from "#enums/species";
|
||||||
import {MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase";
|
import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
import {Mode} from "#app/ui/ui";
|
import {Mode} from "#app/ui/ui";
|
||||||
import {Button} from "#enums/buttons";
|
import {Button} from "#enums/buttons";
|
||||||
import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler";
|
import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler";
|
||||||
import {MysteryEncounterType} from "#enums/mystery-encounter-type";
|
import {MysteryEncounterType} from "#enums/mystery-encounter-type";
|
||||||
import {MysteryEncounterTier} from "#app/data/mystery-encounters/mystery-encounter";
|
import {MysteryEncounterTier} from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
import MessageUiHandler from "#app/ui/message-ui-handler";
|
||||||
|
|
||||||
describe("Mystery Encounter Phases", () => {
|
describe("Mystery Encounter Phases", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -26,34 +26,23 @@ describe("Mystery Encounter Phases", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256);
|
game.override.startingWave(11);
|
||||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11);
|
game.override.mysteryEncounterChance(100);
|
||||||
vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
||||||
|
|
||||||
// Seed guarantees wild encounter to be replaced by ME
|
// Seed guarantees wild encounter to be replaced by ME
|
||||||
vi.spyOn(game.scene, "resetSeed").mockImplementation(() => {
|
game.override.seed("test");
|
||||||
game.scene.waveSeed = "test";
|
|
||||||
Phaser.Math.RND.sow([ game.scene.waveSeed ]);
|
|
||||||
game.scene.rngCounter = 0;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("MysteryEncounterPhase", () => {
|
describe("MysteryEncounterPhase", () => {
|
||||||
it("Runs to MysteryEncounterPhase", async() => {
|
it("Runs to MysteryEncounterPhase", async() => {
|
||||||
await game.runToMysteryEncounter([
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]);
|
||||||
Species.CHARIZARD,
|
|
||||||
Species.VOLCARONA
|
|
||||||
]);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Runs MysteryEncounterPhase", async() => {
|
it("Runs MysteryEncounterPhase", async() => {
|
||||||
await game.runToMysteryEncounter([
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]);
|
||||||
Species.CHARIZARD,
|
|
||||||
Species.VOLCARONA
|
|
||||||
]);
|
|
||||||
|
|
||||||
game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => {
|
game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => {
|
||||||
// End phase early for test
|
// End phase early for test
|
||||||
@ -70,27 +59,28 @@ describe("Mystery Encounter Phases", () => {
|
|||||||
it("Selects an option for MysteryEncounterPhase", async() => {
|
it("Selects an option for MysteryEncounterPhase", async() => {
|
||||||
const dialogueSpy = vi.spyOn(game.scene.ui, "showDialogue");
|
const dialogueSpy = vi.spyOn(game.scene.ui, "showDialogue");
|
||||||
const messageSpy = vi.spyOn(game.scene.ui, "showText");
|
const messageSpy = vi.spyOn(game.scene.ui, "showText");
|
||||||
await game.runToMysteryEncounter([
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]);
|
||||||
Species.CHARIZARD,
|
|
||||||
Species.VOLCARONA
|
|
||||||
]);
|
|
||||||
|
|
||||||
game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => {
|
game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => {
|
||||||
// Select option 1 for encounter
|
const handler = game.scene.ui.getHandler() as MessageUiHandler;
|
||||||
const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler;
|
|
||||||
handler.unblockInput();
|
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
}, () => !game.isCurrentPhase(MysteryEncounterPhase));
|
});
|
||||||
|
|
||||||
await game.phaseInterceptor.run(MysteryEncounterPhase);
|
await game.phaseInterceptor.run(MysteryEncounterPhase);
|
||||||
|
|
||||||
// After option selected
|
// Select option 1 for encounter
|
||||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name);
|
const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler;
|
||||||
|
handler.unblockInput();
|
||||||
|
handler.processInput(Button.ACTION);
|
||||||
|
|
||||||
|
// Waitfor required so that option select messages and preOptionPhase logic are handled
|
||||||
|
await vi.waitFor(() => expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name));
|
||||||
expect(game.scene.ui.getMode()).toBe(Mode.MESSAGE);
|
expect(game.scene.ui.getMode()).toBe(Mode.MESSAGE);
|
||||||
expect(dialogueSpy).toHaveBeenCalledTimes(1);
|
expect(dialogueSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(messageSpy).toHaveBeenCalledTimes(2);
|
expect(messageSpy).toHaveBeenCalledTimes(2);
|
||||||
expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function));
|
expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function));
|
||||||
expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 750, true);
|
expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 750, true);
|
||||||
expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 750, true);
|
expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 300, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Class will intercept any text or dialogue message calls and log them for test purposes
|
||||||
|
*/
|
||||||
export default class TextInterceptor {
|
export default class TextInterceptor {
|
||||||
private scene;
|
private scene;
|
||||||
public logs = [];
|
public logs = [];
|
||||||
@ -11,6 +14,11 @@ export default class TextInterceptor {
|
|||||||
this.logs.push(text);
|
this.logs.push(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showDialogue(text: string, name: string, delay?: integer, callback?: Function, callbackDelay?: integer, promptDelay?: integer): void {
|
||||||
|
console.log(name, text);
|
||||||
|
this.logs.push(name, text);
|
||||||
|
}
|
||||||
|
|
||||||
getLatestMessage(): string {
|
getLatestMessage(): string {
|
||||||
return this.logs.pop();
|
return this.logs.pop();
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,10 @@ import { Button } from "#enums/buttons";
|
|||||||
import { BattlerIndex } from "#app/battle.js";
|
import { BattlerIndex } from "#app/battle.js";
|
||||||
import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js";
|
import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js";
|
||||||
import BattleMessageUiHandler from "#app/ui/battle-message-ui-handler";
|
import BattleMessageUiHandler from "#app/ui/battle-message-ui-handler";
|
||||||
import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase";
|
import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phases";
|
||||||
import { OverridesHelper } from "./overridesHelper";
|
import { OverridesHelper } from "./overridesHelper";
|
||||||
|
import { expect } from "vitest";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to manage the game state and transitions between phases.
|
* Class to manage the game state and transitions between phases.
|
||||||
@ -62,7 +64,7 @@ export default class GameManager {
|
|||||||
this.phaseInterceptor = new PhaseInterceptor(this.scene);
|
this.phaseInterceptor = new PhaseInterceptor(this.scene);
|
||||||
this.textInterceptor = new TextInterceptor(this.scene);
|
this.textInterceptor = new TextInterceptor(this.scene);
|
||||||
this.gameWrapper.setScene(this.scene);
|
this.gameWrapper.setScene(this.scene);
|
||||||
this.override = new OverridesHelper();
|
this.override = new OverridesHelper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,10 +146,11 @@ export default class GameManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the game to a mystery encounter phase.
|
* Runs the game to a mystery encounter phase.
|
||||||
|
* @param encounterType - if specified, will expect encounter to have been spawned
|
||||||
* @param species - Optional array of species for party.
|
* @param species - Optional array of species for party.
|
||||||
* @returns A promise that resolves when the EncounterPhase ends.
|
* @returns A promise that resolves when the EncounterPhase ends.
|
||||||
*/
|
*/
|
||||||
async runToMysteryEncounter(species?: Species[]) {
|
async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) {
|
||||||
await this.runToTitle();
|
await this.runToTitle();
|
||||||
|
|
||||||
this.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
this.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||||
@ -164,6 +167,9 @@ export default class GameManager {
|
|||||||
}, () => this.isCurrentPhase(MysteryEncounterPhase), true);
|
}, () => this.isCurrentPhase(MysteryEncounterPhase), true);
|
||||||
|
|
||||||
await this.phaseInterceptor.run(EncounterPhase);
|
await this.phaseInterceptor.run(EncounterPhase);
|
||||||
|
if (encounterType) {
|
||||||
|
expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,6 +154,10 @@ export default class MockContainer {
|
|||||||
// Sends this Game Object to the back of its parent's display list.
|
// Sends this Game Object to the back of its parent's display list.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveTo(obj) {
|
||||||
|
// Moves this Game Object to the given index in the list.
|
||||||
|
}
|
||||||
|
|
||||||
moveAbove(obj) {
|
moveAbove(obj) {
|
||||||
// Moves this Game Object to be above the given Game Object in the display list.
|
// Moves this Game Object to be above the given Game Object in the display list.
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ export default class MockText {
|
|||||||
// Phaser.GameObjects.Text.prototype.updateText = () => null;
|
// Phaser.GameObjects.Text.prototype.updateText = () => null;
|
||||||
// Phaser.Textures.TextureManager.prototype.addCanvas = () => {};
|
// Phaser.Textures.TextureManager.prototype.addCanvas = () => {};
|
||||||
UI.prototype.showText = this.showText;
|
UI.prototype.showText = this.showText;
|
||||||
|
UI.prototype.showDialogue = this.showDialogue;
|
||||||
// super(scene, x, y);
|
// super(scene, x, y);
|
||||||
// this.phaserText = new Phaser.GameObjects.Text(scene, x, y, content, styleOptions);
|
// this.phaserText = new Phaser.GameObjects.Text(scene, x, y, content, styleOptions);
|
||||||
}
|
}
|
||||||
@ -79,6 +80,13 @@ export default class MockText {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showDialogue(text, name, delay, callback, callbackDelay, promptDelay) {
|
||||||
|
this.scene.messageWrapper.showDialogue(text, name, delay, callback, callbackDelay, promptDelay);
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setScale(scale) {
|
setScale(scale) {
|
||||||
// return this.phaserText.setScale(scale);
|
// return this.phaserText.setScale(scale);
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,113 @@
|
|||||||
import { Weather, WeatherType } from "#app/data/weather";
|
import { Weather, WeatherType } from "#app/data/weather";
|
||||||
import { Biome } from "#app/enums/biome";
|
import { Biome } from "#app/enums/biome";
|
||||||
import * as Overrides from "#app/overrides";
|
import * as Overrides from "#app/overrides";
|
||||||
import { vi } from "vitest";
|
import { MockInstance, vi } from "vitest";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import * as overrides from "#app/overrides";
|
||||||
|
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to handle overrides in tests
|
* Helper to handle overrides in tests
|
||||||
*/
|
*/
|
||||||
export class OverridesHelper {
|
export class OverridesHelper {
|
||||||
constructor() {}
|
game: GameManager;
|
||||||
|
constructor(game: GameManager) {
|
||||||
|
this.game = game;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the encounter chance for a mystery encounter.
|
* Override the encounter chance for a mystery encounter.
|
||||||
* @param percentage the encounter chance in %
|
* @param percentage the encounter chance in %
|
||||||
|
* @returns spy instance
|
||||||
*/
|
*/
|
||||||
mysteryEncounterChance(percentage: number) {
|
mysteryEncounterChance(percentage: number) {
|
||||||
const maxRate: number = 256; // 100%
|
const maxRate: number = 256; // 100%
|
||||||
const rate = maxRate * (percentage / 100);
|
const rate = maxRate * (percentage / 100);
|
||||||
vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate);
|
const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate);
|
||||||
this.log(`Mystery encounter chance set to ${percentage}% (=${rate})!`);
|
this.log(`Mystery encounter chance set to ${percentage}% (=${rate})!`);
|
||||||
|
return spy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the encounter chance for a mystery encounter.
|
||||||
|
* @returns spy instance
|
||||||
|
* @param tier
|
||||||
|
*/
|
||||||
|
mysteryEncounterTier(tier: MysteryEncounterTier): MockInstance {
|
||||||
|
const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_TIER_OVERRIDE", "get").mockReturnValue(tier);
|
||||||
|
this.log(`Mystery encounter tier set to ${tier}!`);
|
||||||
|
return spy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the encounter that spawns for the scene
|
||||||
|
* @param encounterType
|
||||||
|
* @returns spy instance
|
||||||
|
*/
|
||||||
|
mysteryEncounter(encounterType: MysteryEncounterType) {
|
||||||
|
const spy = vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(encounterType);
|
||||||
|
this.log(`Mystery encounter override set to ${encounterType}!`);
|
||||||
|
return spy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the starting biome
|
* Override the starting biome
|
||||||
* @warning The biome will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils)
|
* @warning Any event listeners that are attached to [NewArenaEvent](events\battle-scene.ts) may need to be handled down the line
|
||||||
* @param biome the biome to set
|
* @param biome the biome to set
|
||||||
*/
|
*/
|
||||||
startingBiome(biome: Biome) {
|
startingBiome(biome: Biome) {
|
||||||
vi.spyOn(Overrides, "STARTING_BIOME_OVERRIDE", "get").mockReturnValue(biome);
|
this.game.scene.newArena(biome);
|
||||||
this.log(`Starting biome set to ${Biome[biome]} (=${biome})!`);
|
this.log(`Starting biome set to ${Biome[biome]} (=${biome})!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the starting wave (index)
|
* Override the starting wave (index)
|
||||||
* @param wave the wave (index) to set. Classic: `1`-`200`
|
* @param wave the wave (index) to set. Classic: `1`-`200`
|
||||||
|
* @returns spy instance
|
||||||
*/
|
*/
|
||||||
startingWave(wave: number) {
|
startingWave(wave: number) {
|
||||||
vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(wave);
|
const spy = vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(wave);
|
||||||
this.log(`Starting wave set to ${wave}!`);
|
this.log(`Starting wave set to ${wave}!`);
|
||||||
|
return spy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override each wave to have or not have standard trainer battles
|
||||||
|
* @returns spy instance
|
||||||
|
* @param disable - true
|
||||||
|
*/
|
||||||
|
disableTrainerWave(disable: boolean): MockInstance {
|
||||||
|
const spy = vi.spyOn(this.game.scene.gameMode, "isWaveTrainer").mockReturnValue(!disable);
|
||||||
|
this.log(`Standard trainer waves are ${disable? "disabled" : "enabled"}!`);
|
||||||
|
return spy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the weather (type)
|
* Override the weather (type)
|
||||||
* @param type weather type to set
|
* @param type weather type to set
|
||||||
|
* @returns spy instance
|
||||||
*/
|
*/
|
||||||
weather(type: WeatherType) {
|
weather(type: WeatherType) {
|
||||||
vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type);
|
const spy = vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type);
|
||||||
this.log(`Weather set to ${Weather[type]} (=${type})!`);
|
this.log(`Weather set to ${Weather[type]} (=${type})!`);
|
||||||
|
return spy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the seed
|
* Override the seed
|
||||||
* @warning The seed will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils)
|
|
||||||
* @param seed the seed to set
|
* @param seed the seed to set
|
||||||
|
* @returns spy instance
|
||||||
*/
|
*/
|
||||||
seed(seed: string) {
|
seed(seed: string) {
|
||||||
vi.spyOn(Overrides, "SEED_OVERRIDE", "get").mockReturnValue(seed);
|
const spy = vi.spyOn(this.game.scene, "resetSeed").mockImplementation(() => {
|
||||||
|
this.game.scene.waveSeed = seed;
|
||||||
|
Phaser.Math.RND.sow([seed]);
|
||||||
|
this.game.scene.rngCounter = 0;
|
||||||
|
});
|
||||||
|
this.game.scene.resetSeed();
|
||||||
this.log(`Seed set to "${seed}"!`);
|
this.log(`Seed set to "${seed}"!`);
|
||||||
|
return spy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private log(...params: any[]) {
|
private log(...params: any[]) {
|
||||||
|
@ -44,7 +44,7 @@ import {
|
|||||||
MysteryEncounterPhase,
|
MysteryEncounterPhase,
|
||||||
MysteryEncounterRewardsPhase,
|
MysteryEncounterRewardsPhase,
|
||||||
PostMysteryEncounterPhase
|
PostMysteryEncounterPhase
|
||||||
} from "#app/phases/mystery-encounter-phase";
|
} from "#app/phases/mystery-encounter-phases";
|
||||||
|
|
||||||
export default class PhaseInterceptor {
|
export default class PhaseInterceptor {
|
||||||
public scene;
|
public scene;
|
||||||
@ -104,11 +104,12 @@ export default class PhaseInterceptor {
|
|||||||
[MysteryEncounterBattlePhase, this.startPhase],
|
[MysteryEncounterBattlePhase, this.startPhase],
|
||||||
[MysteryEncounterRewardsPhase, this.startPhase],
|
[MysteryEncounterRewardsPhase, this.startPhase],
|
||||||
[PostMysteryEncounterPhase, this.startPhase],
|
[PostMysteryEncounterPhase, this.startPhase],
|
||||||
[LearnMovePhase, this.startPhase]
|
[LearnMovePhase, this.startPhase],
|
||||||
|
// [CommonAnimPhase, this.startPhase]
|
||||||
];
|
];
|
||||||
|
|
||||||
private endBySetMode = [
|
private endBySetMode = [
|
||||||
TitlePhase, SelectGenderPhase, CommandPhase, SelectModifierPhase, PostMysteryEncounterPhase
|
TitlePhase, SelectGenderPhase, CommandPhase, SelectModifierPhase, MysteryEncounterPhase, PostMysteryEncounterPhase
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import i18next, { type ParseKeys } from "i18next";
|
import i18next, { type ParseKeys } from "i18next";
|
||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
import GameManager from "./gameManager";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the i18next mock.
|
* Sets up the i18next mock.
|
||||||
@ -22,15 +21,3 @@ export function mockI18next() {
|
|||||||
export function arrayOfRange(start: integer, end: integer) {
|
export function arrayOfRange(start: integer, end: integer) {
|
||||||
return Array.from({ length: end - start }, (_v, k) => k + start);
|
return Array.from({ length: end - start }, (_v, k) => k + start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Woraround to reinitialize the game scene with overrides being set properly.
|
|
||||||
* By default the scene is initialized without all overrides even having a chance to be applied.
|
|
||||||
* @warning USE AT YOUR OWN RISK! Might be deleted in the future
|
|
||||||
* @param game The game manager
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export async function workaround_reInitSceneWithOverrides(game: GameManager) {
|
|
||||||
await game.runToTitle();
|
|
||||||
game.gameWrapper.setScene(game.scene);
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@ import { Mode } from "./ui";
|
|||||||
import UiHandler from "./ui-handler";
|
import UiHandler from "./ui-handler";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { addWindow, WindowVariant } from "./ui-theme";
|
import { addWindow, WindowVariant } from "./ui-theme";
|
||||||
import { MysteryEncounterPhase } from "../phases/mystery-encounter-phase";
|
import { MysteryEncounterPhase } from "../phases/mystery-encounter-phases";
|
||||||
import { PartyUiMode } from "./party-ui-handler";
|
import { PartyUiMode } from "./party-ui-handler";
|
||||||
import MysteryEncounterOption, { EncounterOptionMode } from "../data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption, { EncounterOptionMode } from "../data/mystery-encounters/mystery-encounter-option";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
|
Loading…
Reference in New Issue
Block a user