Merge pull request #3787 from AsdarDevelops/mystery-encounters-beta
Moving MEs branch to main repo
|
@ -0,0 +1,951 @@
|
||||||
|
{
|
||||||
|
"id": 686,
|
||||||
|
"graphic": "PRAS- Dragon Dance",
|
||||||
|
"frames": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 4,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 12,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -12,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 12,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 16,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -16,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 24,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -20,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 32,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 24,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -24,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 36,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 28,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -28,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 36,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 36,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 32,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 24,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 12,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 4,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -4,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 12,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -12,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -12,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 16,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -16,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -24,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -20,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -32,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 24,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -24,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 155,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -36,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 28,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": -28,
|
||||||
|
"y": -0.5,
|
||||||
|
"zoomX": 108,
|
||||||
|
"zoomY": 100,
|
||||||
|
"mirror": true,
|
||||||
|
"visible": true,
|
||||||
|
"blendType": 1,
|
||||||
|
"target": 2,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 70,
|
||||||
|
"tone": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -36,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -36,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -32,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -24,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -12,
|
||||||
|
"y": -12,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": -4,
|
||||||
|
"y": -8,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"zoomX": 100,
|
||||||
|
"zoomY": 100,
|
||||||
|
"visible": true,
|
||||||
|
"target": 0,
|
||||||
|
"graphicFrame": 0,
|
||||||
|
"opacity": 255,
|
||||||
|
"locked": true,
|
||||||
|
"priority": 1,
|
||||||
|
"focus": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"frameTimedEvents": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"frameIndex": 0,
|
||||||
|
"resourceName": "PRSFX- Attract.wav",
|
||||||
|
"volume": 100,
|
||||||
|
"pitch": 100,
|
||||||
|
"eventType": "AnimTimedSoundEvent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1": [
|
||||||
|
{
|
||||||
|
"frameIndex": 0,
|
||||||
|
"resourceName": "PRSFX- Ally Switch.wav",
|
||||||
|
"volume": 80,
|
||||||
|
"pitch": 100,
|
||||||
|
"eventType": "AnimTimedSoundEvent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": 4,
|
||||||
|
"hue": 0
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 318 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 372 B |
After Width: | Height: | Size: 240 B |
|
@ -0,0 +1,734 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "b2w2_lady.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 399,
|
||||||
|
"h": 360
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 57,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 114,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 9,
|
||||||
|
"y": 8,
|
||||||
|
"w": 55,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 171,
|
||||||
|
"y": 0,
|
||||||
|
"w": 55,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 11,
|
||||||
|
"y": 8,
|
||||||
|
"w": 54,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 228,
|
||||||
|
"y": 0,
|
||||||
|
"w": 54,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 11,
|
||||||
|
"y": 8,
|
||||||
|
"w": 54,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 285,
|
||||||
|
"y": 0,
|
||||||
|
"w": 54,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 8,
|
||||||
|
"w": 52,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 342,
|
||||||
|
"y": 0,
|
||||||
|
"w": 52,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 72,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 47,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 57,
|
||||||
|
"y": 72,
|
||||||
|
"w": 47,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0009.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 47,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 114,
|
||||||
|
"y": 72,
|
||||||
|
"w": 47,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0010.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 171,
|
||||||
|
"y": 72,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0011.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 228,
|
||||||
|
"y": 72,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0012.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 285,
|
||||||
|
"y": 72,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0013.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 342,
|
||||||
|
"y": 72,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0014.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 144,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0015.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 57,
|
||||||
|
"y": 144,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0016.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 114,
|
||||||
|
"y": 144,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0017.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 171,
|
||||||
|
"y": 144,
|
||||||
|
"w": 49,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0018.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 228,
|
||||||
|
"y": 144,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0019.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 285,
|
||||||
|
"y": 144,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0020.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 342,
|
||||||
|
"y": 144,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0021.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 22,
|
||||||
|
"y": 8,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 216,
|
||||||
|
"w": 48,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0022.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 8,
|
||||||
|
"w": 50,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 57,
|
||||||
|
"y": 216,
|
||||||
|
"w": 50,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0023.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 18,
|
||||||
|
"y": 8,
|
||||||
|
"w": 51,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 114,
|
||||||
|
"y": 216,
|
||||||
|
"w": 51,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0024.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 18,
|
||||||
|
"y": 8,
|
||||||
|
"w": 51,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 171,
|
||||||
|
"y": 216,
|
||||||
|
"w": 51,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0025.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 15,
|
||||||
|
"y": 8,
|
||||||
|
"w": 53,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 228,
|
||||||
|
"y": 216,
|
||||||
|
"w": 53,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0026.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 10,
|
||||||
|
"y": 8,
|
||||||
|
"w": 57,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 285,
|
||||||
|
"y": 216,
|
||||||
|
"w": 57,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0027.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 10,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 342,
|
||||||
|
"y": 216,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0028.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 10,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 288,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0029.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 9,
|
||||||
|
"y": 8,
|
||||||
|
"w": 55,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 57,
|
||||||
|
"y": 288,
|
||||||
|
"w": 55,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0030.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 114,
|
||||||
|
"y": 288,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0031.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 171,
|
||||||
|
"y": 288,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0032.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 228,
|
||||||
|
"y": 288,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0033.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 8,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 285,
|
||||||
|
"y": 288,
|
||||||
|
"w": 56,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:e7f062304401dbd7b3ec79512f0ff4cb:0136dac01331f88892a3df26aeab78f5:1ed1e22abb9b55d76337a5a599835c06$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 23 KiB |
|
@ -0,0 +1,797 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "b2w2_veteran_m.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 424,
|
||||||
|
"h": 390
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 53,
|
||||||
|
"y": 0,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 106,
|
||||||
|
"y": 0,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 159,
|
||||||
|
"y": 0,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 212,
|
||||||
|
"y": 0,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 265,
|
||||||
|
"y": 0,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 318,
|
||||||
|
"y": 0,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 371,
|
||||||
|
"y": 0,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 78,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0009.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 53,
|
||||||
|
"y": 78,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0010.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 48,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 106,
|
||||||
|
"y": 78,
|
||||||
|
"w": 48,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0011.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 50,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 159,
|
||||||
|
"y": 78,
|
||||||
|
"w": 50,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0012.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 212,
|
||||||
|
"y": 78,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0013.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 265,
|
||||||
|
"y": 78,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0014.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 318,
|
||||||
|
"y": 78,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0015.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 371,
|
||||||
|
"y": 78,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0016.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 156,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0017.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 53,
|
||||||
|
"y": 156,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0018.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 106,
|
||||||
|
"y": 156,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0019.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 159,
|
||||||
|
"y": 156,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0020.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 212,
|
||||||
|
"y": 156,
|
||||||
|
"w": 53,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0021.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 265,
|
||||||
|
"y": 156,
|
||||||
|
"w": 52,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0022.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 318,
|
||||||
|
"y": 156,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0023.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 371,
|
||||||
|
"y": 156,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0024.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 234,
|
||||||
|
"w": 51,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0025.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 50,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 53,
|
||||||
|
"y": 234,
|
||||||
|
"w": 50,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0026.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 48,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 106,
|
||||||
|
"y": 234,
|
||||||
|
"w": 48,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0027.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 46,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 159,
|
||||||
|
"y": 234,
|
||||||
|
"w": 46,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0028.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 46,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 212,
|
||||||
|
"y": 234,
|
||||||
|
"w": 46,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0029.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 265,
|
||||||
|
"y": 234,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0030.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 318,
|
||||||
|
"y": 234,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0031.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 371,
|
||||||
|
"y": 234,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0032.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 312,
|
||||||
|
"w": 44,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0033.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 53,
|
||||||
|
"y": 312,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0034.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 106,
|
||||||
|
"y": 312,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0035.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 159,
|
||||||
|
"y": 312,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0036.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 212,
|
||||||
|
"y": 312,
|
||||||
|
"w": 43,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:4deb068879a8ac195cb4f00c8b17b7f5:b32f0f90436649264b6f3c49b09ac06a:05e903aa75b8e50c28334d9b5e14c85a$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,83 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "bait.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 14,
|
||||||
|
"h": 43
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 16
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3,
|
||||||
|
"w": 12,
|
||||||
|
"h": 13
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 12,
|
||||||
|
"h": 13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 16
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3,
|
||||||
|
"w": 12,
|
||||||
|
"h": 13
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 16,
|
||||||
|
"w": 12,
|
||||||
|
"h": 13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 16
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 5,
|
||||||
|
"w": 11,
|
||||||
|
"h": 11
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 31,
|
||||||
|
"w": 11,
|
||||||
|
"h": 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:f0ec04fcd67ac346dce973693711d032:b697e09191c4312b8faaa0a080a309b7:1af241a52e61fa01ca849aa03c112f85$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 277 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "berry_bush.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 49,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 49,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 49,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 49,
|
||||||
|
"h": 53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:d5f83625477b5f98b726343f4a3a396f:f4665258986e97345cfeee041b4b8bcf:e7781fcc447e6d12deb2af78c9493c7f$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 719 B |
|
@ -0,0 +1,19 @@
|
||||||
|
{ "frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 46, "h": 60 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 46, "h": 60 },
|
||||||
|
"sourceSize": { "w": 46, "h": 60 }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.aseprite.org/",
|
||||||
|
"version": "1.3.7-x64",
|
||||||
|
"image": "buoy-sheet.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": { "w": 46, "h": 60 },
|
||||||
|
"scale": "1"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,209 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "chest_blue.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 58,
|
||||||
|
"h": 528
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 30,
|
||||||
|
"w": 48,
|
||||||
|
"h": 41
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 48,
|
||||||
|
"h": 41
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 34,
|
||||||
|
"w": 49,
|
||||||
|
"h": 37
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 44,
|
||||||
|
"w": 49,
|
||||||
|
"h": 37
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 30,
|
||||||
|
"w": 48,
|
||||||
|
"h": 41
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 83,
|
||||||
|
"w": 48,
|
||||||
|
"h": 41
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 23,
|
||||||
|
"w": 48,
|
||||||
|
"h": 48
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 126,
|
||||||
|
"w": 48,
|
||||||
|
"h": 48
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 4,
|
||||||
|
"w": 55,
|
||||||
|
"h": 67
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 176,
|
||||||
|
"w": 55,
|
||||||
|
"h": 67
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 15,
|
||||||
|
"y": 2,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 245,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 15,
|
||||||
|
"y": 2,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 316,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 387,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 75,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 2,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 458,
|
||||||
|
"w": 56,
|
||||||
|
"h": 69
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:5f36000f6160ee6f397afe5a6fd60b73:cf6f4b08e23400447813583c322eb6c7:f4f3c064e6c93b8d1290f93bee927f60$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "chest_red.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 76,
|
||||||
|
"h": 57
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 76,
|
||||||
|
"h": 57
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 10,
|
||||||
|
"y": 3,
|
||||||
|
"w": 56,
|
||||||
|
"h": 54
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 54
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "dark_deal_porygon.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 36,
|
||||||
|
"h": 45
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 36,
|
||||||
|
"h": 45
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 44,
|
||||||
|
"h": 44
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 36,
|
||||||
|
"h": 45
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 255 B |
After Width: | Height: | Size: 378 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "girawitch.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 46,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 46,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 46,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 46,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:e68bbc186f511d505c53b2beec3c3741:7108795fc29d953a1d3729ad93d70936:1661aeeeb2f0e4561c644aff254770b3$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1021 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "mad_scientist_m.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 46,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 44,
|
||||||
|
"h": 74
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 44,
|
||||||
|
"h": 74
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 74
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:a7f8ff2bbb362868f51125c254eb6681:cf76e61ddd31a8f46af67ced168c44a2:4fc09abe16c0608828269e5da81d0744$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 920 B |
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "mud.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 14,
|
||||||
|
"h": 68
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 20
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 12,
|
||||||
|
"h": 13
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 12,
|
||||||
|
"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",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 20
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 1,
|
||||||
|
"w": 12,
|
||||||
|
"h": 16
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 32,
|
||||||
|
"w": 12,
|
||||||
|
"h": 16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 12,
|
||||||
|
"h": 20
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3,
|
||||||
|
"w": 12,
|
||||||
|
"h": 17
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 50,
|
||||||
|
"w": 12,
|
||||||
|
"h": 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:4f18a8effb8f01eb70f9f25b8294c1bf:ad663a73c51f780bbf45d00a52519553:c64f6b8befc3d5e9f836246d2b9536be$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 375 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "pokemon_salesman.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 40,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 21,
|
||||||
|
"y": 2,
|
||||||
|
"w": 38,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 38,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:dd57e3db21f3933c15be65bec261f4c1:05c7ef32252a5c2d3ad007b7e26fabd7:ae82f52e471ed81e2558206f05476cd7$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 839 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "safari_zone.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 120,
|
||||||
|
"h": 84
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 118,
|
||||||
|
"h": 82
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 118,
|
||||||
|
"h": 82
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 118,
|
||||||
|
"h": 82
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:6fad7a61e47043b974153148b4fd3997:5ec4d0890f2f03446daf22c8ae8ba77b:87aa745cd95eef6cbf38935230f4e10f$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "teacher.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 43,
|
||||||
|
"h": 74
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 19,
|
||||||
|
"y": 8,
|
||||||
|
"w": 41,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:506e5a4ce79c134a7b4af90a90aef244:1b81d3d84bf12cedc419805eaff82548:59bc5dd000b5e72588320b473e31c312$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 727 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "teleporter.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 64,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 64,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 64,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 64,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:a8e006630c2838130468b0d5c9aeb8a6:684c1813cb6c86e395c18027a593ed28:ce1615396ce7b0a146766d50b319bb81$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 661 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "training_gear.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 76,
|
||||||
|
"h": 57
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 76,
|
||||||
|
"h": 57
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 10,
|
||||||
|
"y": 3,
|
||||||
|
"w": 56,
|
||||||
|
"h": 54
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 8,
|
||||||
|
"y": 0,
|
||||||
|
"w": 56,
|
||||||
|
"h": 54
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,524 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "buck.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 120,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0010.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0011.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0014.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0015.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 33,
|
||||||
|
"y": 4,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 76
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0018.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 18,
|
||||||
|
"y": 8,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 38,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0019.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 18,
|
||||||
|
"y": 8,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 38,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0020.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 15,
|
||||||
|
"y": 8,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 38,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0021.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 15,
|
||||||
|
"y": 8,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 38,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0022.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 8,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 38,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0023.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 8,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 38,
|
||||||
|
"y": 1,
|
||||||
|
"w": 44,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0009.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0012.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0013.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0016.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0017.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 34,
|
||||||
|
"y": 5,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 84,
|
||||||
|
"y": 1,
|
||||||
|
"w": 35,
|
||||||
|
"h": 75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:033f3d363b4192f64c92e02c19622c15:0d06141bef5af87ef82da967253207cb:3347efe478119141b0e3e6eccdecd0f5$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,398 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "cheryl.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 154,
|
||||||
|
"h": 83
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 25,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 25,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 26,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0009.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 26,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0010.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 27,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 44,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0011.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 27,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 44,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0012.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 24,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 44,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0013.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 24,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 44,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0014.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 27,
|
||||||
|
"y": 0,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 87,
|
||||||
|
"y": 1,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0015.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 27,
|
||||||
|
"y": 0,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 87,
|
||||||
|
"y": 1,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0016.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 26,
|
||||||
|
"y": 0,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 87,
|
||||||
|
"y": 1,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0017.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 26,
|
||||||
|
"y": 0,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 87,
|
||||||
|
"y": 1,
|
||||||
|
"w": 33,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 122,
|
||||||
|
"y": 1,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 122,
|
||||||
|
"y": 1,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 122,
|
||||||
|
"y": 1,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 0,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 122,
|
||||||
|
"y": 1,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 21,
|
||||||
|
"y": 0,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 122,
|
||||||
|
"y": 1,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 21,
|
||||||
|
"y": 0,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 122,
|
||||||
|
"y": 1,
|
||||||
|
"w": 31,
|
||||||
|
"h": 81
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:dfcf7aedbd588c4e42427a2e17c171bf:206549943a0e3325d20a017ef01eefee:a233cd27590422717866c66e366b68fb$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,83 @@
|
||||||
|
{ "frames": [
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 31, "h": 77 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 26, "y": 2, "w": 31, "h": 77 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"frame": { "x": 32, "y": 0, "w": 28, "h": 78 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 28, "y": 1, "w": 28, "h": 78 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"frame": { "x": 32, "y": 0, "w": 28, "h": 78 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 28, "y": 1, "w": 28, "h": 78 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"frame": { "x": 0, "y": 78, "w": 31, "h": 77 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 28, "y": 2, "w": 31, "h": 77 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"frame": { "x": 0, "y": 78, "w": 31, "h": 77 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 28, "y": 2, "w": 31, "h": 77 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.pngprite.org/",
|
||||||
|
"version": "1.3.7-x64",
|
||||||
|
"image": "marley.png",
|
||||||
|
"format": "I8",
|
||||||
|
"size": { "w": 60, "h": 155 },
|
||||||
|
"scale": "1"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,209 @@
|
||||||
|
{ "frames": [
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 23, "y": 14, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 23, "y": 14, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 22, "y": 13, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 11, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 11, "w": 53, "h": 63 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 12, "w": 53, "h": 63 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 13, "y": 11, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 13, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 14, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0009.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 13, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0010.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 13, "y": 11, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0011.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 11, "w": 53, "h": 63 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0012.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 12, "w": 53, "h": 63 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0013.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 11, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0014.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 22, "y": 13, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0015.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 23, "y": 14, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0016.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 22, "y": 13, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0017.png",
|
||||||
|
"frame": { "x": 53, "y": 0, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 11, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0018.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 11, "w": 53, "h": 63 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0019.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 53, "h": 63 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 21, "y": 12, "w": 53, "h": 63 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0020.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 13, "y": 11, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0021.png",
|
||||||
|
"frame": { "x": 0, "y": 63, "w": 44, "h": 65 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 13, "w": 44, "h": 65 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.aseprite.org/",
|
||||||
|
"version": "1.3.7-x64",
|
||||||
|
"image": "mira.png",
|
||||||
|
"format": "I8",
|
||||||
|
"size": { "w": 97, "h": 128 },
|
||||||
|
"scale": "1"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,209 @@
|
||||||
|
{ "frames": [
|
||||||
|
{
|
||||||
|
"filename": "0000.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0002.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0003.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0004.png",
|
||||||
|
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 31, "y": 0, "w": 37, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0005.png",
|
||||||
|
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 31, "y": 0, "w": 37, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0006.png",
|
||||||
|
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 30, "y": 0, "w": 37, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0007.png",
|
||||||
|
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 30, "y": 0, "w": 37, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0008.png",
|
||||||
|
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 28, "y": 0, "w": 37, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0009.png",
|
||||||
|
"frame": { "x": 55, "y": 80, "w": 37, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 28, "y": 0, "w": 37, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0010.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0011.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0012.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0013.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 11, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0014.png",
|
||||||
|
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0015.png",
|
||||||
|
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0016.png",
|
||||||
|
"frame": { "x": 0, "y": 80, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0017.png",
|
||||||
|
"frame": { "x": 0, "y": 80, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0018.png",
|
||||||
|
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0019.png",
|
||||||
|
"frame": { "x": 55, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0020.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "0021.png",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 12, "y": 0, "w": 55, "h": 80 },
|
||||||
|
"sourceSize": { "w": 80, "h": 80 },
|
||||||
|
"duration": 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.aseprite.org/",
|
||||||
|
"version": "1.3.7-x64",
|
||||||
|
"image": "riley.png",
|
||||||
|
"format": "I8",
|
||||||
|
"size": { "w": 110, "h": 160 },
|
||||||
|
"scale": "1"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "vicky.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 52,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 27,
|
||||||
|
"w": 52,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 52,
|
||||||
|
"h": 53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:bf9d2d417a1982282dd711456ac71206:101e07828e3d6e2a2a7a80aebfa802ad:cabe44a4410c334298b1984a219f8160$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 765 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "victor.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 55,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 12,
|
||||||
|
"y": 27,
|
||||||
|
"w": 55,
|
||||||
|
"h": 53
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 55,
|
||||||
|
"h": 53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:64eff0f697754cdf9552b46342c9292a:611e0e2cacbd90c1229ce5443b2414f0:0cc0f5a2c1b2eedb46dd8318e8feb1d8$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 794 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "victoria.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 52,
|
||||||
|
"h": 54
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 14,
|
||||||
|
"y": 26,
|
||||||
|
"w": 52,
|
||||||
|
"h": 54
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 52,
|
||||||
|
"h": 54
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:4dafeae3674d63b12cc4d8044f67b5a3:7834687d784c31169256927f419c7958:cf0eb39e0a3f2e42f23ca29747d73c40$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 813 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "vito.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 41,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 2,
|
||||||
|
"w": 41,
|
||||||
|
"h": 78
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 41,
|
||||||
|
"h": 78
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:cb988be58fcd5381174e9d120b051e38:4d4723dbbcd9713ee0ed3c2d84ef4bfb:1c7723b536b218346e3138016d865ce9$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 765 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "vivi.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 48,
|
||||||
|
"h": 69
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 13,
|
||||||
|
"y": 11,
|
||||||
|
"w": 48,
|
||||||
|
"h": 69
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 48,
|
||||||
|
"h": 69
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:0a51b4df0b2ed0fed7e3bdb5dffd9e28:af1f3b1480023b3e3761c49e49faf5f1:4fc6bf2bec74c4bb8809df38231deb01$"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 713 B |
|
@ -2,7 +2,7 @@ import Phaser from "phaser";
|
||||||
import UI from "./ui/ui";
|
import UI from "./ui/ui";
|
||||||
import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
|
import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
|
||||||
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
|
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
|
||||||
import { Constructor } 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, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
|
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
|
||||||
import { PokeballType } from "./data/pokeball";
|
import { PokeballType } from "./data/pokeball";
|
||||||
|
@ -11,7 +11,7 @@ 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, getModifierType, getPartyLuckValue, modifierTypes } from "./modifier/modifier-type";
|
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, modifierTypes } from "./modifier/modifier-type";
|
||||||
import AbilityBar from "./ui/ability-bar";
|
import AbilityBar from "./ui/ability-bar";
|
||||||
|
@ -22,14 +22,14 @@ 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";
|
||||||
|
@ -84,6 +84,13 @@ import { TitlePhase } from "./phases/title-phase";
|
||||||
import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase";
|
import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase";
|
||||||
import { TurnInitPhase } from "./phases/turn-init-phase";
|
import { TurnInitPhase } from "./phases/turn-init-phase";
|
||||||
import { ShopCursorTarget } from "./enums/shop-cursor-target";
|
import { ShopCursorTarget } from "./enums/shop-cursor-target";
|
||||||
|
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
|
||||||
|
import { allMysteryEncounters, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
||||||
|
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||||
|
|
||||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
||||||
|
|
||||||
|
@ -236,6 +243,8 @@ export default class BattleScene extends SceneBase {
|
||||||
public money: integer;
|
public money: integer;
|
||||||
public pokemonInfoContainer: PokemonInfoContainer;
|
public pokemonInfoContainer: PokemonInfoContainer;
|
||||||
private party: PlayerPokemon[];
|
private party: PlayerPokemon[];
|
||||||
|
public mysteryEncounterData: MysteryEncounterData = new MysteryEncounterData(null);
|
||||||
|
public lastMysteryEncounter: MysteryEncounter;
|
||||||
/** Combined Biome and Wave count text */
|
/** Combined Biome and Wave count text */
|
||||||
private biomeWaveText: Phaser.GameObjects.Text;
|
private biomeWaveText: Phaser.GameObjects.Text;
|
||||||
private moneyText: Phaser.GameObjects.Text;
|
private moneyText: Phaser.GameObjects.Text;
|
||||||
|
@ -389,6 +398,7 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
this.fieldUI = fieldUI;
|
this.fieldUI = fieldUI;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const transition = this.make.rexTransitionImagePack({
|
const transition = this.make.rexTransitionImagePack({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -874,6 +884,20 @@ export default class BattleScene extends SceneBase {
|
||||||
return pokemon;
|
return pokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removePokemonFromPlayerParty(pokemon: PlayerPokemon, destroy: boolean = true) {
|
||||||
|
if (!pokemon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const partyIndex = this.party.indexOf(pokemon);
|
||||||
|
this.party.splice(partyIndex, 1);
|
||||||
|
if (destroy) {
|
||||||
|
this.field.remove(pokemon, true);
|
||||||
|
pokemon.destroy();
|
||||||
|
}
|
||||||
|
this.updateModifiers(true);
|
||||||
|
}
|
||||||
|
|
||||||
addPokemonIcon(pokemon: Pokemon, x: number, y: number, originX: number = 0.5, originY: number = 0.5, ignoreOverride: boolean = false): Phaser.GameObjects.Container {
|
addPokemonIcon(pokemon: Pokemon, x: number, y: number, originX: number = 0.5, originY: number = 0.5, ignoreOverride: boolean = false): Phaser.GameObjects.Container {
|
||||||
const container = this.add.container(x, y);
|
const container = this.add.container(x, y);
|
||||||
container.setName(`${pokemon.name}-icon`);
|
container.setName(`${pokemon.name}-icon`);
|
||||||
|
@ -1065,7 +1089,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle | null {
|
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounter?: MysteryEncounter): Battle | null {
|
||||||
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
|
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
|
||||||
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
|
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
|
||||||
let newDouble: boolean | undefined;
|
let newDouble: boolean | undefined;
|
||||||
|
@ -1113,6 +1137,40 @@ export default class BattleScene extends SceneBase {
|
||||||
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, doubleTrainer ? TrainerVariant.DOUBLE : Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
|
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, doubleTrainer ? TrainerVariant.DOUBLE : Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
|
||||||
this.field.add(newTrainer);
|
this.field.add(newTrainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove these once ME spawn rates are finalized
|
||||||
|
// let testStartingWeight = 0;
|
||||||
|
// while (testStartingWeight < 3) {
|
||||||
|
// calculateMEAggregateStats(this, testStartingWeight);
|
||||||
|
// testStartingWeight += 2;
|
||||||
|
// }
|
||||||
|
// calculateRareSpawnAggregateStats(this, 14);
|
||||||
|
|
||||||
|
// Check for mystery encounter
|
||||||
|
// Can only occur in place of a standard wild battle, waves 10-180
|
||||||
|
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) {
|
||||||
|
const roll = Utils.randSeedInt(256);
|
||||||
|
|
||||||
|
// Base spawn weight is 1/256, and increases by 5/256 for each missed attempt at spawning an encounter on a valid floor
|
||||||
|
const sessionEncounterRate = !isNullOrUndefined(this.mysteryEncounterData?.encounterSpawnChance) ? this.mysteryEncounterData.encounterSpawnChance : BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
|
|
||||||
|
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn
|
||||||
|
// Do the reverse as well
|
||||||
|
// Reduces occurrence of runs with very few (<6) and a ton (>10) of encounters
|
||||||
|
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (180 - 10) * newWaveIndex;
|
||||||
|
const currentRunDiffFromAvg = expectedEncountersByFloor - (this.mysteryEncounterData?.encounteredEvents?.length || 0);
|
||||||
|
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * 5;
|
||||||
|
|
||||||
|
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
|
||||||
|
|
||||||
|
if (roll < successRate) {
|
||||||
|
newBattleType = BattleType.MYSTERY_ENCOUNTER;
|
||||||
|
// Reset base spawn weight
|
||||||
|
this.mysteryEncounterData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
|
} else {
|
||||||
|
this.mysteryEncounterData.encounterSpawnChance = sessionEncounterRate + WEIGHT_INCREMENT_ON_SPAWN_MISS;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (double === undefined && newWaveIndex > 1) {
|
if (double === undefined && newWaveIndex > 1) {
|
||||||
|
@ -1145,12 +1203,21 @@ export default class BattleScene extends SceneBase {
|
||||||
const maxExpLevel = this.getMaxExpLevel();
|
const maxExpLevel = this.getMaxExpLevel();
|
||||||
|
|
||||||
this.lastEnemyTrainer = lastBattle?.trainer ?? null;
|
this.lastEnemyTrainer = lastBattle?.trainer ?? null;
|
||||||
|
this.lastMysteryEncounter = lastBattle?.mysteryEncounter ?? null;
|
||||||
|
|
||||||
this.executeWithSeedOffset(() => {
|
this.executeWithSeedOffset(() => {
|
||||||
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
|
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
|
||||||
}, newWaveIndex << 3, this.waveSeed);
|
}, newWaveIndex << 3, this.waveSeed);
|
||||||
this.currentBattle.incrementTurn(this);
|
this.currentBattle.incrementTurn(this);
|
||||||
|
|
||||||
|
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||||
|
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
|
||||||
|
this.currentBattle.double = false;
|
||||||
|
this.executeWithSeedOffset(() => {
|
||||||
|
this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounter);
|
||||||
|
}, this.currentBattle.waveIndex << 4);
|
||||||
|
}
|
||||||
|
|
||||||
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
|
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
|
||||||
|
|
||||||
if (!waveIndex && lastBattle) {
|
if (!waveIndex && lastBattle) {
|
||||||
|
@ -1167,6 +1234,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
if (resetArenaState) {
|
if (resetArenaState) {
|
||||||
this.arena.resetArenaEffects();
|
this.arena.resetArenaEffects();
|
||||||
|
if (lastBattle?.mysteryEncounter?.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
|
||||||
playerField.forEach((_, p) => this.pushPhase(new ReturnPhase(this, p)));
|
playerField.forEach((_, p) => this.pushPhase(new ReturnPhase(this, p)));
|
||||||
|
|
||||||
for (const pokemon of this.getParty()) {
|
for (const pokemon of this.getParty()) {
|
||||||
|
@ -1176,6 +1244,7 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
this.pushPhase(new ShowTrainerPhase(this));
|
this.pushPhase(new ShowTrainerPhase(this));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const pokemon of this.getParty()) {
|
for (const pokemon of this.getParty()) {
|
||||||
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger);
|
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger);
|
||||||
|
@ -2418,7 +2487,7 @@ export default class BattleScene extends SceneBase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateEnemyModifiers(): Promise<void> {
|
generateEnemyModifiers(heldModifiersConfigs?: HeldModifierConfig[][]): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
|
if (this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
|
||||||
return resolve();
|
return resolve();
|
||||||
|
@ -2440,6 +2509,17 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
|
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
|
||||||
|
if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i] && heldModifiersConfigs[i].length > 0) {
|
||||||
|
heldModifiersConfigs[i].forEach(mt => {
|
||||||
|
const stackCount = mt.stackCount ?? 1;
|
||||||
|
// const isTransferable = mt.isTransferable ?? true;
|
||||||
|
const modifier = mt.modifierType.newModifier(enemyPokemon);
|
||||||
|
modifier.stackCount = stackCount;
|
||||||
|
// TODO: set isTransferable
|
||||||
|
// modifier.setIsTransferable(isTransferable);
|
||||||
|
this.addEnemyModifier(modifier, true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
|
const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);
|
||||||
let upgradeChance = 32;
|
let upgradeChance = 32;
|
||||||
if (isBoss) {
|
if (isBoss) {
|
||||||
|
@ -2463,6 +2543,8 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance)
|
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance)
|
||||||
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
|
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
this.updateModifiers(false).then(() => resolve());
|
this.updateModifiers(false).then(() => resolve());
|
||||||
});
|
});
|
||||||
|
@ -2731,4 +2813,114 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
this.shiftPhase();
|
this.shiftPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads or generates a mystery encounter
|
||||||
|
* @param override - used to load session encounter when restarting game, etc.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getMysteryEncounter(override: MysteryEncounter | undefined): MysteryEncounter {
|
||||||
|
// Loading override or session encounter
|
||||||
|
let encounter: MysteryEncounter | null;
|
||||||
|
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE!)) {
|
||||||
|
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE!];
|
||||||
|
} else {
|
||||||
|
encounter = override?.encounterType && override.encounterType >= 0 ? allMysteryEncounters[override.encounterType] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for queued encounters first
|
||||||
|
if (!encounter && this.mysteryEncounterData?.nextEncounterQueue && this.mysteryEncounterData.nextEncounterQueue.length > 0) {
|
||||||
|
let i = 0;
|
||||||
|
while (i < this.mysteryEncounterData.nextEncounterQueue.length && !!encounter) {
|
||||||
|
const candidate = this.mysteryEncounterData.nextEncounterQueue[i];
|
||||||
|
const forcedChance = candidate[1];
|
||||||
|
if (Utils.randSeedInt(100) < forcedChance) {
|
||||||
|
encounter = allMysteryEncounters[candidate[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encounter) {
|
||||||
|
encounter = new MysteryEncounter(encounter);
|
||||||
|
encounter.populateDialogueTokensFromRequirements(this);
|
||||||
|
return encounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Enum values for base tier weights
|
||||||
|
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
|
||||||
|
this.mysteryEncounterData.encounteredEvents.forEach(val => {
|
||||||
|
const tier = val[1];
|
||||||
|
if (tier === MysteryEncounterTier.COMMON) {
|
||||||
|
tierWeights[0] = tierWeights[0] - 6;
|
||||||
|
} else if (tier === MysteryEncounterTier.GREAT) {
|
||||||
|
tierWeights[1] = tierWeights[1] - 4;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalWeight = tierWeights.reduce((a, b) => a + b);
|
||||||
|
const tierValue = Utils.randSeedInt(totalWeight);
|
||||||
|
const commonThreshold = totalWeight - tierWeights[0];
|
||||||
|
const uncommonThreshold = totalWeight - tierWeights[0] - tierWeights[1];
|
||||||
|
const rareThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2];
|
||||||
|
let tier: MysteryEncounterTier | null = tierValue > commonThreshold ? MysteryEncounterTier.COMMON : tierValue > uncommonThreshold ? MysteryEncounterTier.GREAT : tierValue > rareThreshold ? MysteryEncounterTier.ULTRA : MysteryEncounterTier.ROGUE;
|
||||||
|
|
||||||
|
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE)) {
|
||||||
|
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE!;
|
||||||
|
}
|
||||||
|
|
||||||
|
let availableEncounters: MysteryEncounter[] = [];
|
||||||
|
// New encounter will never be the same as the most recent encounter
|
||||||
|
const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null;
|
||||||
|
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? [];
|
||||||
|
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
|
||||||
|
while (availableEncounters.length === 0 && tier !== null) {
|
||||||
|
availableEncounters = biomeMysteryEncounters
|
||||||
|
.filter((encounterType) => {
|
||||||
|
const encounterCandidate = allMysteryEncounters[encounterType];
|
||||||
|
if (!encounterCandidate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (encounterCandidate.encounterTier !== tier) { // Encounter is in tier
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!encounterCandidate.meetsRequirements!(this)) { // Meets encounter requirements
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isNullOrUndefined(previousEncounter) && encounterType === previousEncounter) { // Previous encounter was not this one
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.mysteryEncounterData.encounteredEvents?.length > 0 && // Encounter has not exceeded max allowed encounters
|
||||||
|
(encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0)
|
||||||
|
&& this.mysteryEncounterData.encounteredEvents.filter(e => e[0] === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((m) => (allMysteryEncounters[m]));
|
||||||
|
// Decrement tier
|
||||||
|
if (tier === MysteryEncounterTier.ROGUE) {
|
||||||
|
tier = MysteryEncounterTier.ULTRA;
|
||||||
|
} else if (tier === MysteryEncounterTier.ULTRA) {
|
||||||
|
tier = MysteryEncounterTier.GREAT;
|
||||||
|
} else if (tier === MysteryEncounterTier.GREAT) {
|
||||||
|
tier = MysteryEncounterTier.COMMON;
|
||||||
|
} else {
|
||||||
|
tier = null; // Ends loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If absolutely no encounters are available, spawn 0th encounter
|
||||||
|
if (availableEncounters.length === 0) {
|
||||||
|
return allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS];
|
||||||
|
}
|
||||||
|
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
|
||||||
|
// New encounter object to not dirty flags
|
||||||
|
encounter = new MysteryEncounter(encounter);
|
||||||
|
encounter.populateDialogueTokensFromRequirements!(this);
|
||||||
|
return encounter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,14 @@ 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 i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
|
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
|
||||||
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
|
||||||
export enum BattleType {
|
export enum BattleType {
|
||||||
WILD,
|
WILD,
|
||||||
TRAINER,
|
TRAINER,
|
||||||
CLEAR
|
CLEAR,
|
||||||
|
MYSTERY_ENCOUNTER
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BattlerIndex {
|
export enum BattlerIndex {
|
||||||
|
@ -67,6 +70,7 @@ export default class Battle {
|
||||||
public lastUsedPokeball: PokeballType | null;
|
public lastUsedPokeball: PokeballType | null;
|
||||||
public playerFaints: number; // The amount of times pokemon on the players side have fainted
|
public playerFaints: number; // The amount of times pokemon on the players side have fainted
|
||||||
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
|
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
|
||||||
|
public mysteryEncounter: MysteryEncounter;
|
||||||
|
|
||||||
private rngCounter: integer = 0;
|
private rngCounter: integer = 0;
|
||||||
|
|
||||||
|
@ -105,7 +109,7 @@ export default class Battle {
|
||||||
this.battleSpec = spec;
|
this.battleSpec = spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLevelForWave(): integer {
|
public getLevelForWave(): integer {
|
||||||
const levelWaveIndex = this.gameMode.getWaveForDifficulty(this.waveIndex);
|
const levelWaveIndex = this.gameMode.getWaveForDifficulty(this.waveIndex);
|
||||||
const baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2);
|
const baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2);
|
||||||
const bossMultiplier = 1.2;
|
const bossMultiplier = 1.2;
|
||||||
|
@ -203,7 +207,7 @@ export default class Battle {
|
||||||
|
|
||||||
getBgmOverride(scene: BattleScene): string | null {
|
getBgmOverride(scene: BattleScene): string | null {
|
||||||
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
||||||
if (this.battleType === BattleType.TRAINER) {
|
if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.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()}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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 Phaser from "phaser";
|
||||||
//import fs from 'vite-plugin-fs/browser';
|
//import fs from 'vite-plugin-fs/browser';
|
||||||
|
|
||||||
export enum AnimFrameTarget {
|
export enum AnimFrameTarget {
|
||||||
|
@ -102,6 +104,18 @@ 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,
|
||||||
|
SMOKESCREEN,
|
||||||
|
DANCE
|
||||||
|
}
|
||||||
|
|
||||||
export class AnimConfig {
|
export class AnimConfig {
|
||||||
public id: integer;
|
public id: integer;
|
||||||
public graphic: string;
|
public graphic: string;
|
||||||
|
@ -303,7 +317,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;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +335,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 {
|
||||||
|
@ -383,7 +397,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;
|
||||||
|
@ -413,7 +427,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();
|
||||||
}
|
}
|
||||||
|
@ -425,7 +439,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,6 +461,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
||||||
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
||||||
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
||||||
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 => {
|
||||||
|
@ -515,6 +532,26 @@ 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 encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
|
||||||
|
for (const anim of anims) {
|
||||||
|
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(cas => encounterAnims.set(anim, 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)) {
|
||||||
|
@ -569,6 +606,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();
|
||||||
|
@ -678,14 +725,16 @@ export abstract class BattleAnim {
|
||||||
public target: Pokemon | null;
|
public target: Pokemon | null;
|
||||||
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 ?? null;
|
this.user = user ?? null;
|
||||||
this.target = target ?? null;
|
this.target = target ?? null;
|
||||||
this.sprites = [];
|
this.sprites = [];
|
||||||
|
this.playOnEmptyField = playOnEmptyField;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getAnim(): AnimConfig | null;
|
abstract getAnim(): AnimConfig | null;
|
||||||
|
@ -757,9 +806,9 @@ export abstract class BattleAnim {
|
||||||
play(scene: BattleScene, callback?: Function) {
|
play(scene: BattleScene, callback?: Function) {
|
||||||
const isOppAnim = this.isOppAnim();
|
const isOppAnim = this.isOppAnim();
|
||||||
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
|
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
@ -983,13 +1032,181 @@ 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) => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setSpritePriority(frame.priority);
|
||||||
|
}
|
||||||
|
moveSprite.setFrame(frame.graphicFrame);
|
||||||
|
|
||||||
|
const graphicFrameData = frameData.get(frame.target)?.get(graphicIndex);
|
||||||
|
if (graphicFrameData) {
|
||||||
|
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.get(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 | null;
|
public commonAnim: CommonAnim | null;
|
||||||
|
|
||||||
constructor(commonAnim: CommonAnim | null, user: Pokemon, target?: Pokemon) {
|
constructor(commonAnim: CommonAnim | null, user: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) {
|
||||||
super(user, target || user);
|
super(user, target || user, playOnEmptyField);
|
||||||
|
|
||||||
this.commonAnim = commonAnim;
|
this.commonAnim = commonAnim;
|
||||||
}
|
}
|
||||||
|
@ -1051,6 +1268,26 @@ export class MoveChargeAnim extends MoveAnim {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EncounterBattleAnim extends BattleAnim {
|
||||||
|
public encounterAnim: EncounterAnim;
|
||||||
|
public oppAnim: boolean;
|
||||||
|
|
||||||
|
constructor(encounterAnim: EncounterAnim, user: Pokemon, target?: Pokemon, oppAnim?: boolean) {
|
||||||
|
super(user, target || user, true);
|
||||||
|
|
||||||
|
this.encounterAnim = encounterAnim;
|
||||||
|
this.oppAnim = oppAnim ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnim(): AnimConfig | null {
|
||||||
|
return encounterAnims.get(this.encounterAnim) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isOppAnim(): boolean {
|
||||||
|
return this.oppAnim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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, ""));
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { StatusEffect } from "./status-effect";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { ChargeAttr, MoveFlags, allMoves } from "./move";
|
import { ChargeAttr, MoveFlags, allMoves } from "./move";
|
||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability";
|
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, ProtectStatAbAttr } from "./ability";
|
||||||
import { TerrainType } from "./terrain";
|
import { TerrainType } from "./terrain";
|
||||||
import { WeatherType } from "./weather";
|
import { WeatherType } from "./weather";
|
||||||
import { BattleStat } from "./battle-stat";
|
import { BattleStat } from "./battle-stat";
|
||||||
|
@ -1827,6 +1827,37 @@ export class ExposedTag extends BattlerTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MysteryEncounterPostSummonTag extends BattlerTag {
|
||||||
|
constructor(sourceMove: Moves) {
|
||||||
|
super(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, BattlerTagLapseType.CUSTOM, 1, sourceMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd(pokemon: Pokemon): void {
|
||||||
|
super.onAdd(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||||
|
const ret = super.lapse(pokemon, lapseType);
|
||||||
|
|
||||||
|
if (lapseType === BattlerTagLapseType.CUSTOM) {
|
||||||
|
// Give pokemon +1 stats for battle
|
||||||
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
||||||
|
if (!cancelled.value) {
|
||||||
|
const mysteryEncounterBattleEffects = pokemon.mysteryEncounterBattleEffects;
|
||||||
|
if (mysteryEncounterBattleEffects) {
|
||||||
|
mysteryEncounterBattleEffects(pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(pokemon: Pokemon): void {
|
||||||
|
super.onRemove(pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag {
|
export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag {
|
||||||
switch (tagType) {
|
switch (tagType) {
|
||||||
|
@ -1962,6 +1993,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
||||||
case BattlerTagType.GULP_MISSILE_ARROKUDA:
|
case BattlerTagType.GULP_MISSILE_ARROKUDA:
|
||||||
case BattlerTagType.GULP_MISSILE_PIKACHU:
|
case BattlerTagType.GULP_MISSILE_PIKACHU:
|
||||||
return new GulpMissileTag(tagType, sourceMove);
|
return new GulpMissileTag(tagType, sourceMove);
|
||||||
|
case BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON:
|
||||||
|
return new MysteryEncounterPostSummonTag(sourceMove);
|
||||||
case BattlerTagType.NONE:
|
case BattlerTagType.NONE:
|
||||||
default:
|
default:
|
||||||
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
|
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
|
||||||
|
|
|
@ -909,6 +909,126 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
[TrainerType.BUCK]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:stat_trainer_buck.encounter.1",
|
||||||
|
"dialogue:stat_trainer_buck.encounter.2"
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:stat_trainer_buck.victory.1"
|
||||||
|
],
|
||||||
|
defeat: [
|
||||||
|
"dialogue:stat_trainer_buck.defeat.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.CHERYL]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:stat_trainer_cheryl.encounter.1",
|
||||||
|
"dialogue:stat_trainer_cheryl.encounter.2"
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:stat_trainer_cheryl.victory.1"
|
||||||
|
],
|
||||||
|
defeat: [
|
||||||
|
"dialogue:stat_trainer_cheryl.defeat.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.MARLEY]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:stat_trainer_marley.encounter.1",
|
||||||
|
"dialogue:stat_trainer_marley.encounter.2"
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:stat_trainer_marley.victory.1"
|
||||||
|
],
|
||||||
|
defeat: [
|
||||||
|
"dialogue:stat_trainer_marley.defeat.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.MIRA]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:stat_trainer_mira.encounter.1",
|
||||||
|
"dialogue:stat_trainer_mira.encounter.2"
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:stat_trainer_mira.victory.1"
|
||||||
|
],
|
||||||
|
defeat: [
|
||||||
|
"dialogue:stat_trainer_mira.defeat.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.RILEY]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:stat_trainer_riley.encounter.1",
|
||||||
|
"dialogue:stat_trainer_riley.encounter.2"
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:stat_trainer_riley.victory.1"
|
||||||
|
],
|
||||||
|
defeat: [
|
||||||
|
"dialogue:stat_trainer_riley.defeat.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.VICTOR]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:winstrates_victor.encounter.1",
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:winstrates_victor.victory.1"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.VICTORIA]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:winstrates_victoria.encounter.1",
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:winstrates_victoria.victory.1"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.VIVI]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:winstrates_vivi.encounter.1",
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:winstrates_vivi.victory.1"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.VICKY]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:winstrates_vicky.encounter.1",
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:winstrates_vicky.victory.1"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[TrainerType.VITO]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:winstrates_vito.encounter.1",
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:winstrates_vito.victory.1"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
[TrainerType.BROCK]: {
|
[TrainerType.BROCK]: {
|
||||||
encounter: [
|
encounter: [
|
||||||
"dialogue:brock.encounter.1",
|
"dialogue:brock.encounter.1",
|
||||||
|
|
|
@ -61,7 +61,10 @@ export interface IEggOptions {
|
||||||
/** Defines if the egg will hatch with the hidden ability of this species.
|
/** Defines if the egg will hatch with the hidden ability of this species.
|
||||||
* If no hidden ability exist, a random one will get choosen.
|
* If no hidden ability exist, a random one will get choosen.
|
||||||
*/
|
*/
|
||||||
overrideHiddenAbility?: boolean
|
overrideHiddenAbility?: boolean,
|
||||||
|
|
||||||
|
/** If Egg is of {@link EggSourceType.EVENT}, can customize the message displayed for where the egg was obtained */
|
||||||
|
eventEggTypeDescriptor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Egg {
|
export class Egg {
|
||||||
|
@ -83,6 +86,8 @@ export class Egg {
|
||||||
|
|
||||||
private _overrideHiddenAbility: boolean;
|
private _overrideHiddenAbility: boolean;
|
||||||
|
|
||||||
|
private _eventEggTypeDescriptor?: string;
|
||||||
|
|
||||||
////
|
////
|
||||||
// #endregion
|
// #endregion
|
||||||
////
|
////
|
||||||
|
@ -180,6 +185,8 @@ export class Egg {
|
||||||
this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct?
|
this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct?
|
||||||
this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct?
|
this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._eventEggTypeDescriptor = eggOptions?.eventEggTypeDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
|
@ -279,6 +286,8 @@ export class Egg {
|
||||||
return i18next.t("egg:gachaTypeShiny");
|
return i18next.t("egg:gachaTypeShiny");
|
||||||
case EggSourceType.GACHA_MOVE:
|
case EggSourceType.GACHA_MOVE:
|
||||||
return i18next.t("egg:gachaTypeMove");
|
return i18next.t("egg:gachaTypeMove");
|
||||||
|
case EggSourceType.EVENT:
|
||||||
|
return this._eventEggTypeDescriptor ?? i18next.t("egg:eventType");
|
||||||
default:
|
default:
|
||||||
console.warn("getEggTypeDescriptor case not defined. Returning default empty string");
|
console.warn("getEggTypeDescriptor case not defined. Returning default empty string");
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { trainerConfigs, } from "#app/data/trainer-config";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { IEggOptions } from "#app/data/egg";
|
||||||
|
import { EggSourceType } from "#enums/egg-source-types";
|
||||||
|
import { EggTier } from "#enums/egg-type";
|
||||||
|
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:aTrainersTest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Trainer's Test encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/115 | GitHub Issue #115}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const ATrainersTestEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.A_TRAINERS_TEST)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
|
.withSceneWaveRangeRequirement(100, 180)
|
||||||
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Randomly pick from 1 of the 5 stat trainers to spawn
|
||||||
|
let trainerType: TrainerType;
|
||||||
|
let spriteKeys;
|
||||||
|
let trainerNameKey: string;
|
||||||
|
switch (randSeedInt(5)) {
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
trainerType = TrainerType.BUCK;
|
||||||
|
spriteKeys = getSpriteKeysFromSpecies(Species.CLAYDOL);
|
||||||
|
trainerNameKey = "buck";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
trainerType = TrainerType.CHERYL;
|
||||||
|
spriteKeys = getSpriteKeysFromSpecies(Species.BLISSEY);
|
||||||
|
trainerNameKey = "cheryl";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
trainerType = TrainerType.MARLEY;
|
||||||
|
spriteKeys = getSpriteKeysFromSpecies(Species.ARCANINE);
|
||||||
|
trainerNameKey = "marley";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
trainerType = TrainerType.MIRA;
|
||||||
|
spriteKeys = getSpriteKeysFromSpecies(Species.ALAKAZAM, false, 1);
|
||||||
|
trainerNameKey = "mira";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
trainerType = TrainerType.RILEY;
|
||||||
|
spriteKeys = getSpriteKeysFromSpecies(Species.LUCARIO, false, 1);
|
||||||
|
trainerNameKey = "riley";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialogue and tokens for trainer
|
||||||
|
encounter.dialogue.intro = [
|
||||||
|
{
|
||||||
|
speaker: `trainerNames:${trainerNameKey}`,
|
||||||
|
text: `${namespace}.${trainerNameKey}.intro_dialogue`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
encounter.options[0].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
speaker: `trainerNames:${trainerNameKey}`,
|
||||||
|
text: `${namespace}.${trainerNameKey}.accept`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
encounter.options[1].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
speaker: `trainerNames:${trainerNameKey}`,
|
||||||
|
text: `${namespace}.${trainerNameKey}.decline`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
encounter.setDialogueToken("statTrainerName", i18next.t(`trainerNames:${trainerNameKey}`));
|
||||||
|
const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(`trainerNames:${trainerNameKey}`);
|
||||||
|
encounter.misc = { trainerType, trainerNameKey, trainerEggDescription: eggDescription };
|
||||||
|
|
||||||
|
// Trainer config
|
||||||
|
const trainerConfig = trainerConfigs[trainerType].copy();
|
||||||
|
const trainerSpriteKey = trainerConfig.getSpriteKey();
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
trainerConfig: trainerConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: spriteKeys.spriteKey,
|
||||||
|
fileRoot: spriteKeys.fileRoot,
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
isPokemon: true,
|
||||||
|
x: 22,
|
||||||
|
y: -2,
|
||||||
|
yShadow: -2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: trainerSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
disableAnimation: true,
|
||||||
|
x: -24,
|
||||||
|
y: 4,
|
||||||
|
yShadow: 4
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withIntroDialogue()
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn standard trainer battle with memory mushroom reward
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
|
||||||
|
const eggOptions: IEggOptions = {
|
||||||
|
scene,
|
||||||
|
pulled: false,
|
||||||
|
sourceType: EggSourceType.EVENT,
|
||||||
|
eventEggTypeDescriptor: encounter.misc.trainerEggDescription,
|
||||||
|
tier: EggTier.ULTRA
|
||||||
|
};
|
||||||
|
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`));
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, [eggOptions]);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Full heal party
|
||||||
|
scene.unshiftPhase(new PartyHealPhase(scene, true));
|
||||||
|
|
||||||
|
const eggOptions: IEggOptions = {
|
||||||
|
scene,
|
||||||
|
pulled: false,
|
||||||
|
sourceType: EggSourceType.EVENT,
|
||||||
|
eventEggTypeDescriptor: encounter.misc.trainerEggDescription,
|
||||||
|
tier: EggTier.GREAT
|
||||||
|
};
|
||||||
|
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.rare`));
|
||||||
|
setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: 0 }, [eggOptions]);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.build();
|
|
@ -0,0 +1,514 @@
|
||||||
|
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { PersistentModifierRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { randInt } from "#app/utils";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import { PokeballType } from "#app/data/pokeball";
|
||||||
|
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:absoluteAvarice";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absolute Avarice encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/58 | GitHub Issue #58}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.ABSOLUTE_AVARICE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
// This sprite has the shadow
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.GREEDENT,
|
||||||
|
hasShadow: true,
|
||||||
|
alpha: 0.001,
|
||||||
|
repeat: true,
|
||||||
|
x: -5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.GREEDENT,
|
||||||
|
hasShadow: false,
|
||||||
|
repeat: true,
|
||||||
|
x: -5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "lum_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 7,
|
||||||
|
y: -14,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "salac_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "lansat_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 32,
|
||||||
|
y: 5,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "liechi_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 6,
|
||||||
|
y: -5,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "sitrus_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 7,
|
||||||
|
y: 8,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "enigma_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 26,
|
||||||
|
y: -4,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "leppa_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 16,
|
||||||
|
y: -27,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "petaya_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 30,
|
||||||
|
y: -17,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "ganlon_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 16,
|
||||||
|
y: -11,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "apicot_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 14,
|
||||||
|
y: -2,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "starf_berry",
|
||||||
|
fileRoot: "items",
|
||||||
|
isItem: true,
|
||||||
|
x: 18,
|
||||||
|
y: 9,
|
||||||
|
hidden: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
|
doGreedentSpriteSteal(scene);
|
||||||
|
doBerrySpritePile(scene);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
scene.loadSe("PRSFX- Bug Bite", "battle_anims");
|
||||||
|
scene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
|
||||||
|
|
||||||
|
// Get all player berry items, remove from party, and store reference
|
||||||
|
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
|
||||||
|
|
||||||
|
// Sort berries by party member ID to more easily re-add later if necessary
|
||||||
|
const berryItemsMap = new Map<number, BerryModifier[]>();
|
||||||
|
scene.getParty().forEach(pokemon => {
|
||||||
|
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
|
||||||
|
if (pokemonBerries?.length > 0) {
|
||||||
|
berryItemsMap.set(pokemon.id, pokemonBerries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
encounter.misc = { berryItemsMap };
|
||||||
|
|
||||||
|
// Generates copies of the stolen berries to put on the Greedent
|
||||||
|
const bossModifierConfigs: HeldModifierConfig[] = [];
|
||||||
|
berryItems.forEach(berryMod => {
|
||||||
|
// Can't define stack count on a ModifierType, have to just create separate instances for each stack
|
||||||
|
// Overflow berries will be "lost" on the boss, but it's un-catchable anyway
|
||||||
|
for (let i = 0; i < berryMod.stackCount; i++) {
|
||||||
|
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [berryMod.berryType]) as PokemonHeldItemModifierType;
|
||||||
|
bossModifierConfigs.push({ modifierType });
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.removeModifier(berryMod);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.GREEDENT),
|
||||||
|
isBoss: true,
|
||||||
|
bossSegments: 3,
|
||||||
|
moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF],
|
||||||
|
modifierConfigs: bossModifierConfigs,
|
||||||
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`);
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Provides 1x Reviver Seed to each party member at end of battle
|
||||||
|
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED);
|
||||||
|
const givePartyPokemonReviverSeeds = () => {
|
||||||
|
const party = scene.getParty();
|
||||||
|
party.forEach(p => {
|
||||||
|
const seedModifier = revSeed.newModifier(p);
|
||||||
|
scene.addModifier(seedModifier, false, false, false, true);
|
||||||
|
});
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.1.food_stash`);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds);
|
||||||
|
encounter.startOfBattleEffects.push({
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.ENEMY],
|
||||||
|
move: new PokemonMove(Moves.STUFF_CHEEKS),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const berryMap = encounter.misc.berryItemsMap;
|
||||||
|
|
||||||
|
// Returns 2/5 of the berries stolen from each Pokemon
|
||||||
|
const party = scene.getParty();
|
||||||
|
party.forEach(pokemon => {
|
||||||
|
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
|
||||||
|
const berryTypesAsArray: BerryType[] = [];
|
||||||
|
stolenBerries?.forEach(bMod => berryTypesAsArray.push(...new Array(bMod.stackCount).fill(bMod.berryType)));
|
||||||
|
const returnedBerryCount = Math.floor((berryTypesAsArray.length ?? 0) * 2 / 5);
|
||||||
|
|
||||||
|
if (returnedBerryCount > 0) {
|
||||||
|
for (let i = 0; i < returnedBerryCount; i++) {
|
||||||
|
// Shuffle remaining berry types and pop
|
||||||
|
Phaser.Math.RND.shuffle(berryTypesAsArray);
|
||||||
|
const randBerryType = berryTypesAsArray.pop();
|
||||||
|
|
||||||
|
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [randBerryType]) as BerryModifierType;
|
||||||
|
applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Animate berries being eaten
|
||||||
|
doGreedentEatBerries(scene);
|
||||||
|
doBerrySpritePile(scene, true);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Let it have the food
|
||||||
|
// Greedent joins the team, level equal to 2 below highest party member
|
||||||
|
const level = getHighestLevelPlayerPokemon(scene).level - 2;
|
||||||
|
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false);
|
||||||
|
greedent.moveset = [new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF)];
|
||||||
|
greedent.passive = true;
|
||||||
|
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false);
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
function doGreedentSpriteSteal(scene: BattleScene) {
|
||||||
|
const shakeDelay = 50;
|
||||||
|
const slideDelay = 500;
|
||||||
|
|
||||||
|
const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals?.getSpriteAtIndex(1);
|
||||||
|
|
||||||
|
scene.playSound("Follow Me");
|
||||||
|
scene.tweens.chain({
|
||||||
|
targets: greedentSprites,
|
||||||
|
tweens: [
|
||||||
|
{ // Slide Greedent diagonally
|
||||||
|
duration: slideDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
y: "+=75",
|
||||||
|
x: "-=65",
|
||||||
|
scale: 1.1
|
||||||
|
},
|
||||||
|
{ // Shake
|
||||||
|
duration: shakeDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
},
|
||||||
|
{ // Shake
|
||||||
|
duration: shakeDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
},
|
||||||
|
{ // Shake
|
||||||
|
duration: shakeDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
},
|
||||||
|
{ // Shake
|
||||||
|
duration: shakeDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
},
|
||||||
|
{ // Shake
|
||||||
|
duration: shakeDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
},
|
||||||
|
{ // Shake
|
||||||
|
duration: shakeDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
x: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
y: (randInt(2) > 0 ? "-=" : "+=") + 5,
|
||||||
|
},
|
||||||
|
{ // Slide Greedent diagonally
|
||||||
|
duration: slideDelay,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
y: "-=75",
|
||||||
|
x: "+=65",
|
||||||
|
scale: 1
|
||||||
|
},
|
||||||
|
{ // Bounce at the end
|
||||||
|
duration: 300,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
y: "-=20",
|
||||||
|
loop: 1,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doGreedentEatBerries(scene: BattleScene) {
|
||||||
|
const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals?.getSpriteAtIndex(1);
|
||||||
|
let index = 1;
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: greedentSprites,
|
||||||
|
duration: 150,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
y: "-=8",
|
||||||
|
loop: 5,
|
||||||
|
onStart: () => {
|
||||||
|
scene.playSound("PRSFX- Bug Bite");
|
||||||
|
},
|
||||||
|
onLoop: () => {
|
||||||
|
if (index % 2 === 0) {
|
||||||
|
scene.playSound("PRSFX- Bug Bite");
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param scene
|
||||||
|
* @param isEat - default false. Will "create" pile when false, and remove pile when true.
|
||||||
|
*/
|
||||||
|
function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
|
||||||
|
const berryAddDelay = 150;
|
||||||
|
let animationOrder = ["starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa"];
|
||||||
|
if (isEat) {
|
||||||
|
animationOrder = animationOrder.reverse();
|
||||||
|
}
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
animationOrder.forEach((berry, i) => {
|
||||||
|
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
|
||||||
|
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
|
||||||
|
const sprites = encounter.introVisuals?.getSpriteAtIndex(introVisualsIndex);
|
||||||
|
if (sprites) {
|
||||||
|
sprite = sprites[0];
|
||||||
|
tintSprite = sprites[1];
|
||||||
|
}
|
||||||
|
scene.time.delayedCall(berryAddDelay * i + 400, () => {
|
||||||
|
if (sprite) {
|
||||||
|
sprite.setVisible(!isEat);
|
||||||
|
}
|
||||||
|
if (tintSprite) {
|
||||||
|
tintSprite.setVisible(!isEat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animate Petaya berry falling off the pile
|
||||||
|
if (berry === "petaya" && sprite && tintSprite && !isEat) {
|
||||||
|
scene.time.delayedCall(200, () => {
|
||||||
|
doBerryBounce(scene, [sprite, tintSprite], 30, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: integer) {
|
||||||
|
let bouncePower = 1;
|
||||||
|
let bounceYOffset = yd;
|
||||||
|
|
||||||
|
const doBounce = () => {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: berrySprites,
|
||||||
|
y: "+=" + bounceYOffset,
|
||||||
|
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
|
||||||
|
duration: bouncePower * baseBounceDuration,
|
||||||
|
ease: "Cubic.easeIn",
|
||||||
|
onComplete: () => {
|
||||||
|
bouncePower = bouncePower > 0.01 ? bouncePower * 0.5 : 0;
|
||||||
|
|
||||||
|
if (bouncePower) {
|
||||||
|
bounceYOffset = bounceYOffset * bouncePower;
|
||||||
|
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: berrySprites,
|
||||||
|
y: "-=" + bounceYOffset,
|
||||||
|
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
|
||||||
|
duration: bouncePower * baseBounceDuration,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
onComplete: () => doBounce()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
doBounce();
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
import { leaveEncounterWithoutBattle, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
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 MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:offerYouCantRefuse";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Offer You Can't Refuse encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/72 | GitHub Issue #72}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.LIEPARD.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 0,
|
||||||
|
y: -4,
|
||||||
|
yShadow: -4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "rich_kid_m",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: 2,
|
||||||
|
y: 5,
|
||||||
|
yShadow: 5
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const pokemon = getHighestStatTotalPlayerPokemon(scene, false);
|
||||||
|
const price = scene.getWaveMoneyAmount(10);
|
||||||
|
|
||||||
|
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("price", price.toString());
|
||||||
|
|
||||||
|
// Store pokemon and price
|
||||||
|
encounter.misc = {
|
||||||
|
pokemon: pokemon,
|
||||||
|
price: price
|
||||||
|
};
|
||||||
|
|
||||||
|
// If player meets the combo OR requirements for option 2, populate the token
|
||||||
|
const opt2Req = encounter.options[1].primaryPokemonRequirements[0];
|
||||||
|
if (opt2Req.meetsRequirement(scene)) {
|
||||||
|
const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"];
|
||||||
|
const moveToken = encounter.dialogueTokens["option2PrimaryMove"];
|
||||||
|
if (abilityToken) {
|
||||||
|
encounter.setDialogueToken("moveOrAbility", abilityToken);
|
||||||
|
} else if (moveToken) {
|
||||||
|
encounter.setDialogueToken("moveOrAbility", moveToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Update money and remove pokemon from party
|
||||||
|
updatePlayerMoney(scene, encounter.misc.price);
|
||||||
|
scene.removePokemonFromPlayerParty(encounter.misc.pokemon);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give the player a Shiny charm
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.SHINY_CHARM));
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement(
|
||||||
|
new MoveRequirement(EXTORTION_MOVES),
|
||||||
|
new AbilityRequirement(EXTORTION_ABILITIES))
|
||||||
|
)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}.option.2.tooltip_disabled`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Extort the rich kid for money
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Update money and remove pokemon from party
|
||||||
|
updatePlayerMoney(scene, encounter.misc.price);
|
||||||
|
|
||||||
|
setEncounterExp(scene, encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true);
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,267 @@
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import {
|
||||||
|
EnemyPartyConfig, generateModifierType, generateModifierTypeOption,
|
||||||
|
initBattleWithEnemyConfig,
|
||||||
|
leaveEncounterWithoutBattle, setEncounterExp,
|
||||||
|
setEncounterRewards
|
||||||
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import {
|
||||||
|
BerryModifierType,
|
||||||
|
getPartyLuckValue,
|
||||||
|
ModifierPoolType,
|
||||||
|
ModifierTypeOption, modifierTypes,
|
||||||
|
regenerateModifierPoolThresholds,
|
||||||
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import { applyModifierTypeToPlayerPokemon, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
|
import i18next from "#app/plugins/i18n";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:berriesAbound";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Berries Abound encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/24 | GitHub Issue #24}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const BerriesAboundEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.BERRIES_ABOUND)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([]) // Set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
||||||
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
pokemonConfigs: [{
|
||||||
|
level: level,
|
||||||
|
species: bossSpecies,
|
||||||
|
dataSource: new PokemonData(bossPokemon),
|
||||||
|
isBoss: true
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
// Calculate the number of extra berries that player receives
|
||||||
|
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
|
||||||
|
const numBerries =
|
||||||
|
scene.currentBattle.waveIndex > 160 ? 7
|
||||||
|
: scene.currentBattle.waveIndex > 120 ? 5
|
||||||
|
: scene.currentBattle.waveIndex > 40 ? 4 : 2;
|
||||||
|
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
||||||
|
encounter.misc = { numBerries };
|
||||||
|
|
||||||
|
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: "berry_bush",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
x: 25,
|
||||||
|
y: -6,
|
||||||
|
yShadow: -7,
|
||||||
|
disableAnimation: true,
|
||||||
|
hasShadow: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: spriteKey,
|
||||||
|
fileRoot: fileRoot,
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 0.25,
|
||||||
|
x: -5,
|
||||||
|
repeat: true,
|
||||||
|
isPokemon: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get fastest party pokemon for option 2
|
||||||
|
const fastestPokemon = getHighestStatPlayerPokemon(scene, Stat.SPD, true);
|
||||||
|
encounter.misc.fastestPokemon = fastestPokemon;
|
||||||
|
encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD);
|
||||||
|
encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender());
|
||||||
|
|
||||||
|
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;
|
||||||
|
const numBerries = encounter.misc.numBerries;
|
||||||
|
|
||||||
|
const doBerryRewards = async () => {
|
||||||
|
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
||||||
|
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
|
||||||
|
|
||||||
|
// Generate a random berry and give it to the first Pokemon with room for it
|
||||||
|
for (let i = 0; i < numBerries; i++) {
|
||||||
|
await tryGiveBerry(scene);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const shopOptions: ModifierTypeOption[] = [];
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
// Generate shop berries
|
||||||
|
shopOptions.push(generateModifierTypeOption(scene, modifierTypes.BERRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
||||||
|
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick race for berries
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const fastestPokemon = encounter.misc.fastestPokemon;
|
||||||
|
const enemySpeed = encounter.misc.enemySpeed;
|
||||||
|
const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
|
||||||
|
const numBerries = encounter.misc.numBerries;
|
||||||
|
|
||||||
|
const shopOptions: ModifierTypeOption[] = [];
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
// Generate shop berries
|
||||||
|
shopOptions.push(generateModifierTypeOption(scene, modifierTypes.BERRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speedDiff < 1) {
|
||||||
|
// Caught and attacked by boss, gets +1 to all stats at start of fight
|
||||||
|
const doBerryRewards = async () => {
|
||||||
|
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
||||||
|
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
|
||||||
|
|
||||||
|
// Generate a random berry and give it to the first Pokemon with room for it
|
||||||
|
for (let i = 0; i < numBerries; i++) {
|
||||||
|
await tryGiveBerry(scene);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0];
|
||||||
|
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
||||||
|
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||||
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.boss_enraged`);
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
||||||
|
};
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
||||||
|
await showEncounterText(scene, `${namespace}.option.2.selected_bad`);
|
||||||
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
|
||||||
|
const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1)/0.08), numBerries), 2);
|
||||||
|
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
|
||||||
|
const doFasterBerryRewards = async () => {
|
||||||
|
const berryText = numBerriesGrabbed + " " + i18next.t(`${namespace}.berries`);
|
||||||
|
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
|
||||||
|
|
||||||
|
// Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first)
|
||||||
|
for (let i = 0; i < numBerriesGrabbed; i++) {
|
||||||
|
await tryGiveBerry(scene, fastestPokemon);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
|
||||||
|
await showEncounterText(scene, `${namespace}.option.2.selected`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
|
||||||
|
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
|
||||||
|
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
|
||||||
|
|
||||||
|
const party = scene.getParty();
|
||||||
|
|
||||||
|
// Will try to apply to prioritized pokemon first, then do normal application method if it fails
|
||||||
|
if (prioritizedPokemon) {
|
||||||
|
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier
|
||||||
|
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
||||||
|
|
||||||
|
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the party until berry was successfully given
|
||||||
|
for (const pokemon of party) {
|
||||||
|
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier
|
||||||
|
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
||||||
|
|
||||||
|
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,496 @@
|
||||||
|
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { randSeedInt, randSeedShuffle } from "#app/utils";
|
||||||
|
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { Ability } from "#app/data/ability";
|
||||||
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { MoveCategory } from "#app/data/move";
|
||||||
|
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:clowningAround";
|
||||||
|
|
||||||
|
const RANDOM_ABILITY_POOL = [
|
||||||
|
Abilities.STURDY,
|
||||||
|
Abilities.PICKUP,
|
||||||
|
Abilities.INTIMIDATE,
|
||||||
|
Abilities.GUTS,
|
||||||
|
Abilities.DROUGHT,
|
||||||
|
Abilities.DRIZZLE,
|
||||||
|
Abilities.SNOW_WARNING,
|
||||||
|
Abilities.SAND_STREAM,
|
||||||
|
Abilities.ELECTRIC_SURGE,
|
||||||
|
Abilities.PSYCHIC_SURGE,
|
||||||
|
Abilities.GRASSY_SURGE,
|
||||||
|
Abilities.MISTY_SURGE,
|
||||||
|
Abilities.MAGICIAN,
|
||||||
|
Abilities.SHEER_FORCE,
|
||||||
|
Abilities.PRANKSTER
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clowning Around encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/69 | GitHub Issue #69}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const ClowningAroundEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.CLOWNING_AROUND)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(80, 180)
|
||||||
|
.withAnimations(EncounterAnim.SMOKESCREEN)
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.MR_MIME.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: -25,
|
||||||
|
tint: 0.3,
|
||||||
|
y: -3,
|
||||||
|
yShadow: -3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: Species.BLACEPHALON.toString(),
|
||||||
|
fileRoot: "pokemon/exp",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 25,
|
||||||
|
tint: 0.3,
|
||||||
|
y: -3,
|
||||||
|
yShadow: -3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "harlequin",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: 0,
|
||||||
|
y: 2,
|
||||||
|
yShadow: 2
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
const clownTrainerType = TrainerType.HARLEQUIN;
|
||||||
|
const clownConfig = trainerConfigs[clownTrainerType].copy();
|
||||||
|
const clownPartyTemplate = new TrainerPartyCompoundTemplate(
|
||||||
|
new TrainerPartyTemplate(1, PartyMemberStrength.STRONG),
|
||||||
|
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER));
|
||||||
|
clownConfig.setPartyTemplates(clownPartyTemplate);
|
||||||
|
clownConfig.setDoubleOnly();
|
||||||
|
// @ts-ignore
|
||||||
|
clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists
|
||||||
|
|
||||||
|
// Generate random ability for Blacephalon from pool
|
||||||
|
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
|
||||||
|
encounter.setDialogueToken("ability", new Ability(ability, 3).name);
|
||||||
|
encounter.misc = { ability };
|
||||||
|
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: clownConfig,
|
||||||
|
pokemonConfigs: [ // Overrides first 2 pokemon to be Mr. Mime and Blacephalon
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.MR_MIME),
|
||||||
|
isBoss: true,
|
||||||
|
moveSet: [Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC]
|
||||||
|
},
|
||||||
|
{ // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
|
||||||
|
species: getPokemonSpecies(Species.BLACEPHALON),
|
||||||
|
mysteryEncounterData: new MysteryEncounterPokemonData(undefined, ability, undefined, [randSeedInt(18), randSeedInt(18)]),
|
||||||
|
isBoss: true,
|
||||||
|
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
doubleBattle: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load animations/sfx for start of fight moves
|
||||||
|
loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn battle
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
|
||||||
|
// TODO: when Magic Room and Wonder Room are implemented, add those to start of battle
|
||||||
|
encounter.startOfBattleEffects.push(
|
||||||
|
{ // Mr. Mime copies the Blacephalon's random ability
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.ENEMY_2],
|
||||||
|
move: new PokemonMove(Moves.ROLE_PLAY),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.TAUNT),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
|
targets: [BattlerIndex.PLAYER_2],
|
||||||
|
move: new PokemonMove(Moves.TAUNT),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
// After the battle, offer the player the opportunity to permanently swap ability
|
||||||
|
const abilityWasSwapped = await handleSwapAbility(scene);
|
||||||
|
if (abilityWasSwapped) {
|
||||||
|
await showEncounterText(scene, `${namespace}.option.1.ability_gained`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play animations once ability swap is complete
|
||||||
|
// Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: scene.currentBattle.trainer,
|
||||||
|
x: "+=16",
|
||||||
|
y: "-=16",
|
||||||
|
alpha: 0,
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
duration: 250
|
||||||
|
});
|
||||||
|
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||||
|
background.playWithoutTargets(scene, 230, 40, 2);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected_2`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected_3`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Swap player's items on pokemon with the most items
|
||||||
|
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
|
||||||
|
// So Vitamins, form change items, etc. are not included
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
const party = scene.getParty();
|
||||||
|
let mostHeldItemsPokemon = party[0];
|
||||||
|
let count = mostHeldItemsPokemon.getHeldItems()
|
||||||
|
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
||||||
|
.reduce((v, m) => v + m.stackCount, 0);
|
||||||
|
|
||||||
|
party.forEach(pokemon => {
|
||||||
|
const nextCount = pokemon.getHeldItems()
|
||||||
|
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
||||||
|
.reduce((v, m) => v + m.stackCount, 0);
|
||||||
|
if (nextCount > count) {
|
||||||
|
mostHeldItemsPokemon = pokemon;
|
||||||
|
count = nextCount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
encounter.setDialogueToken("switchPokemon", mostHeldItemsPokemon.getNameToRender());
|
||||||
|
|
||||||
|
const items = mostHeldItemsPokemon.getHeldItems();
|
||||||
|
|
||||||
|
// Shuffles Berries (if they have any)
|
||||||
|
let numBerries = 0;
|
||||||
|
items.filter(m => m instanceof BerryModifier)
|
||||||
|
.forEach(m => {
|
||||||
|
numBerries += m.stackCount;
|
||||||
|
scene.removeModifier(m);
|
||||||
|
});
|
||||||
|
|
||||||
|
generateItemsOfTier(scene, mostHeldItemsPokemon, numBerries, "Berries");
|
||||||
|
|
||||||
|
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
|
||||||
|
let numUltra = 0;
|
||||||
|
let numRogue = 0;
|
||||||
|
items.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
||||||
|
.forEach(m => {
|
||||||
|
const type = m.type.withTierFromPool();
|
||||||
|
const tier = type.tier ?? ModifierTier.ULTRA;
|
||||||
|
if (type.id === "LUCKY_EGG" || tier === ModifierTier.ULTRA) {
|
||||||
|
numUltra += m.stackCount;
|
||||||
|
scene.removeModifier(m);
|
||||||
|
} else if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
|
||||||
|
numRogue += m.stackCount;
|
||||||
|
scene.removeModifier(m);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
generateItemsOfTier(scene, mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
|
||||||
|
generateItemsOfTier(scene, mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Play animations
|
||||||
|
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||||
|
background.playWithoutTargets(scene, 230, 40, 2);
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected_2`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected_3`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Swap player's types on all party pokemon
|
||||||
|
// If a Pokemon had a single type prior, they will still have a single type after
|
||||||
|
for (const pokemon of scene.getParty()) {
|
||||||
|
const originalTypes = pokemon.getTypes(false, false, true);
|
||||||
|
|
||||||
|
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
|
||||||
|
// Makes the "randomness" of the shuffle slightly less punishing
|
||||||
|
let priorityTypes = pokemon.moveset
|
||||||
|
.filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS)
|
||||||
|
.map(move => move!.getMove().type);
|
||||||
|
if (priorityTypes?.length > 0) {
|
||||||
|
priorityTypes = [...new Set(priorityTypes)];
|
||||||
|
randSeedShuffle(priorityTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
let newTypes;
|
||||||
|
if (!originalTypes || originalTypes.length < 1) {
|
||||||
|
newTypes = priorityTypes?.length > 0 ? [priorityTypes.pop()] : [(randSeedInt(18) as Type)];
|
||||||
|
} else {
|
||||||
|
newTypes = originalTypes.map(m => {
|
||||||
|
if (priorityTypes?.length > 0) {
|
||||||
|
const ret = priorityTypes.pop();
|
||||||
|
randSeedShuffle(priorityTypes);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return randSeedInt(18) as Type;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pokemon.mysteryEncounterData) {
|
||||||
|
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes);
|
||||||
|
} else {
|
||||||
|
pokemon.mysteryEncounterData.types = newTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Play animations
|
||||||
|
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||||
|
background.playWithoutTargets(scene, 230, 40, 2);
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
async function handleSwapAbility(scene: BattleScene) {
|
||||||
|
return new Promise<boolean>(async resolve => {
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.option.1.apply_ability_dialogue`, `${namespace}.speaker`);
|
||||||
|
await showEncounterText(scene, `${namespace}.option.1.apply_ability_message`);
|
||||||
|
|
||||||
|
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||||
|
displayYesNoOptions(scene, resolve);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayYesNoOptions(scene: BattleScene, resolve) {
|
||||||
|
showEncounterText(scene, `${namespace}.option.1.ability_prompt`, 500, false);
|
||||||
|
const fullOptions = [
|
||||||
|
{
|
||||||
|
label: i18next.t("menu:yes"),
|
||||||
|
handler: () => {
|
||||||
|
onYesAbilitySwap(scene, resolve);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18next.t("menu:no"),
|
||||||
|
handler: () => {
|
||||||
|
resolve(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const config: OptionSelectConfig = {
|
||||||
|
options: fullOptions,
|
||||||
|
maxOptions: 7,
|
||||||
|
yOffset: 0
|
||||||
|
};
|
||||||
|
scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onYesAbilitySwap(scene: BattleScene, resolve) {
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Do ability swap
|
||||||
|
if (!pokemon.mysteryEncounterData) {
|
||||||
|
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, Abilities.AERILATE);
|
||||||
|
}
|
||||||
|
pokemon.mysteryEncounterData.ability = scene.currentBattle.mysteryEncounter.misc.ability;
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
||||||
|
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPokemonNotSelected = () => {
|
||||||
|
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||||
|
displayYesNoOptions(scene, resolve);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
selectPokemonForOption(scene, onPokemonSelected, onPokemonNotSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItems: integer, tier: ModifierTier | "Berries") {
|
||||||
|
// These pools have to be defined at runtime so that modifierTypes exist
|
||||||
|
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
|
||||||
|
// This is to prevent "over-generating" a random item of a certain type during item swaps
|
||||||
|
const ultraPool = [
|
||||||
|
[modifierTypes.REVIVER_SEED, 1],
|
||||||
|
[modifierTypes.GOLDEN_PUNCH, 5],
|
||||||
|
[modifierTypes.ATTACK_TYPE_BOOSTER, 99],
|
||||||
|
[modifierTypes.QUICK_CLAW, 3],
|
||||||
|
[modifierTypes.WIDE_LENS, 3]
|
||||||
|
];
|
||||||
|
|
||||||
|
const roguePool = [
|
||||||
|
[modifierTypes.LEFTOVERS, 4],
|
||||||
|
[modifierTypes.SHELL_BELL, 4],
|
||||||
|
[modifierTypes.SOUL_DEW, 10],
|
||||||
|
[modifierTypes.SOOTHE_BELL, 3],
|
||||||
|
[modifierTypes.SCOPE_LENS, 1],
|
||||||
|
[modifierTypes.BATON, 1],
|
||||||
|
[modifierTypes.FOCUS_BAND, 5],
|
||||||
|
[modifierTypes.KINGS_ROCK, 3],
|
||||||
|
[modifierTypes.GRIP_CLAW, 5]
|
||||||
|
];
|
||||||
|
|
||||||
|
const berryPool = [
|
||||||
|
[BerryType.APICOT, 3],
|
||||||
|
[BerryType.ENIGMA, 2],
|
||||||
|
[BerryType.GANLON, 3],
|
||||||
|
[BerryType.LANSAT, 3],
|
||||||
|
[BerryType.LEPPA, 2],
|
||||||
|
[BerryType.LIECHI, 3],
|
||||||
|
[BerryType.LUM, 2],
|
||||||
|
[BerryType.PETAYA, 3],
|
||||||
|
[BerryType.SALAC, 2],
|
||||||
|
[BerryType.SITRUS, 2],
|
||||||
|
[BerryType.STARF, 3]
|
||||||
|
];
|
||||||
|
|
||||||
|
let pool: any[];
|
||||||
|
if (tier === "Berries") {
|
||||||
|
pool = berryPool;
|
||||||
|
} else {
|
||||||
|
pool = tier === ModifierTier.ULTRA ? ultraPool : roguePool;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numItems; i++) {
|
||||||
|
const randIndex = randSeedInt(pool.length);
|
||||||
|
const newItemType = pool[randIndex];
|
||||||
|
let newMod;
|
||||||
|
if (tier === "Berries") {
|
||||||
|
newMod = generateModifierType(scene, modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType;
|
||||||
|
} else {
|
||||||
|
newMod = generateModifierType(scene, newItemType[0]) as PokemonHeldItemModifierType;
|
||||||
|
}
|
||||||
|
applyModifierTypeToPlayerPokemon(scene, pokemon, newMod);
|
||||||
|
// Decrement max stacks and remove from pool if at max
|
||||||
|
newItemType[1]--;
|
||||||
|
if (newItemType[1] <= 0) {
|
||||||
|
pool.splice(randIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,295 @@
|
||||||
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { Biome } from "#enums/biome";
|
||||||
|
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
|
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { catchPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { PokeballType } from "#enums/pokeball";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
||||||
|
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:dancingLessons";
|
||||||
|
|
||||||
|
// Fire form
|
||||||
|
const BAILE_STYLE_BIOMES = [
|
||||||
|
Biome.VOLCANO,
|
||||||
|
Biome.BEACH,
|
||||||
|
Biome.ISLAND,
|
||||||
|
Biome.WASTELAND,
|
||||||
|
Biome.MOUNTAIN,
|
||||||
|
Biome.BADLANDS,
|
||||||
|
Biome.DESERT
|
||||||
|
];
|
||||||
|
|
||||||
|
// Electric form
|
||||||
|
const POM_POM_STYLE_BIOMES = [
|
||||||
|
Biome.CONSTRUCTION_SITE,
|
||||||
|
Biome.POWER_PLANT,
|
||||||
|
Biome.FACTORY,
|
||||||
|
Biome.LABORATORY,
|
||||||
|
Biome.SLUM,
|
||||||
|
Biome.METROPOLIS,
|
||||||
|
Biome.DOJO
|
||||||
|
];
|
||||||
|
|
||||||
|
// Psychic form
|
||||||
|
const PAU_STYLE_BIOMES = [
|
||||||
|
Biome.JUNGLE,
|
||||||
|
Biome.FAIRY_CAVE,
|
||||||
|
Biome.MEADOW,
|
||||||
|
Biome.PLAINS,
|
||||||
|
Biome.GRASS,
|
||||||
|
Biome.TALL_GRASS,
|
||||||
|
Biome.FOREST
|
||||||
|
];
|
||||||
|
|
||||||
|
// Ghost form
|
||||||
|
const SENSU_STYLE_BIOMES = [
|
||||||
|
Biome.RUINS,
|
||||||
|
Biome.SWAMP,
|
||||||
|
Biome.CAVE,
|
||||||
|
Biome.ABYSS,
|
||||||
|
Biome.GRAVEYARD,
|
||||||
|
Biome.LAKE,
|
||||||
|
Biome.TEMPLE
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dancing Lessons encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/130 | GitHub Issue #130}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const DancingLessonsEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DANCING_LESSONS)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withIntroSpriteConfigs([]) // Uses a real Pokemon sprite instead of ME Intro Visuals
|
||||||
|
.withAnimations(EncounterAnim.DANCE)
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
|
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon());
|
||||||
|
danceAnim.play(scene);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
const species = getPokemonSpecies(Species.ORICORIO);
|
||||||
|
const enemyPokemon = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false);
|
||||||
|
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
|
||||||
|
if (enemyPokemon.moveset.length < 4) {
|
||||||
|
enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE));
|
||||||
|
} else {
|
||||||
|
enemyPokemon.moveset[0] = new PokemonMove(Moves.REVELATION_DANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the form index based on the biome
|
||||||
|
// Defaults to Baile style if somehow nothing matches
|
||||||
|
const currentBiome = scene.arena.biomeType;
|
||||||
|
if (BAILE_STYLE_BIOMES.includes(currentBiome)) {
|
||||||
|
enemyPokemon.formIndex = 0;
|
||||||
|
} else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) {
|
||||||
|
enemyPokemon.formIndex = 1;
|
||||||
|
} else if (PAU_STYLE_BIOMES.includes(currentBiome)) {
|
||||||
|
enemyPokemon.formIndex = 2;
|
||||||
|
} else if (SENSU_STYLE_BIOMES.includes(currentBiome)) {
|
||||||
|
enemyPokemon.formIndex = 3;
|
||||||
|
} else {
|
||||||
|
enemyPokemon.formIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oricorioData = new PokemonData(enemyPokemon);
|
||||||
|
|
||||||
|
// Adds a real Pokemon sprite to the field (required for the animation)
|
||||||
|
scene.currentBattle.enemyParty[0] = enemyPokemon;
|
||||||
|
scene.field.add(enemyPokemon);
|
||||||
|
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
pokemonConfigs: [{
|
||||||
|
species: species,
|
||||||
|
dataSource: oricorioData,
|
||||||
|
isBoss: true,
|
||||||
|
// Gets +1 to all stats on battle start
|
||||||
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`);
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
encounter.misc = {
|
||||||
|
oricorioData
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
|
||||||
|
encounter.startOfBattleEffects.push({
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.REVELATION_DANCE),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.BATON], fillRemaining: true });
|
||||||
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Learn its Dance
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
|
scene.unshiftPhase(new LearnMovePhase(scene, scene.getParty().indexOf(pokemon), Moves.REVELATION_DANCE));
|
||||||
|
|
||||||
|
// Play animation again to "learn" the dance
|
||||||
|
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon());
|
||||||
|
danceAnim.play(scene);
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Learn its Dance
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new MoveRequirement(DANCING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens automatically
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}.option.3.disabled_tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Open menu for selecting pokemon with a Dancing move
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for nature selection
|
||||||
|
return pokemon.moveset
|
||||||
|
.filter(move => move && DANCING_MOVES.includes(move.getMove().id))
|
||||||
|
.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and second option selected
|
||||||
|
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("selectedMove", move.getName());
|
||||||
|
encounter.misc.selectedMove = move;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that have a Dancing move can be selected
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Show the Oricorio a dance, and recruit it
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const oricorio = encounter.misc.oricorioData.toPokemon(scene);
|
||||||
|
oricorio.passive = true;
|
||||||
|
|
||||||
|
// Ensure the Oricorio's moveset gains the Dance move the player used
|
||||||
|
const move = encounter.misc.selectedMove?.getMove().id;
|
||||||
|
if (!oricorio.moveset.some(m => m.getMove().id === move)) {
|
||||||
|
if (oricorio.moveset.length < 4) {
|
||||||
|
oricorio.moveset.push(new PokemonMove(move));
|
||||||
|
} else {
|
||||||
|
oricorio.moveset[3] = new PokemonMove(move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,187 @@
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
|
||||||
|
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||||
|
|
||||||
|
/** i18n namespace for encounter */
|
||||||
|
const namespace = "mysteryEncounter:darkDeal";
|
||||||
|
|
||||||
|
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */
|
||||||
|
const excludedBosses = [
|
||||||
|
Species.NECROZMA,
|
||||||
|
Species.COSMOG,
|
||||||
|
Species.COSMOEM,
|
||||||
|
Species.SOLGALEO,
|
||||||
|
Species.LUNALA,
|
||||||
|
Species.ETERNATUS,
|
||||||
|
Species.NIHILEGO,
|
||||||
|
Species.BUZZWOLE,
|
||||||
|
Species.PHEROMOSA,
|
||||||
|
Species.XURKITREE,
|
||||||
|
Species.CELESTEELA,
|
||||||
|
Species.KARTANA,
|
||||||
|
Species.GUZZLORD,
|
||||||
|
Species.POIPOLE,
|
||||||
|
Species.NAGANADEL,
|
||||||
|
Species.STAKATAKA,
|
||||||
|
Species.BLACEPHALON,
|
||||||
|
Species.GREAT_TUSK,
|
||||||
|
Species.SCREAM_TAIL,
|
||||||
|
Species.BRUTE_BONNET,
|
||||||
|
Species.FLUTTER_MANE,
|
||||||
|
Species.SLITHER_WING,
|
||||||
|
Species.SANDY_SHOCKS,
|
||||||
|
Species.ROARING_MOON,
|
||||||
|
Species.KORAIDON,
|
||||||
|
Species.WALKING_WAKE,
|
||||||
|
Species.GOUGING_FIRE,
|
||||||
|
Species.RAGING_BOLT,
|
||||||
|
Species.IRON_TREADS,
|
||||||
|
Species.IRON_BUNDLE,
|
||||||
|
Species.IRON_HANDS,
|
||||||
|
Species.IRON_JUGULIS,
|
||||||
|
Species.IRON_MOTH,
|
||||||
|
Species.IRON_THORNS,
|
||||||
|
Species.IRON_VALIANT,
|
||||||
|
Species.MIRAIDON,
|
||||||
|
Species.IRON_LEAVES,
|
||||||
|
Species.IRON_BOULDER,
|
||||||
|
Species.IRON_CROWN,
|
||||||
|
Species.MEW,
|
||||||
|
Species.CELEBI,
|
||||||
|
Species.DEOXYS,
|
||||||
|
Species.JIRACHI,
|
||||||
|
Species.PHIONE,
|
||||||
|
Species.MANAPHY,
|
||||||
|
Species.ARCEUS,
|
||||||
|
Species.VICTINI,
|
||||||
|
Species.MELTAN,
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "mad_scientist_m",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "dark_deal_porygon",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
|
||||||
|
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.option.1.selected_dialogue`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
|
||||||
|
// Will never return last battle able mon and instead pick fainted/unable to battle
|
||||||
|
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
|
||||||
|
scene.removePokemonFromPlayerParty(removedPokemon);
|
||||||
|
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.getNameToRender());
|
||||||
|
|
||||||
|
// Store removed pokemon types
|
||||||
|
scene.currentBattle.mysteryEncounter.misc = [
|
||||||
|
removedPokemon.species.type1,
|
||||||
|
];
|
||||||
|
if (removedPokemon.species.type2) {
|
||||||
|
scene.currentBattle.mysteryEncounter.misc.push(removedPokemon.species.type2);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give the player 5 Rogue Balls
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ROGUE_BALL));
|
||||||
|
|
||||||
|
// Start encounter with random legendary (7-10 starter strength) that has level additive
|
||||||
|
const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[];
|
||||||
|
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
|
||||||
|
const roll = randSeedInt(100);
|
||||||
|
const starterTier: number | [number, number] =
|
||||||
|
roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10];
|
||||||
|
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
|
||||||
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
|
species: bossSpecies,
|
||||||
|
isBoss: true,
|
||||||
|
};
|
||||||
|
if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) {
|
||||||
|
pokemonConfig.formIndex = 0;
|
||||||
|
}
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
pokemonConfigs: [pokemonConfig],
|
||||||
|
};
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.build();
|
|
@ -0,0 +1,302 @@
|
||||||
|
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { HealingBoosterModifier, HiddenAbilityRateBoosterModifier, LevelIncrementBoosterModifier, PokemonHeldItemModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import i18next from "#app/plugins/i18n";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:delibirdy";
|
||||||
|
|
||||||
|
/** Berries only */
|
||||||
|
const OPTION_2_ALLOWED_MODIFIERS = ["BerryModifier", "PokemonInstantReviveModifier"];
|
||||||
|
|
||||||
|
/** Disallowed items are berries, Reviver Seeds, and Vitamins (form change items and fusion items are not PokemonHeldItemModifiers) */
|
||||||
|
const OPTION_3_DISALLOWED_MODIFIERS = [
|
||||||
|
"BerryModifier",
|
||||||
|
"PokemonInstantReviveModifier",
|
||||||
|
"TerastallizeModifier",
|
||||||
|
"PokemonBaseStatModifier",
|
||||||
|
"PokemonBaseStatTotalModifier"
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delibird-y encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/57 | GitHub Issue #57}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const DelibirdyEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Must have enough money for it to spawn at the very least
|
||||||
|
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
|
||||||
|
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),
|
||||||
|
new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true)
|
||||||
|
))
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.DELIBIRD,
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
startFrame: 38,
|
||||||
|
scale: 0.94
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.DELIBIRD,
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
scale: 1.06
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.DELIBIRD,
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
startFrame: 65,
|
||||||
|
x: 1,
|
||||||
|
y: 5,
|
||||||
|
yShadow: 5
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(0, 2.75) // Must have money to spawn
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give the player an Ability Charm
|
||||||
|
// Check if the player has max stacks of that item already
|
||||||
|
const existing = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM));
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS))
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.2.select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Get Pokemon held items and filter for valid ones
|
||||||
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
|
return OPTION_2_ALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: modifier.type.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and item selected
|
||||||
|
encounter.setDialogueToken("chosenItem", modifier.type.name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
chosenModifier: modifier,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const modifier = encounter.misc.chosenModifier;
|
||||||
|
|
||||||
|
// Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed
|
||||||
|
if (modifier.type.name.includes("Berry")) {
|
||||||
|
// Check if the player has max stacks of that Candy Jar already
|
||||||
|
const existing = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if the player has max stacks of that Healing Charm already
|
||||||
|
const existing = scene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the modifier if its stacks go to 0
|
||||||
|
modifier.stackCount -= 1;
|
||||||
|
if (modifier.stackCount === 0) {
|
||||||
|
scene.removeModifier(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPrimaryPokemonRequirement(new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true))
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Get Pokemon held items and filter for valid ones
|
||||||
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
|
return !OPTION_3_DISALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: modifier.type.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and item selected
|
||||||
|
encounter.setDialogueToken("chosenItem", modifier.type.name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
chosenModifier: modifier,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const modifier = encounter.misc.chosenModifier;
|
||||||
|
|
||||||
|
// Check if the player has max stacks of Berry Pouch already
|
||||||
|
const existing = scene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier;
|
||||||
|
|
||||||
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], shellBell);
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), undefined, true);
|
||||||
|
} else {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the modifier if its stacks go to 0
|
||||||
|
modifier.stackCount -= 1;
|
||||||
|
if (modifier.stackCount === 0) {
|
||||||
|
scene.removeModifier(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,163 @@
|
||||||
|
import {
|
||||||
|
leaveEncounterWithoutBattle,
|
||||||
|
setEncounterRewards,
|
||||||
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, {
|
||||||
|
MysteryEncounterBuilder,
|
||||||
|
} from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
|
||||||
|
/** i18n namespace for encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 100)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "b2w2_lady",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.FURFROU,
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 30,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose TMs
|
||||||
|
const modifiers: ModifierTypeFunc[] = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 4) {
|
||||||
|
// 2/2/1 weight on TM rarity
|
||||||
|
const roll = randSeedInt(5);
|
||||||
|
if (roll < 2) {
|
||||||
|
modifiers.push(modifierTypes.TM_COMMON);
|
||||||
|
} else if (roll < 4) {
|
||||||
|
modifiers.push(modifierTypes.TM_GREAT);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.TM_ULTRA);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose Vitamins
|
||||||
|
const modifiers: ModifierTypeFunc[] = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 3) {
|
||||||
|
// 2/1 weight on base stat booster vs PP Up
|
||||||
|
const roll = randSeedInt(3);
|
||||||
|
if (roll === 0) {
|
||||||
|
modifiers.push(modifierTypes.PP_UP);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.BASE_STAT_BOOSTER);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose X Items
|
||||||
|
const modifiers: ModifierTypeFunc[] = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 5) {
|
||||||
|
// 4/1 weight on base stat booster vs Dire Hit
|
||||||
|
const roll = randSeedInt(5);
|
||||||
|
if (roll === 0) {
|
||||||
|
modifiers.push(modifierTypes.DIRE_HIT);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.TEMP_STAT_BOOSTER);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.4.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.4.tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose Pokeballs
|
||||||
|
const modifiers: ModifierTypeFunc[] = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 4) {
|
||||||
|
// 10/30/20/5 weight on pokeballs
|
||||||
|
const roll = randSeedInt(65);
|
||||||
|
if (roll < 10) {
|
||||||
|
modifiers.push(modifierTypes.POKEBALL);
|
||||||
|
} else if (roll < 40) {
|
||||||
|
modifiers.push(modifierTypes.GREAT_BALL);
|
||||||
|
} else if (roll < 60) {
|
||||||
|
modifiers.push(modifierTypes.ULTRA_BALL);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.ROGUE_BALL);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.build();
|
|
@ -0,0 +1,320 @@
|
||||||
|
import { MoveCategory } from "#app/data/move";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { TempBattleStat } from "#app/data/temp-battle-stat";
|
||||||
|
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
/** i18n namespace for the encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "preschooler_m",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "teacher",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "preschooler_f",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for Pokemon move valid for this option
|
||||||
|
return pokemon.moveset.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and move selected
|
||||||
|
const correctMove = move.getMove().category === MoveCategory.PHYSICAL;
|
||||||
|
encounter.setDialogueToken("moveCategory", "Physical");
|
||||||
|
if (!correctMove) {
|
||||||
|
encounter.options[0].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.incorrect`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.lesson_learned`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_bad`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("move", move.getName());
|
||||||
|
encounter.options[0].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_good`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
}
|
||||||
|
encounter.misc = {
|
||||||
|
correctMove: correctMove,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter.misc.correctMove) {
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ATK]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.DEF]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT),
|
||||||
|
];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for Pokemon move valid for this option
|
||||||
|
return pokemon.moveset.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and move selected
|
||||||
|
const correctMove = move.getMove().category === MoveCategory.SPECIAL;
|
||||||
|
encounter.setDialogueToken("moveCategory", "Special");
|
||||||
|
if (!correctMove) {
|
||||||
|
encounter.options[1].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.incorrect`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.lesson_learned`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_bad`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_bad`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("move", move.getName());
|
||||||
|
encounter.options[1].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_good`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
}
|
||||||
|
encounter.misc = {
|
||||||
|
correctMove: correctMove,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter.misc.correctMove) {
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPATK]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPDEF]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT),
|
||||||
|
];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.second_option_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for Pokemon move valid for this option
|
||||||
|
return pokemon.moveset.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and move selected
|
||||||
|
const correctMove = move.getMove().category === MoveCategory.STATUS;
|
||||||
|
encounter.setDialogueToken("moveCategory", "Status");
|
||||||
|
if (!correctMove) {
|
||||||
|
encounter.options[2].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.incorrect`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.lesson_learned`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_bad`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("move", move.getName());
|
||||||
|
encounter.options[2].dialogue!.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_good`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
}
|
||||||
|
encounter.misc = {
|
||||||
|
correctMove: correctMove,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter.misc.correctMove) {
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ACC]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER),
|
||||||
|
];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,251 @@
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } 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";
|
||||||
|
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
/** 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: MysteryEncounter =
|
||||||
|
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: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.VOLCARONA,
|
||||||
|
repeat: true,
|
||||||
|
hidden: true,
|
||||||
|
hasShadow: true,
|
||||||
|
x: -20,
|
||||||
|
startFrame: 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "",
|
||||||
|
fileRoot: "",
|
||||||
|
species: Species.VOLCARONA,
|
||||||
|
repeat: true,
|
||||||
|
hidden: true,
|
||||||
|
hasShadow: true,
|
||||||
|
x: 20
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Load animations/sfx for Volcarona moves
|
||||||
|
loadCustomMovesForEncounter(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 }, undefined, () => 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.getNameToRender());
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.2.target_burned`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No rewards
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.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 },
|
||||||
|
undefined,
|
||||||
|
() => {
|
||||||
|
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 = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]) as AttackTypeBoosterModifierType;
|
||||||
|
applyModifierTypeToPlayerPokemon(scene, leadPokemon, charcoal);
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
|
||||||
|
queueEncounterMessage(scene, `${namespace}.found_charcoal`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import {
|
||||||
|
EnemyPartyConfig,
|
||||||
|
initBattleWithEnemyConfig,
|
||||||
|
leaveEncounterWithoutBattle, setEncounterExp,
|
||||||
|
setEncounterRewards
|
||||||
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import {
|
||||||
|
getPartyLuckValue,
|
||||||
|
getPlayerModifierTypeOptions,
|
||||||
|
ModifierPoolType,
|
||||||
|
ModifierTypeOption,
|
||||||
|
regenerateModifierPoolThresholds,
|
||||||
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import { getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIGHT_OR_FLIGHT)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([]) // Set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
||||||
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
pokemonConfigs: [{
|
||||||
|
level: level,
|
||||||
|
species: bossSpecies,
|
||||||
|
dataSource: new PokemonData(bossPokemon),
|
||||||
|
isBoss: true
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
// Calculate item
|
||||||
|
// 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
|
||||||
|
const tier =
|
||||||
|
scene.currentBattle.waveIndex > 160
|
||||||
|
? ModifierTier.MASTER
|
||||||
|
: scene.currentBattle.waveIndex > 120
|
||||||
|
? ModifierTier.ROGUE
|
||||||
|
: scene.currentBattle.waveIndex > 40
|
||||||
|
? ModifierTier.ULTRA
|
||||||
|
: ModifierTier.GREAT;
|
||||||
|
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
||||||
|
let item: ModifierTypeOption | null = null;
|
||||||
|
// TMs excluded from possible rewards as they're too swingy in value for a singular item reward
|
||||||
|
while (!item || item.type.id.includes("TM_")) {
|
||||||
|
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
|
||||||
|
}
|
||||||
|
encounter.setDialogueToken("itemName", item.type.name);
|
||||||
|
encounter.misc = item;
|
||||||
|
|
||||||
|
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: item.type.iconImage,
|
||||||
|
fileRoot: "items",
|
||||||
|
hasShadow: false,
|
||||||
|
x: 35,
|
||||||
|
y: -5,
|
||||||
|
scale: 0.75,
|
||||||
|
isItem: true,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: spriteKey,
|
||||||
|
fileRoot: fileRoot,
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 0.25,
|
||||||
|
x: -5,
|
||||||
|
repeat: true,
|
||||||
|
isPokemon: true
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
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 item = scene.currentBattle.mysteryEncounter
|
||||||
|
.misc as ModifierTypeOption;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
||||||
|
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick steal
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
||||||
|
|
||||||
|
// Use primaryPokemon to execute the thievery
|
||||||
|
const primaryPokemon = encounter.options[1].primaryPokemon!;
|
||||||
|
setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,142 @@
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species.js";
|
||||||
|
import { Moves } from "#app/enums/moves";
|
||||||
|
import { Species } from "#app/enums/species.js";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils";
|
||||||
|
import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
const OPTION_1_REQUIRED_MOVE = Moves.SURF;
|
||||||
|
const OPTION_2_REQUIRED_MOVE = Moves.FLY;
|
||||||
|
/**
|
||||||
|
* Damage percentage taken when wandering aimlessly.
|
||||||
|
* Can be a number between `0` - `100`.
|
||||||
|
* The higher the more damage taken (100% = instant KO).
|
||||||
|
*/
|
||||||
|
const DAMAGE_PERCENTAGE: number = 25;
|
||||||
|
/** The i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:lostAtSea";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lost at sea encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(11, 179)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "buoy",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: false,
|
||||||
|
x: 20,
|
||||||
|
y: 3,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([{ text: `${namespace}.intro` }])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const { mysteryEncounter } = scene.currentBattle;
|
||||||
|
|
||||||
|
mysteryEncounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
|
||||||
|
mysteryEncounter.setDialogueToken("option1RequiredMove", Moves[OPTION_1_REQUIRED_MOVE]);
|
||||||
|
mysteryEncounter.setDialogueToken("option2RequiredMove", Moves[OPTION_2_REQUIRED_MOVE]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
// Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
disabledButtonLabel: `${namespace}.option.1.label_disabled`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}.option.1.tooltip_disabled`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
//Option 2: Use a (non fainted) pokemon that can learn fly to guide you back.
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
disabledButtonLabel: `${namespace}.option.2.label_disabled`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}.option.2.tooltip_disabled`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
// Option 3: Wander aimlessly
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle());
|
||||||
|
|
||||||
|
for (const pkm of allowedPokemon) {
|
||||||
|
const percentage = DAMAGE_PERCENTAGE / 100;
|
||||||
|
const damage = Math.floor(pkm.getMaxHp() * percentage);
|
||||||
|
applyDamageToPokemon(scene, pkm, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic handler for using a guiding pokemon to guide you back.
|
||||||
|
*
|
||||||
|
* @param scene Battle scene
|
||||||
|
* @param guidePokemon pokemon choosen as a guide
|
||||||
|
*/
|
||||||
|
async function handlePokemonGuidingYouPhase(scene: BattleScene) {
|
||||||
|
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
|
||||||
|
const { mysteryEncounter } = scene.currentBattle;
|
||||||
|
|
||||||
|
if (mysteryEncounter.selectedOption?.primaryPokemon?.id) {
|
||||||
|
setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
|
||||||
|
} else {
|
||||||
|
console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
import {
|
||||||
|
EnemyPartyConfig,
|
||||||
|
initBattleWithEnemyConfig,
|
||||||
|
setEncounterRewards,
|
||||||
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import {
|
||||||
|
trainerConfigs,
|
||||||
|
TrainerPartyCompoundTemplate,
|
||||||
|
TrainerPartyTemplate,
|
||||||
|
trainerPartyTemplates,
|
||||||
|
} from "#app/data/trainer-config";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import * as Utils from "#app/utils";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Calculates what trainers are available for battle in the encounter
|
||||||
|
|
||||||
|
// Normal difficulty trainer is randomly pulled from biome
|
||||||
|
const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
||||||
|
const normalConfig = trainerConfigs[normalTrainerType].copy();
|
||||||
|
let female = false;
|
||||||
|
if (normalConfig.hasGenders) {
|
||||||
|
female = !!Utils.randSeedInt(2);
|
||||||
|
}
|
||||||
|
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: normalConfig,
|
||||||
|
female: female,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
|
||||||
|
// Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100
|
||||||
|
const hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
||||||
|
const hardTemplate = new TrainerPartyCompoundTemplate(
|
||||||
|
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true),
|
||||||
|
new TrainerPartyTemplate(
|
||||||
|
Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5),
|
||||||
|
PartyMemberStrength.AVERAGE,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const hardConfig = trainerConfigs[hardTrainerType].copy();
|
||||||
|
hardConfig.setPartyTemplates(hardTemplate);
|
||||||
|
female = false;
|
||||||
|
if (hardConfig.hasGenders) {
|
||||||
|
female = !!Utils.randSeedInt(2);
|
||||||
|
}
|
||||||
|
const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly);
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: hardConfig,
|
||||||
|
levelAdditiveMultiplier: 0.5,
|
||||||
|
female: female,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
|
||||||
|
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
|
||||||
|
const brutalTrainerType = scene.arena.randomTrainerType(
|
||||||
|
scene.currentBattle.waveIndex,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const e4Template = trainerPartyTemplates.ELITE_FOUR;
|
||||||
|
const brutalConfig = trainerConfigs[brutalTrainerType].copy();
|
||||||
|
brutalConfig.setPartyTemplates(e4Template);
|
||||||
|
// @ts-ignore
|
||||||
|
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func
|
||||||
|
female = false;
|
||||||
|
if (brutalConfig.hasGenders) {
|
||||||
|
female = !!Utils.randSeedInt(2);
|
||||||
|
}
|
||||||
|
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: brutalConfig,
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
female: female,
|
||||||
|
});
|
||||||
|
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: normalSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: hardSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: brutalSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
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.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn standard trainer battle with memory mushroom reward
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
|
||||||
|
|
||||||
|
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||||
|
let ret;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
ret = initBattleWithEnemyConfig(scene, config);
|
||||||
|
}, scene.currentBattle.waveIndex * 10);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn hard fight with ULTRA/GREAT reward (can improve with luck)
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true });
|
||||||
|
|
||||||
|
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||||
|
let ret;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
ret = initBattleWithEnemyConfig(scene, config);
|
||||||
|
}, scene.currentBattle.waveIndex * 100);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck)
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
|
||||||
|
|
||||||
|
// To avoid player level snowballing from picking this option
|
||||||
|
encounter.expMultiplier = 0.9;
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
|
||||||
|
|
||||||
|
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||||
|
let ret;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
ret = initBattleWithEnemyConfig(scene, config);
|
||||||
|
}, scene.currentBattle.waveIndex * 1000);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.build();
|
|
@ -0,0 +1,141 @@
|
||||||
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { leaveEncounterWithoutBattle, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { randSeedInt } from "#app/utils.js";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
/** 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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 2 to 180
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "chest_blue",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
x: 4,
|
||||||
|
y: 10,
|
||||||
|
yShadow: 3,
|
||||||
|
disableAnimation: true, // Re-enabled after option select
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Play animation
|
||||||
|
const introVisuals =
|
||||||
|
scene.currentBattle.mysteryEncounter.introVisuals!;
|
||||||
|
introVisuals.spriteConfigs[0].disableAnimation = false;
|
||||||
|
introVisuals.playAnim();
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Open the chest
|
||||||
|
const roll = randSeedInt(100);
|
||||||
|
if (roll > 60) {
|
||||||
|
// Choose between 2 COMMON / 2 GREAT tier items (40%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [
|
||||||
|
ModifierTier.COMMON,
|
||||||
|
ModifierTier.COMMON,
|
||||||
|
ModifierTier.GREAT,
|
||||||
|
ModifierTier.GREAT,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.1.normal`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else if (roll > 40) {
|
||||||
|
// Choose between 3 ULTRA tier items (20%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [
|
||||||
|
ModifierTier.ULTRA,
|
||||||
|
ModifierTier.ULTRA,
|
||||||
|
ModifierTier.ULTRA,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.1.good`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else if (roll > 36) {
|
||||||
|
// Choose between 2 ROGUE tier items (4%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.1.great`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else if (roll > 35) {
|
||||||
|
// Choose 1 MASTER tier item (1%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [ModifierTier.MASTER],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.1.amazing`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else {
|
||||||
|
// Your highest level unfainted Pok<6F>mon gets OHKO. Progress with no rewards (35%)
|
||||||
|
const highestLevelPokemon = getHighestLevelPlayerPokemon(
|
||||||
|
scene,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
koPlayerPokemon(scene, highestLevelPokemon);
|
||||||
|
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
||||||
|
// Show which Pokemon was KOed, then leave encounter with no rewards
|
||||||
|
// Does this synchronously so that game over doesn't happen over result message
|
||||||
|
await showEncounterText(scene, `${namespace}.option.1.bad`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,339 @@
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { getEncounterText, showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:partTimer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part Timer encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/82 | GitHub Issue #82}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const PartTimerEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.PART_TIMER)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "worker_f",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "training_gear",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
y: 6,
|
||||||
|
x: 20,
|
||||||
|
yShadow: -2
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
// Load sfx
|
||||||
|
scene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav");
|
||||||
|
scene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav");
|
||||||
|
scene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav");
|
||||||
|
scene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav");
|
||||||
|
|
||||||
|
scene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav");
|
||||||
|
scene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav");
|
||||||
|
scene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav");
|
||||||
|
|
||||||
|
scene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav");
|
||||||
|
scene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav");
|
||||||
|
scene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
|
|
||||||
|
// Calculate the "baseline" stat value (90 base stat, 16 IVs, neutral nature, same level as pokemon) to compare
|
||||||
|
// Resulting money is 2.5 * (% difference from baseline), with minimum of 1 and maximum of 4.
|
||||||
|
// Calculation from Pokemon.calculateStats
|
||||||
|
const baselineValue = Math.floor(((2 * 90 + 16) * pokemon.level) * 0.01) + 5;
|
||||||
|
const percentDiff = (pokemon.getStat(Stat.SPD) - baselineValue) / baselineValue;
|
||||||
|
const moneyMultiplier = Math.min(Math.max(2.5 * (1+ percentDiff), 1), 4);
|
||||||
|
|
||||||
|
encounter.misc = {
|
||||||
|
moneyMultiplier
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reduce all PP to 2 (if they started at greater than 2)
|
||||||
|
pokemon.moveset.forEach(move => {
|
||||||
|
if (move) {
|
||||||
|
const newPpUsed = move.getMovePp() - 2;
|
||||||
|
move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setEncounterExp(scene, pokemon.id, 100);
|
||||||
|
|
||||||
|
// Hide intro visuals
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||||
|
// Play sfx for "working"
|
||||||
|
doDeliverySfx(scene);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon non-KOd pokemon can be selected
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
if (!pokemon.isAllowedInBattle()) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick Deliveries
|
||||||
|
// Bring visuals back in
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene, false, false);
|
||||||
|
|
||||||
|
const moneyMultiplier = scene.currentBattle.mysteryEncounter.misc.moneyMultiplier;
|
||||||
|
|
||||||
|
// Give money and do dialogue
|
||||||
|
if (moneyMultiplier > 2.5) {
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`);
|
||||||
|
} else {
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.job_complete_bad`, `${namespace}.speaker`);
|
||||||
|
}
|
||||||
|
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
|
||||||
|
updatePlayerMoney(scene, moneyChange, true, false);
|
||||||
|
await showEncounterText(scene, i18next.t("mysteryEncounter:receive_money", { amount: moneyChange }));
|
||||||
|
await showEncounterText(scene, `${namespace}.pokemon_tired`);
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
|
|
||||||
|
// Calculate the "baseline" stat value (75 base stat, 16 IVs, neutral nature, same level as pokemon) to compare
|
||||||
|
// Resulting money is 2.5 * (% difference from baseline), with minimum of 1 and maximum of 4.
|
||||||
|
// Calculation from Pokemon.calculateStats
|
||||||
|
const baselineHp = Math.floor(((2 * 75 + 16) * pokemon.level) * 0.01) + pokemon.level + 10;
|
||||||
|
const baselineAtkDef = Math.floor(((2 * 75 + 16) * pokemon.level) * 0.01) + 5;
|
||||||
|
const baselineValue = baselineHp + 1.5 * (baselineAtkDef * 2);
|
||||||
|
const strongestValue = pokemon.getStat(Stat.HP) + 1.5 * (pokemon.getStat(Stat.ATK) + pokemon.getStat(Stat.DEF));
|
||||||
|
const percentDiff = (strongestValue - baselineValue) / baselineValue;
|
||||||
|
const moneyMultiplier = Math.min(Math.max(2.5 * (1 + percentDiff), 1), 4);
|
||||||
|
|
||||||
|
encounter.misc = {
|
||||||
|
moneyMultiplier
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reduce all PP to 2 (if they started at greater than 2)
|
||||||
|
pokemon.moveset.forEach(move => {
|
||||||
|
if (move) {
|
||||||
|
const newPpUsed = move.getMovePp() - 2;
|
||||||
|
move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setEncounterExp(scene, pokemon.id, 100);
|
||||||
|
|
||||||
|
// Hide intro visuals
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||||
|
// Play sfx for "working"
|
||||||
|
doStrongWorkSfx(scene);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon non-KOd pokemon can be selected
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
if (!pokemon.isAllowedInBattle()) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick Move Warehouse items
|
||||||
|
// Bring visuals back in
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene, false, false);
|
||||||
|
|
||||||
|
const moneyMultiplier = scene.currentBattle.mysteryEncounter.misc.moneyMultiplier;
|
||||||
|
|
||||||
|
// Give money and do dialogue
|
||||||
|
if (moneyMultiplier > 2.5) {
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`);
|
||||||
|
} else {
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.job_complete_bad`, `${namespace}.speaker`);
|
||||||
|
}
|
||||||
|
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
|
||||||
|
updatePlayerMoney(scene, moneyChange, true, false);
|
||||||
|
await showEncounterText(scene, i18next.t("mysteryEncounter:receive_money", { amount: moneyChange }));
|
||||||
|
await showEncounterText(scene, `${namespace}.pokemon_tired`);
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new MoveRequirement(CHARMING_MOVES)) // Will set option3PrimaryName and option3PrimaryMove dialogue tokens 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) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const selectedPokemon = encounter.selectedOption?.primaryPokemon!;
|
||||||
|
encounter.setDialogueToken("selectedPokemon", selectedPokemon.getNameToRender());
|
||||||
|
|
||||||
|
// Reduce all PP to 2 (if they started at greater than 2)
|
||||||
|
selectedPokemon.moveset.forEach(move => {
|
||||||
|
if (move) {
|
||||||
|
const newPpUsed = move.getMovePp() - 2;
|
||||||
|
move.ppUsed = move.ppUsed < newPpUsed ? newPpUsed : move.ppUsed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setEncounterExp(scene, selectedPokemon.id, 100);
|
||||||
|
|
||||||
|
// Hide intro visuals
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||||
|
// Play sfx for "working"
|
||||||
|
doSalesSfx(scene);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Assist with Sales
|
||||||
|
// Bring visuals back in
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene, false, false);
|
||||||
|
|
||||||
|
// Give money and do dialogue
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.job_complete_good`, `${namespace}.speaker`);
|
||||||
|
const moneyChange = scene.getWaveMoneyAmount(2.5);
|
||||||
|
updatePlayerMoney(scene, moneyChange, true, false);
|
||||||
|
await showEncounterText(scene, i18next.t("mysteryEncounter:receive_money", { amount: moneyChange }));
|
||||||
|
await showEncounterText(scene, `${namespace}.pokemon_tired`);
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
function doStrongWorkSfx(scene: BattleScene) {
|
||||||
|
scene.playSound("PRSFX- Horn Drill1");
|
||||||
|
scene.playSound("PRSFX- Horn Drill1");
|
||||||
|
|
||||||
|
scene.time.delayedCall(1000, () => {
|
||||||
|
scene.playSound("PRSFX- Guillotine2");
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.time.delayedCall(2000, () => {
|
||||||
|
scene.playSound("PRSFX- Heavy Slam2");
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.time.delayedCall(2500, () => {
|
||||||
|
scene.playSound("PRSFX- Guillotine2");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doDeliverySfx(scene: BattleScene) {
|
||||||
|
scene.playSound("PRSFX- Accelerock1");
|
||||||
|
|
||||||
|
scene.time.delayedCall(1500, () => {
|
||||||
|
scene.playSound("PRSFX- Extremespeed1");
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.time.delayedCall(2000, () => {
|
||||||
|
scene.playSound("PRSFX- Extremespeed1");
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.time.delayedCall(2250, () => {
|
||||||
|
scene.playSound("PRSFX- Agility");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSalesSfx(scene: BattleScene) {
|
||||||
|
scene.playSound("PRSFX- Captivate");
|
||||||
|
|
||||||
|
scene.time.delayedCall(1500, () => {
|
||||||
|
scene.playSound("PRSFX- Attract2");
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.time.delayedCall(2000, () => {
|
||||||
|
scene.playSound("PRSFX- Aurora Veil2");
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.time.delayedCall(3000, () => {
|
||||||
|
scene.playSound("PRSFX- Attract2");
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,501 @@
|
||||||
|
import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import MysteryEncounterOption, { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
|
||||||
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokeballType } from "#app/data/pokeball";
|
||||||
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
|
import { IntegerHolder, randSeedInt } from "#app/utils";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
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 { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||||
|
import { SummonPhase } from "#app/phases/summon-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Cost equal to 1 Max Revive
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "safari_zone",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: false,
|
||||||
|
x: 4,
|
||||||
|
y: 6
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneRequirement(new MoneyRequirement(0, 2.75)) // Cost equal to 1 Max Revive
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Start safari encounter
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
encounter.continuousEncounter = true;
|
||||||
|
encounter.misc = {
|
||||||
|
safariPokemonRemaining: 3
|
||||||
|
};
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||||
|
// Load bait/mud assets
|
||||||
|
scene.loadSe("PRSFX- Bug Bite", "battle_anims");
|
||||||
|
scene.loadSe("PRSFX- Sludge Bomb2", "battle_anims");
|
||||||
|
scene.loadSe("PRSFX- Taunt2", "battle_anims");
|
||||||
|
scene.loadAtlas("bait", "mystery-encounters");
|
||||||
|
scene.loadAtlas("mud", "mystery-encounters");
|
||||||
|
await summonSafariPokemon(scene);
|
||||||
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, hideDescription: true });
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SAFARI ZONE MINIGAME OPTIONS
|
||||||
|
*
|
||||||
|
* Catch and flee rate stages are calculated in the same way stat changes are (they range from -6/+6)
|
||||||
|
* https://bulbapedia.bulbagarden.net/wiki/Catch_rate#Great_Marsh_and_Johto_Safari_Zone
|
||||||
|
*
|
||||||
|
* Catch Rate calculation:
|
||||||
|
* catchRate = speciesCatchRate [1 to 255] * catchStageMultiplier [2/8 to 8/2] * ballCatchRate [1.5]
|
||||||
|
*
|
||||||
|
* Flee calculation:
|
||||||
|
* The harder a species is to catch, the higher its flee rate is
|
||||||
|
* (Caps at 50% base chance to flee for the hardest to catch Pokemon, before factoring in flee stage)
|
||||||
|
* fleeRate = ((255^2 - speciesCatchRate^2) / 255 / 2) [0 to 127.5] * fleeStageMultiplier [2/8 to 8/2]
|
||||||
|
* Flee chance = fleeRate / 255
|
||||||
|
*/
|
||||||
|
const safariZoneGameOptions: MysteryEncounterOption[] = [
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.safari.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.safari.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.safari.1.selected`,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Throw a ball option
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const pokemon = encounter.misc.pokemon;
|
||||||
|
const catchResult = await throwPokeball(scene, pokemon);
|
||||||
|
|
||||||
|
if (catchResult) {
|
||||||
|
// You caught pokemon
|
||||||
|
// Check how many safari pokemon left
|
||||||
|
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||||
|
await summonSafariPokemon(scene);
|
||||||
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: 0, hideDescription: true });
|
||||||
|
} else {
|
||||||
|
// End safari mode
|
||||||
|
encounter.continuousEncounter = false;
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Pokemon catch failed, end turn
|
||||||
|
await doEndTurn(scene, 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.safari.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.safari.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.safari.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Throw bait option
|
||||||
|
const pokemon = scene.currentBattle.mysteryEncounter.misc.pokemon;
|
||||||
|
await throwBait(scene, pokemon);
|
||||||
|
|
||||||
|
// 100% chance to increase catch stage +2
|
||||||
|
tryChangeCatchStage(scene, 2);
|
||||||
|
// 80% chance to increase flee stage +1
|
||||||
|
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8);
|
||||||
|
if (!fleeChangeResult) {
|
||||||
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.busy_eating`) ?? "", 1000, false );
|
||||||
|
} else {
|
||||||
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.eating`) ?? "", 1000, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
await doEndTurn(scene, 1);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.safari.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.safari.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.safari.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Throw mud option
|
||||||
|
const pokemon = scene.currentBattle.mysteryEncounter.misc.pokemon;
|
||||||
|
await throwMud(scene, pokemon);
|
||||||
|
// 100% chance to decrease flee stage -2
|
||||||
|
tryChangeFleeStage(scene, -2);
|
||||||
|
// 80% chance to decrease catch stage -1
|
||||||
|
const catchChangeResult = tryChangeCatchStage(scene, -1, 8);
|
||||||
|
if (!catchChangeResult) {
|
||||||
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.beside_itself_angry`) ?? "", 1000, false );
|
||||||
|
} else {
|
||||||
|
await showEncounterText(scene, getEncounterText(scene, `${namespace}.safari.angry`) ?? "", 1000, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
await doEndTurn(scene, 2);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.safari.4.label`,
|
||||||
|
buttonTooltip: `${namespace}.safari.4.tooltip`,
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Flee option
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const pokemon = encounter.misc.pokemon;
|
||||||
|
await doPlayerFlee(scene, pokemon);
|
||||||
|
// Check how many safari pokemon left
|
||||||
|
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||||
|
await summonSafariPokemon(scene);
|
||||||
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: 3, hideDescription: true });
|
||||||
|
} else {
|
||||||
|
// End safari mode
|
||||||
|
encounter.continuousEncounter = false;
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
];
|
||||||
|
|
||||||
|
async function summonSafariPokemon(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Message pokemon remaining
|
||||||
|
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
|
||||||
|
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
||||||
|
let enemySpecies;
|
||||||
|
let pokemon;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
enemySpecies = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
||||||
|
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(scene.currentBattle.waveIndex, true, false, scene.gameMode));
|
||||||
|
scene.currentBattle.enemyParty = [];
|
||||||
|
pokemon = scene.addEnemyPokemon(enemySpecies, scene.currentBattle.waveIndex, TrainerSlot.NONE, false);
|
||||||
|
|
||||||
|
// Roll shiny twice
|
||||||
|
if (!pokemon.shiny) {
|
||||||
|
pokemon.trySetShiny();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll HA twice
|
||||||
|
if (pokemon.species.abilityHidden) {
|
||||||
|
const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
|
||||||
|
if (pokemon.abilityIndex < hiddenIndex) {
|
||||||
|
const hiddenAbilityChance = new IntegerHolder(256);
|
||||||
|
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||||
|
|
||||||
|
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||||
|
|
||||||
|
if (hasHiddenAbility) {
|
||||||
|
pokemon.abilityIndex = hiddenIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.calculateStats();
|
||||||
|
|
||||||
|
scene.currentBattle.enemyParty[0] = pokemon;
|
||||||
|
}, scene.currentBattle.waveIndex * 1000 + encounter.misc.safariPokemonRemaining);
|
||||||
|
|
||||||
|
scene.gameData.setPokemonSeen(pokemon, true);
|
||||||
|
await pokemon.loadAssets();
|
||||||
|
|
||||||
|
// Reset safari catch and flee rates
|
||||||
|
encounter.misc.catchStage = 0;
|
||||||
|
encounter.misc.fleeStage = 0;
|
||||||
|
encounter.misc.pokemon = pokemon;
|
||||||
|
encounter.misc.safariPokemonRemaining -= 1;
|
||||||
|
|
||||||
|
scene.unshiftPhase(new SummonPhase(scene, 0, false));
|
||||||
|
|
||||||
|
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
||||||
|
showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared") ?? "", 1500, false)
|
||||||
|
.then(() => {
|
||||||
|
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
|
||||||
|
if (ivScannerModifier) {
|
||||||
|
scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwPokeball(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {
|
||||||
|
const baseCatchRate = pokemon.species.catchRate;
|
||||||
|
// Catch stage ranges from -6 to +6 (like stat boost stages)
|
||||||
|
const safariCatchStage = scene.currentBattle.mysteryEncounter.misc.catchStage;
|
||||||
|
// Catch modifier ranges from 2/8 (-6 stage) to 8/2 (+6)
|
||||||
|
const safariModifier = (2 + Math.min(Math.max(safariCatchStage, 0), 6)) / (2 - Math.max(Math.min(safariCatchStage, 0), -6));
|
||||||
|
// Catch rate same as safari ball
|
||||||
|
const pokeballMultiplier = 1.5;
|
||||||
|
const catchRate = Math.round(baseCatchRate * pokeballMultiplier * safariModifier);
|
||||||
|
const ballTwitchRate = Math.round(1048560 / Math.sqrt(Math.sqrt(16711680 / catchRate)));
|
||||||
|
return trainerThrowPokeball(scene, pokemon, PokeballType.POKEBALL, ballTwitchRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {
|
||||||
|
const originalY: number = pokemon.y;
|
||||||
|
|
||||||
|
const fpOffset = pokemon.getFieldPositionOffset();
|
||||||
|
const bait: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 25, "bait", "0001.png");
|
||||||
|
bait.setOrigin(0.5, 0.625);
|
||||||
|
scene.field.add(bait);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||||
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||||
|
scene.playSound("pb_throw");
|
||||||
|
|
||||||
|
// Trainer throw frames
|
||||||
|
scene.trainer.setFrame("2");
|
||||||
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||||
|
scene.trainer.setFrame("3");
|
||||||
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||||
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pokeball move and catch logic
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: bait,
|
||||||
|
x: { value: 210 + fpOffset[0], ease: "Linear" },
|
||||||
|
y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" },
|
||||||
|
duration: 500,
|
||||||
|
onComplete: () => {
|
||||||
|
|
||||||
|
let index = 1;
|
||||||
|
scene.time.delayedCall(768, () => {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: pokemon,
|
||||||
|
duration: 150,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
y: originalY - 5,
|
||||||
|
loop: 6,
|
||||||
|
onStart: () => {
|
||||||
|
scene.playSound("PRSFX- Bug Bite");
|
||||||
|
bait.setFrame("0002.png");
|
||||||
|
},
|
||||||
|
onLoop: () => {
|
||||||
|
if (index % 2 === 0) {
|
||||||
|
scene.playSound("PRSFX- Bug Bite");
|
||||||
|
}
|
||||||
|
if (index === 4) {
|
||||||
|
bait.setFrame("0003.png");
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
scene.time.delayedCall(256, () => {
|
||||||
|
bait.destroy();
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {
|
||||||
|
const originalY: number = pokemon.y;
|
||||||
|
|
||||||
|
const fpOffset = pokemon.getFieldPositionOffset();
|
||||||
|
const mud: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 35, "mud", "0001.png");
|
||||||
|
mud.setOrigin(0.5, 0.625);
|
||||||
|
scene.field.add(mud);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||||
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||||
|
scene.playSound("pb_throw");
|
||||||
|
|
||||||
|
// Trainer throw frames
|
||||||
|
scene.trainer.setFrame("2");
|
||||||
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||||
|
scene.trainer.setFrame("3");
|
||||||
|
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||||
|
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mud throw and splat
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: mud,
|
||||||
|
x: { value: 230 + fpOffset[0], ease: "Linear" },
|
||||||
|
y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" },
|
||||||
|
duration: 500,
|
||||||
|
onComplete: () => {
|
||||||
|
// Mud frame 2
|
||||||
|
scene.playSound("PRSFX- Sludge Bomb2");
|
||||||
|
mud.setFrame("0002.png");
|
||||||
|
// Mud splat
|
||||||
|
scene.time.delayedCall(200, () => {
|
||||||
|
mud.setFrame("0003.png");
|
||||||
|
scene.time.delayedCall(400, () => {
|
||||||
|
mud.setFrame("0004.png");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fade mud then angry animation
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: mud,
|
||||||
|
alpha: 0,
|
||||||
|
ease: "Cubic.easeIn",
|
||||||
|
duration: 1000,
|
||||||
|
onComplete: () => {
|
||||||
|
mud.destroy();
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: pokemon,
|
||||||
|
duration: 300,
|
||||||
|
ease: "Cubic.easeOut",
|
||||||
|
yoyo: true,
|
||||||
|
y: originalY - 20,
|
||||||
|
loop: 1,
|
||||||
|
onStart: () => {
|
||||||
|
scene.playSound("PRSFX- Taunt2");
|
||||||
|
},
|
||||||
|
onLoop: () => {
|
||||||
|
scene.playSound("PRSFX- Taunt2");
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPokemonFlee(pokemon: EnemyPokemon, fleeStage: number): boolean {
|
||||||
|
const speciesCatchRate = pokemon.species.catchRate;
|
||||||
|
const fleeModifier = (2 + Math.min(Math.max(fleeStage, 0), 6)) / (2 - Math.max(Math.min(fleeStage, 0), -6));
|
||||||
|
const fleeRate = (255 * 255 - speciesCatchRate * speciesCatchRate) / 255 / 2 * fleeModifier;
|
||||||
|
console.log("Flee rate: " + fleeRate);
|
||||||
|
const roll = randSeedInt(256);
|
||||||
|
console.log("Roll: " + roll);
|
||||||
|
return roll < fleeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryChangeFleeStage(scene: BattleScene, change: number, chance?: number): boolean {
|
||||||
|
if (chance && randSeedInt(10) >= chance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const currentFleeStage = scene.currentBattle.mysteryEncounter.misc.fleeStage ?? 0;
|
||||||
|
scene.currentBattle.mysteryEncounter.misc.fleeStage = Math.min(Math.max(currentFleeStage + change, -6), 6);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryChangeCatchStage(scene: BattleScene, change: number, chance?: number): boolean {
|
||||||
|
if (chance && randSeedInt(10) >= chance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const currentCatchStage = scene.currentBattle.mysteryEncounter.misc.catchStage ?? 0;
|
||||||
|
scene.currentBattle.mysteryEncounter.misc.catchStage = Math.min(Math.max(currentCatchStage + change, -6), 6);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doEndTurn(scene: BattleScene, cursorIndex: number) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const pokemon = encounter.misc.pokemon;
|
||||||
|
const isFlee = isPokemonFlee(pokemon, encounter.misc.fleeStage);
|
||||||
|
if (isFlee) {
|
||||||
|
// Pokemon flees!
|
||||||
|
await doPokemonFlee(scene, pokemon);
|
||||||
|
// Check how many safari pokemon left
|
||||||
|
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||||
|
await summonSafariPokemon(scene);
|
||||||
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||||
|
} else {
|
||||||
|
// End safari mode
|
||||||
|
encounter.continuousEncounter = false;
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scene.queueMessage(getEncounterText(scene, `${namespace}.safari.watching`) ?? "", 0, null, 1000);
|
||||||
|
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withPrimaryPokemonStatusEffectRequirement([StatusEffect.NONE]) // Pokemon must not have status
|
||||||
|
.withPrimaryPokemonHealthRatioRequirement([0.34, 1]) // Pokemon must have above 1/3rd HP
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.KROOKODILE.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 12,
|
||||||
|
y: -5,
|
||||||
|
yShadow: -5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "b2w2_veteran_m",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -12,
|
||||||
|
y: 3,
|
||||||
|
yShadow: 3
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Update money
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||||
|
// Calculate modifiers and dialogue tokens
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER),
|
||||||
|
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER),
|
||||||
|
];
|
||||||
|
encounter.setDialogueToken("boost1", modifiers[0].name);
|
||||||
|
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
modifiers: modifiers,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Choose Cheap Option
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
const modifiers = encounter.misc.modifiers;
|
||||||
|
|
||||||
|
for (const modType of modifiers) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Damage and status applied after dealer leaves (to make thematic sense)
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
|
||||||
|
// Pokemon takes 1/3 max HP damage
|
||||||
|
applyDamageToPokemon(scene, chosenPokemon, Math.floor(chosenPokemon.getMaxHp() / 3));
|
||||||
|
|
||||||
|
// Roll for poison (80%)
|
||||||
|
if (randSeedInt(10) < 8) {
|
||||||
|
if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) {
|
||||||
|
// Toxic applied
|
||||||
|
queueEncounterMessage(scene, `${namespace}.bad_poison`);
|
||||||
|
} else {
|
||||||
|
// Pokemon immune or something else prevents status
|
||||||
|
queueEncounterMessage(scene, `${namespace}.damage_only`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueEncounterMessage(scene, `${namespace}.damage_only`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterExp(scene, [chosenPokemon.id], 100);
|
||||||
|
|
||||||
|
chosenPokemon.updateInfo();
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Update money
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney);
|
||||||
|
// Calculate modifiers and dialogue tokens
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER),
|
||||||
|
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER),
|
||||||
|
];
|
||||||
|
encounter.setDialogueToken("boost1", modifiers[0].name);
|
||||||
|
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
modifiers: modifiers,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Choose Expensive Option
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
const modifiers = encounter.misc.modifiers;
|
||||||
|
|
||||||
|
for (const modType of modifiers) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Status applied after dealer leaves (to make thematic sense)
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
|
||||||
|
// Roll for poison (20%)
|
||||||
|
if (randSeedInt(10) < 2) {
|
||||||
|
if (chosenPokemon.trySetStatus(StatusEffect.POISON)) {
|
||||||
|
// Poison applied
|
||||||
|
queueEncounterMessage(scene, `${namespace}.poison`);
|
||||||
|
} else {
|
||||||
|
// Pokemon immune or something else prevents status
|
||||||
|
queueEncounterMessage(scene, `${namespace}.no_bad_effects`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueEncounterMessage(scene, `${namespace}.no_bad_effects`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterExp(scene, [chosenPokemon.id], 100);
|
||||||
|
|
||||||
|
chosenPokemon.updateInfo();
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
speaker: `${namespace}.speaker`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.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 MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, 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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||||
|
|
||||||
|
/** 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: MysteryEncounter =
|
||||||
|
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
|
||||||
|
loadCustomMovesForEncounter(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(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.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();
|
|
@ -0,0 +1,235 @@
|
||||||
|
import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MoneyRequirement, WaveModulusRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { Biome } from "#enums/biome";
|
||||||
|
import { getBiomeKey } from "#app/field/arena";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { getPartyLuckValue, modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:teleportingHijinks";
|
||||||
|
|
||||||
|
const MONEY_COST_MULTIPLIER = 2.5;
|
||||||
|
const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND];
|
||||||
|
const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleporting Hijinks encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/119 | GitHub Issue #119}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TELEPORTING_HIJINKS)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withSceneRequirement(new WaveModulusRequirement([1, 2, 3], 10)) // Must be in first 3 waves after boss wave
|
||||||
|
.withSceneRequirement(new MoneyRequirement(undefined, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "teleporter",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
y: 4
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
|
||||||
|
encounter.setDialogueToken("price", price.toString());
|
||||||
|
encounter.misc = {
|
||||||
|
price
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(undefined, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Update money
|
||||||
|
updatePlayerMoney(scene, -scene.currentBattle.mysteryEncounter.misc.price, true, false);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene);
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPokemonTypeRequirement(MACHINE_INTERFACING_TYPES, true, 1) // Must have Steel or Electric type
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
disabledButtonTooltip: `${namespace}.option.2.disabled_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene);
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
setEncounterExp(scene, scene.currentBattle.mysteryEncounter.selectedOption!.primaryPokemon!.id, 100);
|
||||||
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.3.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Inspect the Machine
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Init enemy
|
||||||
|
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
||||||
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
pokemonConfigs: [{
|
||||||
|
level: level,
|
||||||
|
species: bossSpecies,
|
||||||
|
dataSource: new PokemonData(bossPokemon),
|
||||||
|
isBoss: true,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.STEEL]);
|
||||||
|
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.ELECTRIC]);
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [magnet, metalCoat], fillRemaining: true });
|
||||||
|
setEncounterExp(scene, encounter.selectedOption!.primaryPokemon!.id, 100);
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true);
|
||||||
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate new biome (cannot be current biome)
|
||||||
|
const filteredBiomes = BIOME_CANDIDATES.filter(b => scene.arena.biomeType !== b);
|
||||||
|
const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)];
|
||||||
|
|
||||||
|
// Show dialogue and transition biome
|
||||||
|
await showEncounterText(scene, `${namespace}.transport`);
|
||||||
|
await Promise.all([animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene)]);
|
||||||
|
scene.playBgm();
|
||||||
|
await showEncounterText(scene, `${namespace}.attacked`);
|
||||||
|
|
||||||
|
// Init enemy
|
||||||
|
const level = (scene.currentBattle.enemyLevels?.[0] ?? scene.currentBattle.waveIndex) + Math.max(Math.round((scene.currentBattle.waveIndex / 10)), 0);
|
||||||
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
pokemonConfigs: [{
|
||||||
|
level: level,
|
||||||
|
species: bossSpecies,
|
||||||
|
dataSource: new PokemonData(bossPokemon),
|
||||||
|
isBoss: true,
|
||||||
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function animateBiomeChange(scene: BattleScene, nextBiome: Biome) {
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: [scene.arenaEnemy, scene.lastEnemyTrainer],
|
||||||
|
x: "+=300",
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: () => {
|
||||||
|
scene.newArena(nextBiome);
|
||||||
|
|
||||||
|
const biomeKey = getBiomeKey(nextBiome);
|
||||||
|
const bgTexture = `${biomeKey}_bg`;
|
||||||
|
scene.arenaBgTransition.setTexture(bgTexture);
|
||||||
|
scene.arenaBgTransition.setAlpha(0);
|
||||||
|
scene.arenaBgTransition.setVisible(true);
|
||||||
|
scene.arenaPlayerTransition.setBiome(nextBiome);
|
||||||
|
scene.arenaPlayerTransition.setAlpha(0);
|
||||||
|
scene.arenaPlayerTransition.setVisible(true);
|
||||||
|
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: [scene.arenaPlayer, scene.arenaBgTransition, scene.arenaPlayerTransition],
|
||||||
|
duration: 1000,
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
alpha: (target: any) => target === scene.arenaPlayer ? 0 : 1,
|
||||||
|
onComplete: () => {
|
||||||
|
scene.arenaBg.setTexture(bgTexture);
|
||||||
|
scene.arenaPlayer.setBiome(nextBiome);
|
||||||
|
scene.arenaPlayer.setAlpha(1);
|
||||||
|
scene.arenaEnemy.setBiome(nextBiome);
|
||||||
|
scene.arenaEnemy.setAlpha(1);
|
||||||
|
scene.arenaNextEnemy.setBiome(nextBiome);
|
||||||
|
scene.arenaBgTransition.setVisible(false);
|
||||||
|
scene.arenaPlayerTransition.setVisible(false);
|
||||||
|
if (scene.lastEnemyTrainer) {
|
||||||
|
scene.lastEnemyTrainer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: scene.arenaEnemy,
|
||||||
|
x: "-=300",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { PokeballType } from "#app/data/pokeball";
|
||||||
|
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:pokemonSalesman";
|
||||||
|
|
||||||
|
const MAX_POKEMON_PRICE_MULTIPLIER = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon Salesman encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/36 | GitHub Issue #36}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_POKEMON_SALESMAN)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withSceneRequirement(new MoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "pokemon_salesman",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
||||||
|
const tries = 0;
|
||||||
|
|
||||||
|
// Reroll any species that don't have HAs
|
||||||
|
while (isNullOrUndefined(species.abilityHidden) && tries < 5) {
|
||||||
|
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let pokemon: PlayerPokemon;
|
||||||
|
if (isNullOrUndefined(species.abilityHidden) || randSeedInt(100) === 0) {
|
||||||
|
// If no HA mon found or you roll 1%, give shiny Magikarp
|
||||||
|
species = getPokemonSpecies(Species.MAGIKARP);
|
||||||
|
const hiddenIndex = species.ability2 ? 2 : 1;
|
||||||
|
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, undefined, true);
|
||||||
|
} else {
|
||||||
|
const hiddenIndex = species.ability2 ? 2 : 1;
|
||||||
|
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(pokemon);
|
||||||
|
encounter.spriteConfigs.push({
|
||||||
|
spriteKey: spriteKey,
|
||||||
|
fileRoot: fileRoot,
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
isPokemon: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const starterTier = speciesStarters[species.speciesId];
|
||||||
|
// Prices decrease by starter tier less than 5, but only reduces cost by half at max
|
||||||
|
let priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER * (Math.max(starterTier, 2.5) / 5);
|
||||||
|
if (pokemon.shiny) {
|
||||||
|
// Always max price for shiny (flip HA back to normal), and add special messaging
|
||||||
|
priceMultiplier = MAX_POKEMON_PRICE_MULTIPLIER;
|
||||||
|
pokemon.abilityIndex = 0;
|
||||||
|
encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}.description_shiny`;
|
||||||
|
encounter.options[0].dialogue!.buttonTooltip = `${namespace}.option.1.tooltip_shiny`;
|
||||||
|
}
|
||||||
|
const price = scene.getWaveMoneyAmount(priceMultiplier);
|
||||||
|
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("price", price.toString());
|
||||||
|
encounter.misc = {
|
||||||
|
price: price,
|
||||||
|
pokemon: pokemon
|
||||||
|
};
|
||||||
|
|
||||||
|
pokemon.calculateStats();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
|
.withSceneMoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected_message`,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const price = encounter.misc.price;
|
||||||
|
const purchasedPokemon = encounter.misc.pokemon as PlayerPokemon;
|
||||||
|
|
||||||
|
// Update money
|
||||||
|
updatePlayerMoney(scene, -price, true, false);
|
||||||
|
|
||||||
|
// Show dialogue
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.option.1.selected_dialogue`, `${namespace}.speaker`);
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
|
||||||
|
// "Catch" purchased pokemon
|
||||||
|
const data = new PokemonData(purchasedPokemon);
|
||||||
|
data.player = false;
|
||||||
|
await catchPokemon(scene, data.toPokemon(scene) as EnemyPokemon, null, PokeballType.POKEBALL, true, true);
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,199 @@
|
||||||
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Nature } from "#app/data/nature";
|
||||||
|
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||||
|
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:theStrongStuff";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Strong Stuff encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/54 | GitHub Issue #54}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const TheStrongStuffEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "berry_juice",
|
||||||
|
fileRoot: "items",
|
||||||
|
hasShadow: true,
|
||||||
|
isItem: true,
|
||||||
|
scale: 1.5,
|
||||||
|
x: -15,
|
||||||
|
y: 3,
|
||||||
|
disableAnimation: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: Species.SHUCKLE.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
scale: 1.5,
|
||||||
|
x: 20,
|
||||||
|
y: 10,
|
||||||
|
yShadow: 7
|
||||||
|
},
|
||||||
|
]) // Set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
disableSwitch: true,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.SHUCKLE),
|
||||||
|
isBoss: true,
|
||||||
|
bossSegments: 5,
|
||||||
|
mysteryEncounterData: new MysteryEncounterPokemonData(1.5),
|
||||||
|
nature: Nature.BOLD,
|
||||||
|
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.stat_boost`);
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF, BattleStat.SPDEF], 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
loadCustomMovesForEncounter(scene, [Moves.GASTRO_ACID, Moves.STEALTH_ROCK]);
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Do blackout and hide intro visuals during blackout
|
||||||
|
scene.time.delayedCall(750, () => {
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
// -20 to all base stats of highest BST, +10 to all base stats of rest of party
|
||||||
|
// Get highest BST mon
|
||||||
|
const party = scene.getParty();
|
||||||
|
let highestBst: PlayerPokemon | null = null;
|
||||||
|
let statTotal = 0;
|
||||||
|
for (const pokemon of party) {
|
||||||
|
if (!highestBst) {
|
||||||
|
highestBst = pokemon;
|
||||||
|
statTotal = pokemon.getSpeciesForm().getBaseStatTotal();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = pokemon.getSpeciesForm().getBaseStatTotal();
|
||||||
|
if (total > statTotal) {
|
||||||
|
highestBst = pokemon;
|
||||||
|
statTotal = total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!highestBst) {
|
||||||
|
highestBst = party[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyPlayerPokemonBST(highestBst, -20);
|
||||||
|
for (const pokemon of party) {
|
||||||
|
if (highestBst.id === pokemon.id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyPlayerPokemonBST(pokemon, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
encounter.setDialogueToken("highBstPokemon", highestBst.getNameToRender());
|
||||||
|
await showEncounterText(scene, `${namespace}.option.1.selected_2`, undefined, true);
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SOUL_DEW], fillRemaining: true });
|
||||||
|
encounter.startOfBattleEffects.push(
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.GASTRO_ACID),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.STEALTH_ROCK),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,497 @@
|
||||||
|
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Nature } from "#enums/nature";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { SpeciesFormChangeManualTrigger } from "#app/data/pokemon-forms";
|
||||||
|
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/ability";
|
||||||
|
import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||||
|
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
||||||
|
import { ReturnPhase } from "#app/phases/return-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:theWinstrateChallenge";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Winstrate Challenge encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/136 | GitHub Issue #136}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const TheWinstrateChallengeEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_WINSTRATE_CHALLENGE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
|
.withSceneWaveRangeRequirement(80, 180)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "vito",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: false,
|
||||||
|
x: 16,
|
||||||
|
y: -4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "vivi",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: false,
|
||||||
|
x: -14,
|
||||||
|
y: -4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "victor",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "victoria",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: 40,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "vicky",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
x: 3,
|
||||||
|
y: 5,
|
||||||
|
yShadow: 5
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Loaded back to front for pop() operations
|
||||||
|
encounter.enemyPartyConfigs.push(getVitoTrainerConfig(scene));
|
||||||
|
encounter.enemyPartyConfigs.push(getVickyTrainerConfig(scene));
|
||||||
|
encounter.enemyPartyConfigs.push(getViviTrainerConfig(scene));
|
||||||
|
encounter.enemyPartyConfigs.push(getVictoriaTrainerConfig(scene));
|
||||||
|
encounter.enemyPartyConfigs.push(getVictorTrainerConfig(scene));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: "trainerNames:victor",
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Spawn 5 trainer battles back to back with Macho Brace in rewards
|
||||||
|
// scene.currentBattle.mysteryEncounter.continuousEncounter = true;
|
||||||
|
scene.currentBattle.mysteryEncounter.doContinueEncounter = (scene: BattleScene) => {
|
||||||
|
return endTrainerBattleAndShowDialogue(scene);
|
||||||
|
};
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||||
|
await spawnNextTrainerOrEndEncounter(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Refuse the challenge, they full heal the party and give the player a Rarer Candy
|
||||||
|
scene.unshiftPhase(new PartyHealPhase(scene, true));
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY], fillRemaining: false });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const nextConfig = encounter.enemyPartyConfigs.pop();
|
||||||
|
if (!nextConfig) {
|
||||||
|
await transitionMysteryEncounterIntroVisuals(scene, false, false);
|
||||||
|
await showEncounterDialogue(scene, `${namespace}.victory`, `${namespace}.speaker`);
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE], fillRemaining: false });
|
||||||
|
encounter.doContinueEncounter = undefined;
|
||||||
|
leaveEncounterWithoutBattle(scene, false, MysteryEncounterMode.TRAINER_BATTLE);
|
||||||
|
} else {
|
||||||
|
await initBattleWithEnemyConfig(scene, nextConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
if (scene.currentBattle.mysteryEncounter.enemyPartyConfigs.length === 0) {
|
||||||
|
// Battle is over
|
||||||
|
const trainer = scene.currentBattle.trainer;
|
||||||
|
if (trainer) {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: trainer,
|
||||||
|
x: "+=16",
|
||||||
|
y: "-=16",
|
||||||
|
alpha: 0,
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
duration: 750,
|
||||||
|
onComplete: () => {
|
||||||
|
scene.field.remove(trainer, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await spawnNextTrainerOrEndEncounter(scene);
|
||||||
|
resolve(); // Wait for all dialogue/post battle stuff to complete before resolving
|
||||||
|
} else {
|
||||||
|
scene.arena.resetArenaEffects();
|
||||||
|
const playerField = scene.getPlayerField();
|
||||||
|
playerField.forEach((_, p) => scene.unshiftPhase(new ReturnPhase(scene, p)));
|
||||||
|
|
||||||
|
for (const pokemon of scene.getParty()) {
|
||||||
|
// Only trigger form change when Eiscue is in Noice form
|
||||||
|
// Hardcoded Eiscue for now in case it is fused with another pokemon
|
||||||
|
if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) {
|
||||||
|
scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.resetBattleData();
|
||||||
|
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.unshiftPhase(new ShowTrainerPhase(scene));
|
||||||
|
// Hide the trainer and init next battle
|
||||||
|
const trainer = scene.currentBattle.trainer;
|
||||||
|
// Unassign previous trainer from battle so it isn't destroyed before animation completes
|
||||||
|
scene.currentBattle.trainer = null;
|
||||||
|
await spawnNextTrainerOrEndEncounter(scene);
|
||||||
|
if (trainer) {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: trainer,
|
||||||
|
x: "+=16",
|
||||||
|
y: "-=16",
|
||||||
|
alpha: 0,
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
duration: 750,
|
||||||
|
onComplete: () => {
|
||||||
|
scene.field.remove(trainer, true);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||||
|
return {
|
||||||
|
trainerType: TrainerType.VICTOR,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.SWELLOW),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 0, // Guts
|
||||||
|
nature: Nature.ADAMANT,
|
||||||
|
moveSet: [Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.FOCUS_BAND) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.OBSTAGOON),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 1, // Guts
|
||||||
|
nature: Nature.ADAMANT,
|
||||||
|
moveSet: [Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||||
|
return {
|
||||||
|
trainerType: TrainerType.VICTORIA,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.ROSERADE),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 0, // Natural Cure
|
||||||
|
nature: Nature.CALM,
|
||||||
|
moveSet: [Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.GARDEVOIR),
|
||||||
|
isBoss: false,
|
||||||
|
formIndex: 1,
|
||||||
|
nature: Nature.TIMID,
|
||||||
|
moveSet: [Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.PSYCHIC]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 1,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FAIRY]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 1,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||||
|
return {
|
||||||
|
trainerType: TrainerType.VIVI,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.SEAKING),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 3, // Lightning Rod
|
||||||
|
nature: Nature.ADAMANT,
|
||||||
|
moveSet: [Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 4,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.BRELOOM),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 1, // Poison Heal
|
||||||
|
nature: Nature.JOLLY,
|
||||||
|
moveSet: [Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 4,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.CAMERUPT),
|
||||||
|
isBoss: false,
|
||||||
|
formIndex: 1,
|
||||||
|
nature: Nature.CALM,
|
||||||
|
moveSet: [Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 3,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||||
|
return {
|
||||||
|
trainerType: TrainerType.VICKY,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.MEDICHAM),
|
||||||
|
isBoss: false,
|
||||||
|
formIndex: 1,
|
||||||
|
nature: Nature.IMPISH,
|
||||||
|
moveSet: [Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||||
|
return {
|
||||||
|
trainerType: TrainerType.VITO,
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.HISUI_ELECTRODE),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 0, // Soundproof
|
||||||
|
nature: Nature.MODEST,
|
||||||
|
moveSet: [Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.SWALOT),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 2, // Gluttony
|
||||||
|
nature: Nature.QUIET,
|
||||||
|
moveSet: [Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.STARF]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SALAC]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LANSAT]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LIECHI]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.PETAYA]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LEPPA]) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.DODRIO),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 2, // Tangled Feet
|
||||||
|
nature: Nature.JOLLY,
|
||||||
|
moveSet: [Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.ALAKAZAM),
|
||||||
|
isBoss: false,
|
||||||
|
formIndex: 1,
|
||||||
|
nature: Nature.BOLD,
|
||||||
|
moveSet: [Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
species: getPokemonSpecies(Species.DARMANITAN),
|
||||||
|
isBoss: false,
|
||||||
|
abilityIndex: 0, // Sheer Force
|
||||||
|
nature: Nature.IMPISH,
|
||||||
|
moveSet: [Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifierType: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: 2,
|
||||||
|
isTransferable: false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,415 @@
|
||||||
|
import { Ability, allAbilities } from "#app/data/ability";
|
||||||
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { getNatureName, Nature } from "#app/data/nature";
|
||||||
|
import { speciesStarters } from "#app/data/pokemon-species";
|
||||||
|
import { getStatName } from "#app/data/pokemon-stat";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { AbilityAttr } from "#app/system/game-data";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { isNullOrUndefined, randSeedShuffle } from "#app/utils";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||||
|
|
||||||
|
/** The i18n namespace for the encounter */
|
||||||
|
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: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TRAINING_SESSION)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 unfainted pokemon in party
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "training_gear",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
y: 6,
|
||||||
|
x: 5,
|
||||||
|
yShadow: -2
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
encounter.misc = {
|
||||||
|
playerPokemon: pokemon,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that are not KOed/legal can be trained
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
const meetsReqs = pokemon.isAllowedInBattle();
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||||
|
|
||||||
|
// Spawn light training session with chosen pokemon
|
||||||
|
// Every 50 waves, add +1 boss segment, capping at 5
|
||||||
|
const segments = Math.min(
|
||||||
|
2 + Math.floor(scene.currentBattle.waveIndex / 50),
|
||||||
|
5
|
||||||
|
);
|
||||||
|
const modifiers = new ModifiersHolder();
|
||||||
|
const config = getEnemyConfig(
|
||||||
|
scene,
|
||||||
|
playerPokemon,
|
||||||
|
segments,
|
||||||
|
modifiers
|
||||||
|
);
|
||||||
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
|
const onBeforeRewardsPhase = () => {
|
||||||
|
encounter.setDialogueToken("stat1", "-");
|
||||||
|
encounter.setDialogueToken("stat2", "-");
|
||||||
|
// Add the pokemon back to party with IV boost
|
||||||
|
const ivIndexes: any[] = [];
|
||||||
|
playerPokemon.ivs.forEach((iv, index) => {
|
||||||
|
if (iv < 31) {
|
||||||
|
ivIndexes.push({ iv: iv, index: index });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Improves 2 random non-maxed IVs
|
||||||
|
// +10 if IV is < 10, +5 if between 10-20, and +3 if > 20
|
||||||
|
// A 0-4 starting IV will cap in 6 encounters (assuming you always rolled that IV)
|
||||||
|
// 5-14 starting IV caps in 5 encounters
|
||||||
|
// 15-19 starting IV caps in 4 encounters
|
||||||
|
// 20-24 starting IV caps in 3 encounters
|
||||||
|
// 25-27 starting IV caps in 2 encounters
|
||||||
|
let improvedCount = 0;
|
||||||
|
while (ivIndexes.length > 0 && improvedCount < 2) {
|
||||||
|
randSeedShuffle(ivIndexes);
|
||||||
|
const ivToChange = ivIndexes.pop();
|
||||||
|
let newVal = ivToChange.iv;
|
||||||
|
if (improvedCount === 0) {
|
||||||
|
encounter.setDialogueToken(
|
||||||
|
"stat1",
|
||||||
|
getStatName(ivToChange.index) ?? ""
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken(
|
||||||
|
"stat2",
|
||||||
|
getStatName(ivToChange.index) ?? ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corrects required encounter breakpoints to be continuous for all IV values
|
||||||
|
if (ivToChange.iv <= 21 && ivToChange.iv - (1 % 5) === 0) {
|
||||||
|
newVal += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
newVal += ivToChange.iv <= 10 ? 10 : ivToChange.iv <= 20 ? 5 : 3;
|
||||||
|
newVal = Math.min(newVal, 31);
|
||||||
|
playerPokemon.ivs[ivToChange.index] = newVal;
|
||||||
|
improvedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (improvedCount > 0) {
|
||||||
|
playerPokemon.calculateStats();
|
||||||
|
scene.gameData.updateSpeciesDexIvs(
|
||||||
|
playerPokemon.species.getRootSpeciesId(true),
|
||||||
|
playerPokemon.ivs
|
||||||
|
);
|
||||||
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pokemon and mods back
|
||||||
|
scene.getParty().push(playerPokemon);
|
||||||
|
for (const mod of modifiers.value) {
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.1.finished`);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.2.select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
// Open menu for selecting pokemon and Nature
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const natures = new Array(25).fill(null).map((val, i) => i as Nature);
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for nature selection
|
||||||
|
return natures.map((nature: Nature) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: getNatureName(nature, true, true, true, scene.uiTheme),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and second option selected
|
||||||
|
encounter.setDialogueToken("nature", getNatureName(nature));
|
||||||
|
encounter.misc = {
|
||||||
|
playerPokemon: pokemon,
|
||||||
|
chosenNature: nature,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that are not KOed/legal can be trained
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
const meetsReqs = pokemon.isAllowedInBattle();
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||||
|
|
||||||
|
// Spawn medium training session with chosen pokemon
|
||||||
|
// Every 40 waves, add +1 boss segment, capping at 6
|
||||||
|
const segments = Math.min(
|
||||||
|
2 + Math.floor(scene.currentBattle.waveIndex / 40),
|
||||||
|
6
|
||||||
|
);
|
||||||
|
const modifiers = new ModifiersHolder();
|
||||||
|
const config = getEnemyConfig(
|
||||||
|
scene,
|
||||||
|
playerPokemon,
|
||||||
|
segments,
|
||||||
|
modifiers
|
||||||
|
);
|
||||||
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
|
const onBeforeRewardsPhase = () => {
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.2.finished`);
|
||||||
|
// Add the pokemon back to party with Nature change
|
||||||
|
playerPokemon.setNature(encounter.misc.chosenNature);
|
||||||
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
|
||||||
|
// Add pokemon and mods back
|
||||||
|
scene.getParty().push(playerPokemon);
|
||||||
|
for (const mod of modifiers.value) {
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.3.select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
// Open menu for selecting pokemon and ability to learn
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for ability selection
|
||||||
|
const speciesForm = !!pokemon.getFusionSpeciesForm()
|
||||||
|
? pokemon.getFusionSpeciesForm()
|
||||||
|
: pokemon.getSpeciesForm();
|
||||||
|
const abilityCount = speciesForm.getAbilityCount();
|
||||||
|
const abilities = new Array(abilityCount)
|
||||||
|
.fill(null)
|
||||||
|
.map((val, i) => allAbilities[speciesForm.getAbility(i)]);
|
||||||
|
return abilities.map((ability: Ability, index) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: ability.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and ability selected
|
||||||
|
encounter.setDialogueToken("ability", ability.name);
|
||||||
|
encounter.misc = {
|
||||||
|
playerPokemon: pokemon,
|
||||||
|
abilityIndex: index,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onHover: () => {
|
||||||
|
scene.ui.showText(ability.description);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that are not KOed/legal can be trained
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
const meetsReqs = pokemon.isAllowedInBattle();
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||||
|
|
||||||
|
// Spawn hard training session with chosen pokemon
|
||||||
|
// Every 30 waves, add +1 boss segment, capping at 6
|
||||||
|
// Also starts with +1 to all stats
|
||||||
|
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 30), 6);
|
||||||
|
const modifiers = new ModifiersHolder();
|
||||||
|
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
||||||
|
config.pokemonConfigs![0].tags = [
|
||||||
|
BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON,
|
||||||
|
];
|
||||||
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
|
const onBeforeRewardsPhase = () => {
|
||||||
|
queueEncounterMessage(scene, `${namespace}.option.3.finished`);
|
||||||
|
// Add the pokemon back to party with ability change
|
||||||
|
const abilityIndex = encounter.misc.abilityIndex;
|
||||||
|
if (!!playerPokemon.getFusionSpeciesForm()) {
|
||||||
|
playerPokemon.fusionAbilityIndex = abilityIndex;
|
||||||
|
if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies!.speciesId)) {
|
||||||
|
scene.gameData.starterData[playerPokemon.fusionSpecies!.speciesId]
|
||||||
|
.abilityAttr |=
|
||||||
|
abilityIndex !== 1 || playerPokemon.fusionSpecies!.ability2
|
||||||
|
? Math.pow(2, playerPokemon.fusionAbilityIndex)
|
||||||
|
: AbilityAttr.ABILITY_HIDDEN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerPokemon.abilityIndex = abilityIndex;
|
||||||
|
if (
|
||||||
|
speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)
|
||||||
|
) {
|
||||||
|
scene.gameData.starterData[
|
||||||
|
playerPokemon.species.speciesId
|
||||||
|
].abilityAttr |=
|
||||||
|
abilityIndex !== 1 || playerPokemon.species.ability2
|
||||||
|
? Math.pow(2, playerPokemon.abilityIndex)
|
||||||
|
: AbilityAttr.ABILITY_HIDDEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playerPokemon.getAbility();
|
||||||
|
playerPokemon.calculateStats();
|
||||||
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
|
||||||
|
// Add pokemon and mods back
|
||||||
|
scene.getParty().push(playerPokemon);
|
||||||
|
for (const mod of modifiers.value) {
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig {
|
||||||
|
playerPokemon.resetSummonData();
|
||||||
|
|
||||||
|
// Passes modifiers by reference
|
||||||
|
modifiers.value = scene.findModifiers((m) => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === playerPokemon.id) as PokemonHeldItemModifier[];
|
||||||
|
const modifierConfigs = modifiers.value.map((mod) => {
|
||||||
|
return {
|
||||||
|
modifierType: mod.type
|
||||||
|
};
|
||||||
|
}) as HeldModifierConfig[];
|
||||||
|
|
||||||
|
const data = new PokemonData(playerPokemon);
|
||||||
|
return {
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: playerPokemon.species,
|
||||||
|
isBoss: true,
|
||||||
|
bossSegments: segments,
|
||||||
|
formIndex: playerPokemon.formIndex,
|
||||||
|
level: playerPokemon.level,
|
||||||
|
dataSource: data,
|
||||||
|
modifierConfigs: modifierConfigs,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModifiersHolder {
|
||||||
|
public value: PokemonHeldItemModifier[] = [];
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#app/modifier/modifier";
|
||||||
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import i18next from "#app/plugins/i18n";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:trashToTreasure";
|
||||||
|
|
||||||
|
const SOUND_EFFECT_WAIT_TIME = 700;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trash to Treasure encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/74 | GitHub Issue #74}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const TrashToTreasureEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TRASH_TO_TREASURE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withMaxAllowedEncounters(1)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.GARBODOR.toString() + "-gigantamax",
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: false,
|
||||||
|
disableAnimation: true,
|
||||||
|
scale: 1.5,
|
||||||
|
y: 8,
|
||||||
|
tint: 0.4
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.withAutoHideIntroVisuals(false)
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const bossSpecies = getPokemonSpecies(Species.GARBODOR);
|
||||||
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
|
species: bossSpecies,
|
||||||
|
isBoss: true,
|
||||||
|
formIndex: 1, // Gmax
|
||||||
|
bossSegmentModifier: 1, // +1 Segment from normal
|
||||||
|
moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH]
|
||||||
|
};
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
pokemonConfigs: [pokemonConfig],
|
||||||
|
disableSwitch: true
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
// Load animations/sfx for Garbodor fight start moves
|
||||||
|
loadCustomMovesForEncounter(scene, [Moves.TOXIC, Moves.AMNESIA]);
|
||||||
|
|
||||||
|
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
|
||||||
|
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Play Dig2 and then Venom Drench sfx
|
||||||
|
doGarbageDig(scene);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Gain 2 Leftovers and 2 Shell Bell
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
await tryApplyDigRewardItems(scene);
|
||||||
|
|
||||||
|
// Give the player the Black Sludge curse
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE));
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Investigate garbage, battle Gmax Garbodor
|
||||||
|
scene.setFieldScale(0.75);
|
||||||
|
await showEncounterText(scene, `${namespace}.option.2.selected_2`);
|
||||||
|
transitionMysteryEncounterIntroVisuals(scene);
|
||||||
|
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
|
||||||
|
encounter.startOfBattleEffects.push(
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.PLAYER],
|
||||||
|
move: new PokemonMove(Moves.TOXIC),
|
||||||
|
ignorePp: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
targets: [BattlerIndex.ENEMY],
|
||||||
|
move: new PokemonMove(Moves.AMNESIA),
|
||||||
|
ignorePp: true
|
||||||
|
});
|
||||||
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
async function tryApplyDigRewardItems(scene: BattleScene) {
|
||||||
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
|
const leftovers = generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType;
|
||||||
|
|
||||||
|
const party = scene.getParty();
|
||||||
|
|
||||||
|
// Iterate over the party until an item was successfully given
|
||||||
|
// First leftovers
|
||||||
|
for (const pokemon of party) {
|
||||||
|
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||||
|
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
|
||||||
|
|
||||||
|
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount(scene)) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, pokemon, leftovers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second leftovers
|
||||||
|
for (const pokemon of party) {
|
||||||
|
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||||
|
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
|
||||||
|
|
||||||
|
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount(scene)) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, pokemon, leftovers);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + leftovers.name }), undefined, true);
|
||||||
|
|
||||||
|
// First Shell bell
|
||||||
|
for (const pokemon of party) {
|
||||||
|
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||||
|
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
|
||||||
|
|
||||||
|
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount(scene)) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, pokemon, shellBell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second Shell bell
|
||||||
|
for (const pokemon of party) {
|
||||||
|
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
|
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||||
|
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
|
||||||
|
|
||||||
|
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount(scene)) {
|
||||||
|
await applyModifierTypeToPlayerPokemon(scene, pokemon, shellBell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.playSound("item_fanfare");
|
||||||
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + shellBell.name }), undefined, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doGarbageDig(scene: BattleScene) {
|
||||||
|
scene.playSound("PRSFX- Dig2");
|
||||||
|
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => {
|
||||||
|
scene.playSound("PRSFX- Dig2");
|
||||||
|
scene.playSound("PRSFX- Venom Drench", { volume: 2 });
|
||||||
|
});
|
||||||
|
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME * 2, () => {
|
||||||
|
scene.playSound("PRSFX- Dig2");
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,541 @@
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
|
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||||
|
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { HiddenAbilityRateBoosterModifier, PokemonBaseStatTotalModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { achvs } from "#app/system/achv";
|
||||||
|
import { speciesEggMoves } from "#app/data/egg-moves";
|
||||||
|
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||||
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { Stat } from "#app/data/pokemon-stat";
|
||||||
|
import i18next from "#app/plugins/i18n";
|
||||||
|
import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence";
|
||||||
|
import { getLevelTotalExp } from "#app/data/exp";
|
||||||
|
|
||||||
|
/** i18n namespace for encounter */
|
||||||
|
const namespace = "mysteryEncounter:weirdDream";
|
||||||
|
|
||||||
|
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, Urshifu, the Poison Chain trio, Ogerpon */
|
||||||
|
const excludedPokemon = [
|
||||||
|
Species.ETERNATUS,
|
||||||
|
/** UBs */
|
||||||
|
Species.NIHILEGO,
|
||||||
|
Species.BUZZWOLE,
|
||||||
|
Species.PHEROMOSA,
|
||||||
|
Species.XURKITREE,
|
||||||
|
Species.CELESTEELA,
|
||||||
|
Species.KARTANA,
|
||||||
|
Species.GUZZLORD,
|
||||||
|
Species.POIPOLE,
|
||||||
|
Species.NAGANADEL,
|
||||||
|
Species.STAKATAKA,
|
||||||
|
Species.BLACEPHALON,
|
||||||
|
/** Paradox */
|
||||||
|
Species.GREAT_TUSK,
|
||||||
|
Species.SCREAM_TAIL,
|
||||||
|
Species.BRUTE_BONNET,
|
||||||
|
Species.FLUTTER_MANE,
|
||||||
|
Species.SLITHER_WING,
|
||||||
|
Species.SANDY_SHOCKS,
|
||||||
|
Species.ROARING_MOON,
|
||||||
|
Species.WALKING_WAKE,
|
||||||
|
Species.GOUGING_FIRE,
|
||||||
|
Species.RAGING_BOLT,
|
||||||
|
Species.IRON_TREADS,
|
||||||
|
Species.IRON_BUNDLE,
|
||||||
|
Species.IRON_HANDS,
|
||||||
|
Species.IRON_JUGULIS,
|
||||||
|
Species.IRON_MOTH,
|
||||||
|
Species.IRON_THORNS,
|
||||||
|
Species.IRON_VALIANT,
|
||||||
|
Species.IRON_LEAVES,
|
||||||
|
Species.IRON_BOULDER,
|
||||||
|
Species.IRON_CROWN,
|
||||||
|
/** These are banned so they don't appear in the < 570 BST pool */
|
||||||
|
Species.COSMOG,
|
||||||
|
Species.MELTAN,
|
||||||
|
Species.KUBFU,
|
||||||
|
Species.COSMOEM,
|
||||||
|
Species.POIPOLE,
|
||||||
|
Species.TERAPAGOS,
|
||||||
|
Species.TYPE_NULL,
|
||||||
|
Species.CALYREX,
|
||||||
|
Species.NAGANADEL,
|
||||||
|
Species.URSHIFU,
|
||||||
|
Species.OGERPON,
|
||||||
|
Species.OKIDOGI,
|
||||||
|
Species.MUNKIDORI,
|
||||||
|
Species.FEZANDIPITI,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weird Dream encounter.
|
||||||
|
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/137 | GitHub Issue #137}
|
||||||
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||||
|
*/
|
||||||
|
export const WeirdDreamEncounter: MysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.WEIRD_DREAM)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "girawitch",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: false,
|
||||||
|
y: 4
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}.intro`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: `${namespace}.speaker`,
|
||||||
|
text: `${namespace}.intro_dialogue`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withTitle(`${namespace}.title`)
|
||||||
|
.withDescription(`${namespace}.description`)
|
||||||
|
.withQuery(`${namespace}.query`)
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
scene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3");
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
|
// Change the bgm
|
||||||
|
scene.fadeOutBgm(3000, false);
|
||||||
|
scene.time.delayedCall(3000, () => {
|
||||||
|
scene.playBgm("mystery_encounter_weird_dream");
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withHasDexProgress(true)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.1.selected`,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Play the animation as the player goes through the dialogue
|
||||||
|
scene.time.delayedCall(1000, () => {
|
||||||
|
doShowDreamBackground(scene);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate all the newly transformed Pokemon and begin asset load
|
||||||
|
const teamTransformations = getTeamTransformations(scene);
|
||||||
|
const loadAssets = teamTransformations.map(t => (t.newPokemon as PlayerPokemon).loadAssets());
|
||||||
|
scene.currentBattle.mysteryEncounter.misc = {
|
||||||
|
teamTransformations,
|
||||||
|
loadAssets
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Starts cutscene dialogue, but does not await so that cutscene plays as player goes through dialogue
|
||||||
|
const cutsceneDialoguePromise = showEncounterText(scene, `${namespace}.option.1.cutscene`);
|
||||||
|
|
||||||
|
// Change the entire player's party
|
||||||
|
// Wait for all new Pokemon assets to be loaded before showing transformation animations
|
||||||
|
await Promise.all(scene.currentBattle.mysteryEncounter.misc.loadAssets);
|
||||||
|
const transformations = scene.currentBattle.mysteryEncounter.misc.teamTransformations;
|
||||||
|
|
||||||
|
// If there are 1-3 transformations, do them centered back to back
|
||||||
|
// Otherwise, the first 3 transformations are executed side-by-side, then any remaining 1-3 transformations occur in those same respective positions
|
||||||
|
if (transformations.length <= 3) {
|
||||||
|
for (const transformation of transformations) {
|
||||||
|
const pokemon1 = transformation.previousPokemon;
|
||||||
|
const pokemon2 = transformation.newPokemon;
|
||||||
|
|
||||||
|
await doPokemonTransformationSequence(scene, pokemon1, pokemon2, TransformationScreenPosition.CENTER);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await doSideBySideTransformations(scene, transformations);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure player has finished cutscene dialogue
|
||||||
|
await cutsceneDialoguePromise;
|
||||||
|
|
||||||
|
doHideDreamBackground(scene);
|
||||||
|
await showEncounterText(scene, `${namespace}.option.1.dream_complete`);
|
||||||
|
|
||||||
|
await doNewTeamPostProcess(scene, transformations);
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT]});
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.2.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Reduce party levels by 20%
|
||||||
|
for (const pokemon of scene.getParty()) {
|
||||||
|
pokemon.level = Math.max(Math.ceil(0.8 * pokemon.level), 1);
|
||||||
|
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
||||||
|
pokemon.levelExp = 0;
|
||||||
|
|
||||||
|
pokemon.calculateStats();
|
||||||
|
pokemon.updateInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
interface PokemonTransformation {
|
||||||
|
previousPokemon: PlayerPokemon;
|
||||||
|
newSpecies: PokemonSpecies;
|
||||||
|
newPokemon: PlayerPokemon;
|
||||||
|
heldItems: PokemonHeldItemModifier[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
|
||||||
|
const party = scene.getParty();
|
||||||
|
// Removes all pokemon from the party
|
||||||
|
const alreadyUsedSpecies: PokemonSpecies[] = [];
|
||||||
|
const pokemonTransformations: PokemonTransformation[] = party.map(p => {
|
||||||
|
return {
|
||||||
|
previousPokemon: p
|
||||||
|
} as PokemonTransformation;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only 1 Pokemon can be transformed into BST higher than 600
|
||||||
|
let hasPokemonBstHigherThan600 = false;
|
||||||
|
// Only 1 other Pokemon can be transformed into BST between 570-600
|
||||||
|
let hasPokemonBstBetween570And600 = false;
|
||||||
|
|
||||||
|
// First, roll 2 of the party members to new Pokemon at a +90 to +110 BST difference
|
||||||
|
// Then, roll the remainder of the party members at a +40 to +50 BST difference
|
||||||
|
const numPokemon = party.length;
|
||||||
|
for (let i = 0; i < numPokemon; i++) {
|
||||||
|
const removed = party[randSeedInt(party.length)];
|
||||||
|
const index = pokemonTransformations.findIndex(p => p.previousPokemon.id === removed.id);
|
||||||
|
pokemonTransformations[index].heldItems = removed.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
|
||||||
|
scene.removePokemonFromPlayerParty(removed, false);
|
||||||
|
|
||||||
|
const bst = getOriginalBst(scene, removed);
|
||||||
|
let newBstRange;
|
||||||
|
if (i < 2) {
|
||||||
|
newBstRange = [90, 110];
|
||||||
|
} else {
|
||||||
|
newBstRange = [40, 50];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSpecies = getTransformedSpecies(bst, newBstRange, hasPokemonBstHigherThan600, hasPokemonBstBetween570And600, alreadyUsedSpecies);
|
||||||
|
|
||||||
|
const newSpeciesBst = newSpecies.getBaseStatTotal();
|
||||||
|
if (newSpeciesBst > 600) {
|
||||||
|
hasPokemonBstHigherThan600 = true;
|
||||||
|
}
|
||||||
|
if (newSpeciesBst <= 600 && newSpeciesBst >= 570) {
|
||||||
|
hasPokemonBstBetween570And600 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pokemonTransformations[index].newSpecies = newSpecies;
|
||||||
|
alreadyUsedSpecies.push(newSpecies);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const transformation of pokemonTransformations) {
|
||||||
|
const newAbilityIndex = randSeedInt(transformation.newSpecies.getAbilityCount());
|
||||||
|
const newPlayerPokemon = scene.addPlayerPokemon(transformation.newSpecies, transformation.previousPokemon.level, newAbilityIndex, undefined);
|
||||||
|
transformation.newPokemon = newPlayerPokemon;
|
||||||
|
scene.getParty().push(newPlayerPokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pokemonTransformations;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doNewTeamPostProcess(scene: BattleScene, transformations: PokemonTransformation[]) {
|
||||||
|
let atLeastOneNewStarter = false;
|
||||||
|
for (const transformation of transformations) {
|
||||||
|
const previousPokemon = transformation.previousPokemon;
|
||||||
|
const newPokemon = transformation.newPokemon;
|
||||||
|
const speciesRootForm = newPokemon.species.getRootSpeciesId();
|
||||||
|
|
||||||
|
// Roll HA a second time
|
||||||
|
if (newPokemon.species.abilityHidden) {
|
||||||
|
const hiddenIndex = newPokemon.species.ability2 ? 2 : 1;
|
||||||
|
if (newPokemon.abilityIndex < hiddenIndex) {
|
||||||
|
const hiddenAbilityChance = new IntegerHolder(256);
|
||||||
|
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||||
|
|
||||||
|
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||||
|
|
||||||
|
if (hasHiddenAbility) {
|
||||||
|
newPokemon.abilityIndex = hiddenIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll IVs a second time
|
||||||
|
newPokemon.ivs = newPokemon.ivs.map(iv => {
|
||||||
|
const newValue = randSeedInt(31);
|
||||||
|
return newValue > iv ? newValue : iv;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it
|
||||||
|
if (newPokemon.getSpeciesForm().getBaseStatTotal() <= 570 || newPokemon.isShiny()) {
|
||||||
|
if (newPokemon.getSpeciesForm().abilityHidden && newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1) {
|
||||||
|
scene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPokemon.species.subLegendary) {
|
||||||
|
scene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPokemon.species.legendary) {
|
||||||
|
scene.validateAchv(achvs.CATCH_LEGENDARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPokemon.species.mythical) {
|
||||||
|
scene.validateAchv(achvs.CATCH_MYTHICAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.gameData.updateSpeciesDexIvs(newPokemon.species.getRootSpeciesId(true), newPokemon.ivs);
|
||||||
|
const newStarterUnlocked = await scene.gameData.setPokemonCaught(newPokemon, true, false, false);
|
||||||
|
if (newStarterUnlocked) {
|
||||||
|
atLeastOneNewStarter = true;
|
||||||
|
queueEncounterMessage(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the previous pokemon had higher IVs, override to those (after updating dex IVs > prevents perfect 31s on a new unlock)
|
||||||
|
newPokemon.ivs = newPokemon.ivs.map((iv, index) => {
|
||||||
|
return previousPokemon.ivs[index] > iv ? previousPokemon.ivs[index] : iv;
|
||||||
|
});
|
||||||
|
|
||||||
|
// For pokemon that the player owns (including ones just caught), gain a candy
|
||||||
|
if (!!scene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||||
|
scene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the moveset of the new pokemon to be the same as previous, but with 1 egg move of the new species
|
||||||
|
newPokemon.moveset = previousPokemon.moveset;
|
||||||
|
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
|
||||||
|
const eggMoves = speciesEggMoves[speciesRootForm];
|
||||||
|
const eggMoveIndex = randSeedInt(4);
|
||||||
|
const randomEggMove = eggMoves[eggMoveIndex];
|
||||||
|
if (newPokemon.moveset.length < 4) {
|
||||||
|
newPokemon.moveset.push(new PokemonMove(randomEggMove));
|
||||||
|
} else {
|
||||||
|
newPokemon.moveset[randSeedInt(4)] = new PokemonMove(randomEggMove);
|
||||||
|
}
|
||||||
|
// For pokemon that the player owns (including ones just caught), unlock the egg move
|
||||||
|
if (!!scene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||||
|
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), eggMoveIndex, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Randomize the second type of the pokemon
|
||||||
|
// If the pokemon does not normally have a second type, it will gain 1
|
||||||
|
const newTypes = [newPokemon.getTypes()[0]];
|
||||||
|
let newType = randSeedInt(18) as Type;
|
||||||
|
while (newType === newTypes[0]) {
|
||||||
|
newType = randSeedInt(18) as Type;
|
||||||
|
}
|
||||||
|
newTypes.push(newType);
|
||||||
|
if (!newPokemon.mysteryEncounterData) {
|
||||||
|
newPokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes);
|
||||||
|
} else {
|
||||||
|
newPokemon.mysteryEncounterData.types = newTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of transformation.heldItems) {
|
||||||
|
item.pokemonId = newPokemon.id;
|
||||||
|
scene.addModifier(item, false, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any pokemon that is at or below 450 BST gets +20 permanent BST to 3 stats: HP, lowest of Atk/SpAtk, and lowest of Def/SpDef
|
||||||
|
if (newPokemon.getSpeciesForm().getBaseStatTotal() <= 450) {
|
||||||
|
const stats: Stat[] = [Stat.HP];
|
||||||
|
const baseStats = newPokemon.getSpeciesForm().baseStats.slice(0);
|
||||||
|
// Attack or SpAtk
|
||||||
|
stats.push(baseStats[Stat.ATK] < baseStats[Stat.SPATK] ? Stat.ATK : Stat.SPATK);
|
||||||
|
// Def or SpDef
|
||||||
|
stats.push(baseStats[Stat.DEF] < baseStats[Stat.SPDEF] ? Stat.DEF : Stat.SPDEF);
|
||||||
|
// const mod = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().newModifier(newPokemon, 20, stats);
|
||||||
|
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().generateType(scene.getParty(), [20, stats]);
|
||||||
|
const modifier = modType?.newModifier(newPokemon);
|
||||||
|
if (modifier) {
|
||||||
|
scene.addModifier(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable passive if previous had it
|
||||||
|
newPokemon.passive = previousPokemon.passive;
|
||||||
|
|
||||||
|
newPokemon.calculateStats();
|
||||||
|
newPokemon.initBattleInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// One random pokemon will get its passive unlocked
|
||||||
|
const passiveDisabledPokemon = scene.getParty().filter(p => !p.passive);
|
||||||
|
if (passiveDisabledPokemon?.length > 0) {
|
||||||
|
passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)].passive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If at least one new starter was unlocked, play 1 fanfare
|
||||||
|
if (atLeastOneNewStarter) {
|
||||||
|
scene.playSound("level_up_fanfare");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginalBst(scene: BattleScene, pokemon: Pokemon) {
|
||||||
|
const baseStats = pokemon.getSpeciesForm().baseStats.slice(0);
|
||||||
|
scene.applyModifiers(PokemonBaseStatTotalModifier, true, pokemon, baseStats);
|
||||||
|
if (pokemon.fusionSpecies) {
|
||||||
|
const fusionBaseStats = pokemon.getFusionSpeciesForm().baseStats;
|
||||||
|
for (let s = 0; s < pokemon.stats.length; s++) {
|
||||||
|
baseStats[s] = Math.ceil((baseStats[s] + fusionBaseStats[s]) / 2);
|
||||||
|
}
|
||||||
|
} else if (scene.gameMode.isSplicedOnly) {
|
||||||
|
for (let s = 0; s < pokemon.stats.length; s++) {
|
||||||
|
baseStats[s] = Math.ceil(baseStats[s] / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return baseStats.reduce((a, b) => a + b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTransformedSpecies(originalBst: number, bstSearchRange: [number, number], hasPokemonBstHigherThan600: boolean, hasPokemonBstBetween570And600: boolean, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies {
|
||||||
|
let newSpecies: PokemonSpecies | undefined;
|
||||||
|
while (isNullOrUndefined(newSpecies)) {
|
||||||
|
const bstCap = originalBst + bstSearchRange[1];
|
||||||
|
const bstMin = Math.max(originalBst + bstSearchRange[0], 0);
|
||||||
|
|
||||||
|
// Get any/all species that fall within the Bst range requirements
|
||||||
|
let validSpecies = allSpecies
|
||||||
|
.filter(s => {
|
||||||
|
const speciesBst = s.getBaseStatTotal();
|
||||||
|
const bstInRange = speciesBst >= bstMin && speciesBst <= bstCap;
|
||||||
|
// Checks that a Pokemon has not already been added in the +600 or 570-600 slots;
|
||||||
|
const validBst = (!hasPokemonBstBetween570And600 || (speciesBst < 570 || speciesBst > 600)) &&
|
||||||
|
(!hasPokemonBstHigherThan600 || speciesBst <= 600);
|
||||||
|
return bstInRange && validBst && !excludedPokemon.includes(s.speciesId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// There must be at least 20 species available before it will choose one
|
||||||
|
if (validSpecies?.length > 20) {
|
||||||
|
validSpecies = randSeedShuffle(validSpecies);
|
||||||
|
newSpecies = validSpecies.pop();
|
||||||
|
while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies!)) {
|
||||||
|
newSpecies = validSpecies.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Expands search rand until a Pokemon is found
|
||||||
|
bstSearchRange[0] -= 10;
|
||||||
|
bstSearchRange[1] += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSpecies!;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doShowDreamBackground(scene: BattleScene) {
|
||||||
|
const transformationContainer = scene.add.container(0, -scene.game.canvas.height / 6);
|
||||||
|
transformationContainer.name = "Dream Background";
|
||||||
|
|
||||||
|
// In case it takes a bit for video to load
|
||||||
|
const transformationStaticBg = scene.add.rectangle(0, 0, scene.game.canvas.width / 6, scene.game.canvas.height / 6, 0);
|
||||||
|
transformationStaticBg.setName("Black Background");
|
||||||
|
transformationStaticBg.setOrigin(0, 0);
|
||||||
|
transformationContainer.add(transformationStaticBg);
|
||||||
|
transformationStaticBg.setVisible(true);
|
||||||
|
|
||||||
|
const transformationVideoBg: Phaser.GameObjects.Video = scene.add.video(0, 0, "evo_bg").stop();
|
||||||
|
transformationVideoBg.setLoop(true);
|
||||||
|
transformationVideoBg.setOrigin(0, 0);
|
||||||
|
transformationVideoBg.setScale(0.4359673025);
|
||||||
|
transformationContainer.add(transformationVideoBg);
|
||||||
|
|
||||||
|
scene.fieldUI.add(transformationContainer);
|
||||||
|
scene.fieldUI.bringToTop(transformationContainer);
|
||||||
|
transformationVideoBg.play();
|
||||||
|
|
||||||
|
transformationContainer.setVisible(true);
|
||||||
|
transformationContainer.alpha = 0;
|
||||||
|
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: transformationContainer,
|
||||||
|
alpha: 1,
|
||||||
|
duration: 3000,
|
||||||
|
ease: "Sine.easeInOut"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doHideDreamBackground(scene: BattleScene) {
|
||||||
|
const transformationContainer = scene.fieldUI.getByName("Dream Background");
|
||||||
|
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: transformationContainer,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 3000,
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
onComplete: () => {
|
||||||
|
scene.fieldUI.remove(transformationContainer, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSideBySideTransformations(scene: BattleScene, transformations: PokemonTransformation[]) {
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
const allTransformationPromises: Promise<void>[] = [];
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const delay = i * 4000;
|
||||||
|
scene.time.delayedCall(delay, () => {
|
||||||
|
const transformation = transformations[i];
|
||||||
|
const pokemon1 = transformation.previousPokemon;
|
||||||
|
const pokemon2 = transformation.newPokemon;
|
||||||
|
const screenPosition = i as TransformationScreenPosition;
|
||||||
|
|
||||||
|
const transformationPromise = doPokemonTransformationSequence(scene, pokemon1, pokemon2, screenPosition)
|
||||||
|
.then(() => {
|
||||||
|
if (transformations.length > i + 3) {
|
||||||
|
const nextTransformationAtPosition = transformations[i + 3];
|
||||||
|
const nextPokemon1 = nextTransformationAtPosition.previousPokemon;
|
||||||
|
const nextPokemon2 = nextTransformationAtPosition.newPokemon;
|
||||||
|
|
||||||
|
allTransformationPromises.push(doPokemonTransformationSequence(scene, nextPokemon1, nextPokemon2, screenPosition));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
allTransformationPromises.push(transformationPromise);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all transformations to be loaded into promise array
|
||||||
|
const id = setInterval(checkAllPromisesExist, 500);
|
||||||
|
async function checkAllPromisesExist() {
|
||||||
|
if (allTransformationPromises.length === transformations.length) {
|
||||||
|
clearInterval(id);
|
||||||
|
await Promise.all(allTransformationPromises);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
|
||||||
|
export class MysteryEncounterData {
|
||||||
|
encounteredEvents: [MysteryEncounterType, MysteryEncounterTier][] = [];
|
||||||
|
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
|
nextEncounterQueue: [MysteryEncounterType, integer][] = [];
|
||||||
|
|
||||||
|
constructor(flags: MysteryEncounterData | null) {
|
||||||
|
if (!isNullOrUndefined(flags)) {
|
||||||
|
Object.assign(this, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { TextStyle } from "#app/ui/text";
|
||||||
|
|
||||||
|
export class TextDisplay {
|
||||||
|
speaker?: string;
|
||||||
|
text: string;
|
||||||
|
style?: TextStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OptionTextDisplay {
|
||||||
|
buttonLabel: string;
|
||||||
|
buttonTooltip?: string;
|
||||||
|
disabledButtonLabel?: string;
|
||||||
|
disabledButtonTooltip?: string;
|
||||||
|
secondOptionPrompt?: string;
|
||||||
|
selected?: TextDisplay[];
|
||||||
|
style?: TextStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EncounterOptionsDialogue {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
query?: string;
|
||||||
|
options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example MysteryEncounterDialogue object:
|
||||||
|
*
|
||||||
|
{
|
||||||
|
intro: [
|
||||||
|
{
|
||||||
|
text: "this is a rendered as a message window (no title display)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: "John"
|
||||||
|
text: "this is a rendered as a dialogue window (title "John" is displayed above text)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
encounterOptionsDialogue: {
|
||||||
|
title: "This is the title displayed at top of encounter description box",
|
||||||
|
description: "This is the description in the middle of encounter description box",
|
||||||
|
query: "This is an optional question displayed at the bottom of the description box (keep it short)",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
buttonLabel: "Option #1 button label (keep these short)",
|
||||||
|
selected: [ // Optional dialogue windows displayed when specific option is selected and before functional logic for the option is executed
|
||||||
|
{
|
||||||
|
text: "You chose option #1 message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: "John"
|
||||||
|
text: "So, you've chosen option #1! It's time to d-d-d-duel!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonLabel: "Option #2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
outro: [
|
||||||
|
{
|
||||||
|
text: "This message will be displayed at the very end of the encounter (i.e. post battle, post reward, etc.)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default class MysteryEncounterDialogue {
|
||||||
|
intro?: TextDisplay[];
|
||||||
|
encounterOptionsDialogue?: EncounterOptionsDialogue;
|
||||||
|
outro?: TextDisplay[];
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
||||||
|
import { Moves } from "#app/enums/moves";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import * as Utils from "#app/utils";
|
||||||
|
import { Type } from "../type";
|
||||||
|
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements";
|
||||||
|
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
||||||
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
|
||||||
|
|
||||||
|
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by {@link MysteryEncounterOptionBuilder} class to define required/optional properties on the {@link MysteryEncounterOption} class when building.
|
||||||
|
*
|
||||||
|
* Should ONLY contain properties that are necessary for {@link MysteryEncounterOption} construction.
|
||||||
|
* Post-construct and flag data properties are defined in the {@link MysteryEncounterOption} class itself.
|
||||||
|
*/
|
||||||
|
export interface IMysteryEncounterOption {
|
||||||
|
optionMode: MysteryEncounterOptionMode;
|
||||||
|
hasDexProgress: boolean;
|
||||||
|
requirements: EncounterSceneRequirement[];
|
||||||
|
primaryPokemonRequirements: EncounterPokemonRequirement[];
|
||||||
|
secondaryPokemonRequirements: EncounterPokemonRequirement[];
|
||||||
|
excludePrimaryFromSecondaryRequirements: boolean;
|
||||||
|
|
||||||
|
dialogue?: OptionTextDisplay;
|
||||||
|
|
||||||
|
onPreOptionPhase?: OptionPhaseCallback;
|
||||||
|
onOptionPhase: OptionPhaseCallback;
|
||||||
|
onPostOptionPhase?: OptionPhaseCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
||||||
|
optionMode: MysteryEncounterOptionMode;
|
||||||
|
hasDexProgress: boolean;
|
||||||
|
requirements: EncounterSceneRequirement[];
|
||||||
|
primaryPokemonRequirements: EncounterPokemonRequirement[];
|
||||||
|
secondaryPokemonRequirements: EncounterPokemonRequirement[];
|
||||||
|
primaryPokemon?: PlayerPokemon;
|
||||||
|
secondaryPokemon?: PlayerPokemon[];
|
||||||
|
excludePrimaryFromSecondaryRequirements: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialogue object containing all the dialogue, messages, tooltips, etc. for this option
|
||||||
|
* Will be populated on MysteryEncounter initialization
|
||||||
|
*/
|
||||||
|
dialogue?: OptionTextDisplay;
|
||||||
|
|
||||||
|
/** Executes before any following dialogue or business logic from option. Usually this will be for calculating dialogueTokens or performing scene/data updates */
|
||||||
|
onPreOptionPhase?: OptionPhaseCallback;
|
||||||
|
/** Business logic function for option */
|
||||||
|
onOptionPhase: OptionPhaseCallback;
|
||||||
|
/** Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates */
|
||||||
|
onPostOptionPhase?: OptionPhaseCallback;
|
||||||
|
|
||||||
|
constructor(option: IMysteryEncounterOption | null) {
|
||||||
|
if (!isNullOrUndefined(option)) {
|
||||||
|
Object.assign(this, option);
|
||||||
|
}
|
||||||
|
this.hasDexProgress = this.hasDexProgress ?? false;
|
||||||
|
this.requirements = this.requirements ?? [];
|
||||||
|
this.primaryPokemonRequirements = this.primaryPokemonRequirements ?? [];
|
||||||
|
this.secondaryPokemonRequirements = this.secondaryPokemonRequirements ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRequirements() {
|
||||||
|
return this.requirements.length > 0 || this.primaryPokemonRequirements.length > 0 || this.secondaryPokemonRequirements.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
meetsRequirements(scene: BattleScene) {
|
||||||
|
return !this.requirements.some(requirement => !requirement.meetsRequirement(scene)) &&
|
||||||
|
this.meetsSupportingRequirementAndSupportingPokemonSelected(scene) &&
|
||||||
|
this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon) {
|
||||||
|
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getParty()).map(p => p.id).includes(pokemon.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene) {
|
||||||
|
if (!this.primaryPokemonRequirements) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let qualified: PlayerPokemon[] = scene.getParty();
|
||||||
|
for (const req of this.primaryPokemonRequirements) {
|
||||||
|
if (req.meetsRequirement(scene)) {
|
||||||
|
if (req instanceof EncounterPokemonRequirement) {
|
||||||
|
const queryParty = req.queryParty(scene.getParty());
|
||||||
|
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.primaryPokemon = undefined;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qualified.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.excludePrimaryFromSecondaryRequirements && this.secondaryPokemon) {
|
||||||
|
const truePrimaryPool: PlayerPokemon[] = [];
|
||||||
|
const overlap: PlayerPokemon[] = [];
|
||||||
|
for (const qp of qualified) {
|
||||||
|
if (!this.secondaryPokemon.includes(qp)) {
|
||||||
|
truePrimaryPool.push(qp);
|
||||||
|
} else {
|
||||||
|
overlap.push(qp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (truePrimaryPool.length > 0) {
|
||||||
|
// always choose from the non-overlapping pokemon first
|
||||||
|
this.primaryPokemon = truePrimaryPool[Utils.randSeedInt(truePrimaryPool.length, 0)];
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// if there are multiple overlapping pokemon, we're okay - just choose one and take it out of the supporting pokemon pool
|
||||||
|
if (overlap.length > 1 || (this.secondaryPokemon.length - overlap.length >= 1)) {
|
||||||
|
// is this working?
|
||||||
|
this.primaryPokemon = overlap[Utils.randSeedInt(overlap.length, 0)];
|
||||||
|
this.secondaryPokemon = this.secondaryPokemon.filter((supp) => supp !== this.primaryPokemon);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.log("Mystery Encounter Edge Case: Requirement not met due to primay pokemon overlapping with support pokemon. There's no valid primary pokemon left.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Just pick the first qualifying Pokemon
|
||||||
|
this.primaryPokemon = qualified[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meetsSupportingRequirementAndSupportingPokemonSelected(scene: BattleScene) {
|
||||||
|
if (!this.secondaryPokemonRequirements) {
|
||||||
|
this.secondaryPokemon = [];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let qualified: PlayerPokemon[] = scene.getParty();
|
||||||
|
for (const req of this.secondaryPokemonRequirements) {
|
||||||
|
if (req.meetsRequirement(scene)) {
|
||||||
|
if (req instanceof EncounterPokemonRequirement) {
|
||||||
|
const queryParty = req.queryParty(scene.getParty());
|
||||||
|
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.secondaryPokemon = [];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.secondaryPokemon = qualified;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterOption> {
|
||||||
|
optionMode: MysteryEncounterOptionMode = MysteryEncounterOptionMode.DEFAULT;
|
||||||
|
requirements: EncounterSceneRequirement[] = [];
|
||||||
|
primaryPokemonRequirements: EncounterPokemonRequirement[] = [];
|
||||||
|
secondaryPokemonRequirements: EncounterPokemonRequirement[] = [];
|
||||||
|
excludePrimaryFromSecondaryRequirements: boolean = false;
|
||||||
|
isDisabledOnRequirementsNotMet: boolean = true;
|
||||||
|
hasDexProgress: boolean = false;
|
||||||
|
dialogue?: OptionTextDisplay;
|
||||||
|
|
||||||
|
static newOptionWithMode(optionMode: MysteryEncounterOptionMode): MysteryEncounterOptionBuilder & Pick<IMysteryEncounterOption, "optionMode"> {
|
||||||
|
return Object.assign(new MysteryEncounterOptionBuilder(), { optionMode });
|
||||||
|
}
|
||||||
|
|
||||||
|
withHasDexProgress(hasDexProgress: boolean): this & Required<Pick<IMysteryEncounterOption, "hasDexProgress">> {
|
||||||
|
return Object.assign(this, { hasDexProgress: hasDexProgress });
|
||||||
|
}
|
||||||
|
|
||||||
|
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<IMysteryEncounterOption, "requirements">> {
|
||||||
|
if (requirement instanceof EncounterPokemonRequirement) {
|
||||||
|
Error("Incorrectly added pokemon requirement as scene requirement.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requirements.push(requirement);
|
||||||
|
return Object.assign(this, { requirements: this.requirements });
|
||||||
|
}
|
||||||
|
|
||||||
|
withSceneMoneyRequirement(requiredMoney?: number, scalingMultiplier?: number) {
|
||||||
|
return this.withSceneRequirement(new MoneyRequirement(requiredMoney, scalingMultiplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
withPreOptionPhase(onPreOptionPhase: OptionPhaseCallback): this & Required<Pick<IMysteryEncounterOption, "onPreOptionPhase">> {
|
||||||
|
return Object.assign(this, { onPreOptionPhase: onPreOptionPhase });
|
||||||
|
}
|
||||||
|
|
||||||
|
withOptionPhase(onOptionPhase: OptionPhaseCallback): this & Required<Pick<IMysteryEncounterOption, "onOptionPhase">> {
|
||||||
|
return Object.assign(this, { onOptionPhase: onOptionPhase });
|
||||||
|
}
|
||||||
|
|
||||||
|
withPostOptionPhase(onPostOptionPhase: OptionPhaseCallback): this & Required<Pick<IMysteryEncounterOption, "onPostOptionPhase">> {
|
||||||
|
return Object.assign(this, { onPostOptionPhase: onPostOptionPhase });
|
||||||
|
}
|
||||||
|
|
||||||
|
withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required<Pick<IMysteryEncounterOption, "primaryPokemonRequirements">> {
|
||||||
|
if (requirement instanceof EncounterSceneRequirement) {
|
||||||
|
Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.primaryPokemonRequirements.push(requirement);
|
||||||
|
return Object.assign(this, { primaryPokemonRequirements: this.primaryPokemonRequirements });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player is required to have certain type/s of pokemon in his party (with optional min number of pokemons with that type)
|
||||||
|
*
|
||||||
|
* @param type the required type/s
|
||||||
|
* @param excludeFainted whether to exclude fainted pokemon
|
||||||
|
* @param minNumberOfPokemon number of pokemons to have that type
|
||||||
|
* @param invertQuery
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withPokemonTypeRequirement(type: Type | Type[], excludeFainted?: boolean, minNumberOfPokemon?: number, invertQuery?: boolean) {
|
||||||
|
return this.withPrimaryPokemonRequirement(new TypeRequirement(type, excludeFainted, minNumberOfPokemon, invertQuery));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player is required to have a pokemon that can learn a certain move/moveset
|
||||||
|
*
|
||||||
|
* @param move the required move/moves
|
||||||
|
* @param options see {@linkcode CanLearnMoveRequirementOptions}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withPokemonCanLearnMoveRequirement(move: Moves | Moves[], options?: CanLearnMoveRequirementOptions) {
|
||||||
|
return this.withPrimaryPokemonRequirement(new CanLearnMoveRequirement(move, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = true): this & Required<Pick<IMysteryEncounterOption, "secondaryPokemonRequirements">> {
|
||||||
|
if (requirement instanceof EncounterSceneRequirement) {
|
||||||
|
Error("Incorrectly added scene requirement as pokemon requirement.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.secondaryPokemonRequirements.push(requirement);
|
||||||
|
this.excludePrimaryFromSecondaryRequirements = excludePrimaryFromSecondaryRequirements;
|
||||||
|
return Object.assign(this, { secondaryPokemonRequirements: this.secondaryPokemonRequirements });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Se the full dialogue object to the option. Will override anything already set
|
||||||
|
*
|
||||||
|
* @param dialogue see {@linkcode OptionTextDisplay}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withDialogue(dialogue: OptionTextDisplay) {
|
||||||
|
this.dialogue = dialogue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(this: IMysteryEncounterOption) {
|
||||||
|
return new MysteryEncounterOption(this);
|
||||||
|
}
|
||||||
|
}
|