Finisher timing pass: measured impact windows for all batch fighters
tools/tune_finishers.py detects each finisher's impact frame (max forward extent of the sprite) and sets real-time totals + active windows; values persist in finisher_timings.json and win over DEFAULT_FINISHER in tune. kachujin/vesper keep hand-tuned values. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
e624bec379
commit
c3d682b7dd
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 198,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 95,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
96,
|
||||||
96
|
119
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -676,11 +676,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"96-119": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 138,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 23,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
24,
|
||||||
96
|
47
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -476,11 +476,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"24-47": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 132,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 65,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
66,
|
||||||
96
|
89
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -456,11 +456,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"66-89": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 204,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 131,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
132,
|
||||||
96
|
155
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -696,11 +696,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"132-155": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 300,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 77,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
78,
|
||||||
96
|
101
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -636,11 +636,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"78-101": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 144,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 65,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
66,
|
||||||
96
|
89
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -496,11 +496,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"66-89": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 126,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 89,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
90,
|
||||||
96
|
113
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -436,11 +436,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"90-113": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 186,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 77,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
78,
|
||||||
96
|
101
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -616,11 +616,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"78-101": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 258,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 197,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
198,
|
||||||
96
|
221
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -876,11 +876,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"198-221": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 258,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 197,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
198,
|
||||||
96
|
221
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -616,11 +616,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"198-221": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 324,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 287,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
288,
|
||||||
96
|
311
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -1096,11 +1096,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"288-311": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 150,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 35,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
36,
|
||||||
96
|
59
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -516,11 +516,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"36-59": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 126,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 53,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
54,
|
||||||
96
|
77
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -436,11 +436,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"54-77": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "finisher",
|
"name": "finisher",
|
||||||
"total": 220,
|
"total": 324,
|
||||||
"loop": false,
|
"loop": false,
|
||||||
"startup": 78,
|
"startup": 179,
|
||||||
"active": [
|
"active": [
|
||||||
82,
|
180,
|
||||||
96
|
203
|
||||||
],
|
],
|
||||||
"damage": 0,
|
"damage": 0,
|
||||||
"hitstun": 0,
|
"hitstun": 0,
|
||||||
@ -1096,11 +1096,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hitboxes": {
|
"hitboxes": {
|
||||||
"82-96": [
|
"180-203": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
-320,
|
-320,
|
||||||
170,
|
180,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
324
tools/finisher_timings.json
Normal file
324
tools/finisher_timings.json
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
{
|
||||||
|
"birdie": {
|
||||||
|
"total": 198,
|
||||||
|
"startup": 95,
|
||||||
|
"active": [
|
||||||
|
96,
|
||||||
|
119
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"96-119": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chef": {
|
||||||
|
"total": 138,
|
||||||
|
"startup": 23,
|
||||||
|
"active": [
|
||||||
|
24,
|
||||||
|
47
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"24-47": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"decks": {
|
||||||
|
"total": 132,
|
||||||
|
"startup": 65,
|
||||||
|
"active": [
|
||||||
|
66,
|
||||||
|
89
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"66-89": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dread": {
|
||||||
|
"total": 204,
|
||||||
|
"startup": 131,
|
||||||
|
"active": [
|
||||||
|
132,
|
||||||
|
155
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"132-155": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"elva": {
|
||||||
|
"total": 300,
|
||||||
|
"startup": 77,
|
||||||
|
"active": [
|
||||||
|
78,
|
||||||
|
101
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"78-101": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exec": {
|
||||||
|
"total": 144,
|
||||||
|
"startup": 65,
|
||||||
|
"active": [
|
||||||
|
66,
|
||||||
|
89
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"66-89": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jima": {
|
||||||
|
"total": 126,
|
||||||
|
"startup": 89,
|
||||||
|
"active": [
|
||||||
|
90,
|
||||||
|
113
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"90-113": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jinx": {
|
||||||
|
"total": 186,
|
||||||
|
"startup": 77,
|
||||||
|
"active": [
|
||||||
|
78,
|
||||||
|
101
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"78-101": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nappo": {
|
||||||
|
"total": 258,
|
||||||
|
"startup": 197,
|
||||||
|
"active": [
|
||||||
|
198,
|
||||||
|
221
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"198-221": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"raver": {
|
||||||
|
"total": 258,
|
||||||
|
"startup": 197,
|
||||||
|
"active": [
|
||||||
|
198,
|
||||||
|
221
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"198-221": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roca": {
|
||||||
|
"total": 324,
|
||||||
|
"startup": 287,
|
||||||
|
"active": [
|
||||||
|
288,
|
||||||
|
311
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"288-311": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"umbra": {
|
||||||
|
"total": 150,
|
||||||
|
"startup": 35,
|
||||||
|
"active": [
|
||||||
|
36,
|
||||||
|
59
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"36-59": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"violet": {
|
||||||
|
"total": 126,
|
||||||
|
"startup": 53,
|
||||||
|
"active": [
|
||||||
|
54,
|
||||||
|
77
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"54-77": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"widow": {
|
||||||
|
"total": 324,
|
||||||
|
"startup": 179,
|
||||||
|
"active": [
|
||||||
|
180,
|
||||||
|
203
|
||||||
|
],
|
||||||
|
"damage": 0,
|
||||||
|
"finisher": true,
|
||||||
|
"hitstun": 0,
|
||||||
|
"blockstun": 0,
|
||||||
|
"pushback": 0,
|
||||||
|
"hitboxes": {
|
||||||
|
"180-203": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-320,
|
||||||
|
180,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -130,6 +130,9 @@ MANIFESTS = {
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
chars = sys.argv[1:] or list(MANIFESTS)
|
chars = sys.argv[1:] or list(MANIFESTS)
|
||||||
|
# measured impact windows (tools/tune_finishers.py) beat DEFAULT_FINISHER
|
||||||
|
timings_path = REPO / "tools/finisher_timings.json"
|
||||||
|
timings = json.loads(timings_path.read_text()) if timings_path.exists() else {}
|
||||||
for cid in chars:
|
for cid in chars:
|
||||||
root = REPO / "characters" / cid
|
root = REPO / "characters" / cid
|
||||||
if not root.exists():
|
if not root.exists():
|
||||||
@ -137,6 +140,8 @@ def main() -> None:
|
|||||||
continue
|
continue
|
||||||
merged = dict(TUNE)
|
merged = dict(TUNE)
|
||||||
merged.update(CHAR_TUNE.get(cid, {}))
|
merged.update(CHAR_TUNE.get(cid, {}))
|
||||||
|
if cid in timings:
|
||||||
|
merged["finisher"] = timings[cid]
|
||||||
for move, overrides in merged.items():
|
for move, overrides in merged.items():
|
||||||
data_path = root / "moves" / move / "data.json"
|
data_path = root / "moves" / move / "data.json"
|
||||||
if not data_path.exists():
|
if not data_path.exists():
|
||||||
|
|||||||
67
tools/tune_finishers.py
Normal file
67
tools/tune_finishers.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Per-fighter finisher timing from the actual frames.
|
||||||
|
|
||||||
|
The impact moment of a strike is the frame of maximum forward extent
|
||||||
|
(rightmost opaque pixel, fighters render facing right). Sets:
|
||||||
|
total = frames * 6 ticks (batch renders sample at 10fps -> real-time)
|
||||||
|
active = impact frame .. +3 frames, startup just before
|
||||||
|
Writes tools/finisher_timings.json (merged by tune_fighters.py) and
|
||||||
|
patches each characters/<id>/moves/finisher/data.json in place.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
REPO = Path(__file__).resolve().parent.parent
|
||||||
|
OUT = REPO / "tools/finisher_timings.json"
|
||||||
|
|
||||||
|
|
||||||
|
def impact_frame(frames_dir: Path):
|
||||||
|
frames = sorted(frames_dir.glob("*.png"))
|
||||||
|
ext = []
|
||||||
|
for f in frames:
|
||||||
|
a = np.array(Image.open(f).split()[-1])
|
||||||
|
cols = np.where(a.max(axis=0) > 32)[0]
|
||||||
|
ext.append(cols[-1] if len(cols) else 0)
|
||||||
|
if not ext:
|
||||||
|
return None
|
||||||
|
lo = max(int(len(ext) * 0.2), 1) # skip windup extension
|
||||||
|
i = lo + int(np.argmax(ext[lo:]))
|
||||||
|
return i, len(frames)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
chars = sys.argv[1:] or [d.name for d in (REPO / "characters").iterdir()
|
||||||
|
if not d.name.startswith("_")]
|
||||||
|
timings = json.loads(OUT.read_text()) if OUT.exists() else {}
|
||||||
|
for cid in sorted(chars):
|
||||||
|
fdir = REPO / "characters" / cid / "moves/finisher/frames"
|
||||||
|
if not fdir.exists():
|
||||||
|
continue
|
||||||
|
res = impact_frame(fdir)
|
||||||
|
if res is None:
|
||||||
|
continue
|
||||||
|
i, n = res
|
||||||
|
tpf = 6 # ticks per art frame
|
||||||
|
total = n * tpf
|
||||||
|
a0, a1 = i * tpf, min((i + 4) * tpf - 1, total - 2)
|
||||||
|
t = dict(total=total, startup=max(a0 - 1, 1), active=[a0, a1], damage=0,
|
||||||
|
finisher=True, hitstun=0, blockstun=0, pushback=0,
|
||||||
|
hitboxes={f"{a0}-{a1}": [[0, -320, 180, 300]]})
|
||||||
|
timings[cid] = t
|
||||||
|
data_path = fdir.parent / "data.json"
|
||||||
|
data = json.loads(data_path.read_text())
|
||||||
|
hurt = data.get("hurtboxes")
|
||||||
|
data.update(t)
|
||||||
|
if hurt:
|
||||||
|
data["hurtboxes"] = hurt
|
||||||
|
data_path.write_text(json.dumps(data, indent=2))
|
||||||
|
print(f"{cid}: impact frame {i}/{n} -> active ticks {a0}-{a1}, total {total}")
|
||||||
|
OUT.write_text(json.dumps(timings, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user