-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathframedata.lua
More file actions
357 lines (324 loc) · 12.7 KB
/
framedata.lua
File metadata and controls
357 lines (324 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
print("Frame data collector script")
print("July 5, 2011")
print("http://code.google.com/p/mame-rr/")
print()
print("> 'startup' is the period before the 1st active frame")
print("> hitfreeze '*' means the attacker did not get frozen")
print("Lua hotkey 1: insert blank line")
local print_header = function()
print("startup\tatkrecov.\thitstun\tfr.adv.\thitfreeze")
end
local print_results = function(c)
print(string.format("%d\t%d\t%d\t%+d\t%d%s\t%s",
c.startup, c.atkrecov, c.hitstun, c.advantage, c.hitfreeze, c.non_projectile, c.freeze_details))
end
local profile = {
{
games = {"sf2"}, class = "sf2",
address = {0xFF83C6, 0xFF86C6, projectile_slowdown = 0xFF82E2},
},
{
games = {"sf2ce", "sf2hf"}, class = "sf2",
address = {0xFF83BE, 0xFF86BE, projectile_slowdown = 0xFF82E2},
no_frameskip = function() memory.writebyte(0xFF02BE, 0x00) end,
},
{
games = {"ssf2t"}, class = "sf2",
address = {0xFF844E, 0xFF884E, projectile_slowdown = 0xFF82F2},
no_frameskip = function() memory.writebyte(0xFF8CD3, 0xFF) end,
superfreeze = function(addr) return memory.readbyte(addr + 0x1FA) == 0x01 end,
},
{
games = {"ssf2"}, class = "sf2",
address = {0xFF83CE, 0xFF87CE, projectile_slowdown = 0xFF82F2},
},
{
games = {"hsf2"}, class = "sf2",
address = {0xFF833C, 0xFF873C, projectile_slowdown = 0xFF8C29},
superfreeze = function(addr) return memory.readbyte(addr + 0x1FA) == 0x01 end,
},
{
games = {"sfa"}, class = "sfa",
hitfreeze = function(addr) return memory.readbyte(addr + 0x04F) ~= 0x00 end,
superfreeze = function(addr) return memory.readbyte(0xFFAE84) ~= 0x00 end,
delay = {startup = 0, atk_recover = 1, hit_recover = 0, prefreeze = 0, superfreeze = 10, postfreeze = {["*"] = 4, [""] = -4}},
},
{
games = {"sfa2", "sfz2al"}, class = "sfa",
hitfreeze = function(addr) return memory.readbyte(addr + 0x05F) ~= 0x00 end,
superfreeze = function(addr) return memory.readbyte(0xFF8125) ~= 0x00 end,
delay = {startup = -1, atk_recover = 1, hit_recover = 0, prefreeze = 0, superfreeze = 10, postfreeze = {["*"] = 4, [""] = -4}},
},
{
games = {"sfa3"},
address = {0xFF8400, 0xFF8800},
attacking = function(addr)
return (
memory.readbyte(addr + 0x0A9) == 0x01 or --normal attack
memory.readbyte(addr + 0x0CE) == 0x01 or --special
memory.readbyte(addr + 0x005) == 0x04 --normal throw
)
end,
supering = function(addr) return memory.readbyte(addr + 0x216) == 0x01 end,
hurt = function(addr) return memory.readbyte(addr + 0x005) == 0x02 end,
thrown = function(addr) return memory.readbyte(addr + 0x005) == 0x06 end,
hitfreeze = function(addr) return memory.readbyte(addr + 0x05F) ~= 0x00 end,
superfreeze = function(addr) return memory.readbyte(0xFF8125) ~= 0x00 end,
delay = {startup = -1, atk_recover = 1, hit_recover = 0, prefreeze = 0, superfreeze = 0, postfreeze = {["*"] = -4, [""] = -4}},
},
{
games = {"dstlk"}, class = "dstlk",
address = {0xFF8388, 0xFF8788},
},
{
games = {"nwarr"}, class = "dstlk",
address = {0xFF8388, 0xFF8888},
},
{
games = {"vsav","vhunt2","vsav2"},
address = {0xFF8400, 0xFF8800},
attacking = function(addr) return memory.readbyte(addr + 0x105) == 0x01 end,
supering = function(addr) return memory.readbyte(addr + 0x006) == 0x12 end,
hurt = function(addr) return memory.readbyte(addr + 0x005) == 0x02 end,
thrown = function(addr) return memory.readbyte(addr + 0x005) == 0x06 end,
hitfreeze = function(addr) return memory.readbyte(addr + 0x05C) ~= 0x00 end,
delay = {startup = -1, atk_recover = 1, hit_recover = 1},
},
{
games = {"sfiii"}, class = "sf3",
address = {0x0200D18C, 0x0200D564},
super_frozen = 0x390,
super_shadows = 0x398,
},
{
games = {"sfiii2"}, class = "sf3",
address = {0x0200E504, 0x0200E910},
super_frozen = 0x3B4,
super_shadows = 0x3BC,
},
{
games = {"sfiii3"}, class = "sf3",
address = {0x02068C6C, 0x02069104},
super_frozen = 0x41C,
super_shadows = 0x424,
},
}
local fill_out = {
["sf2"] = function(game)
game.attacking = function(addr) return memory.readbyte(addr + 0x18B) == 0x01 end
game.hurt = function(addr) return memory.readbyte(addr + 0x003) >= 0x0E end
game.thrown = function(addr) return memory.readbyte(addr + 0x063) == 0xFF end
game.hitfreeze = function(addr) return memory.readbyte(addr + 0x047) ~= 0x00 end
game.delay = {startup = -1, atk_recover = 0, hit_recover = 0,
prefreeze = 0, superfreeze = -1, postfreeze = {["*"] = 0, [""] = 0}}
end,
["sfa"] = function(game)
game.address = {0xFF8400, 0xFF8800}
game.attacking = function(addr)
return (
memory.readbyte(addr + 0x132) == 0x01 or --normal attack
memory.readbyte(addr + 0x006) == 0x0E or --special
memory.readbyte(addr + 0x005) == 0x04 --normal throw
)
end
game.supering = function(addr) return memory.readbyte(addr + 0x006) == 0x10 end
game.hurt = function(addr) return memory.readbyte(addr + 0x005) == 0x02 end
game.thrown = function(addr) return memory.readbyte(addr + 0x005) == 0x06 end
end,
["dstlk"] = function(game)
game.attacking = function(addr)
return (
memory.readbyte(addr + 0x005) == 0x02 or --normal attack
memory.readbyte(addr + 0x004) == 0x10 or --special
memory.readbyte(addr + 0x004) == 0x0E or --throw
memory.readbyte(addr + 0x088) == 0x01 --special recovery
)
end
game.supering = function(addr)
return (
memory.readbyte(addr + 0x004) == 0x10 or --special
memory.readbyte(addr + 0x004) == 0x0E or --throw
memory.readbyte(addr + 0x088) == 0x01 --special recovery
)
end
game.hurt = function(addr) return memory.readbyte(addr + 0x004) == 0x0C end
game.thrown = function(addr) return memory.readbyte(addr + 0x004) == 0x12 end
game.hitfreeze = function(addr) return memory.readbyte(addr + 0x04B) ~= 0x00 end
game.delay = {startup = 0, atk_recover = 1, hit_recover = 1}
game.update = {func = emu.registerbefore, cycle = 4}
end,
["sf3"] = function(game)
game.attacking = function(addr)
return memory.readword(addr + 0x026) == 0x0002 or memory.readword(addr + 0x026) == 0x0004
end
game.supering = function(addr) return memory.readword(addr + game.super_shadows) == 0x0001 end
game.hurt = function(addr) return memory.readword(addr + 0x026) == 0x0001 end
game.thrown = function(addr) return memory.readword(addr + 0x026) == 0x0003 end
game.hitfreeze = function(addr, opp_addr)
return memory.readword(addr + 0x044) ~= 0x0000 and
(memory.readbyte(addr + game.super_frozen) == 0x00 and memory.readbyte(opp_addr + game.super_frozen) == 0x00)
end
game.superfreeze = function(addr, opp_addr)
return memory.readword(addr + 0x044) ~= 0x0000 and
(memory.readbyte(addr + game.super_frozen) == 0x01 or memory.readbyte(opp_addr + game.super_frozen) == 0x01)
end
game.delay = {startup = 0, atk_recover = 0, hit_recover = 0,
prefreeze = 0, superfreeze = 1, postfreeze = {["*"] = -4, [""] = -3}}
end,
}
for _, game in ipairs(profile) do
game.update = game.update or {func = emu.registerafter, cycle = 1}
if game.class then
fill_out[game.class](game)
end
end
--------------------------------------------------------------------------------
local game, player_old, count, register_count, last_frame
local super_mode = false
input.registerhotkey(1, function()
print()
end)
input.registerhotkey(2, function()
if not game.supering then
return
end
super_mode = not super_mode
print() print("now tracking: " .. (super_mode and "super moves only" or "all moves"))
print_header()
end)
local function initialize_count()
count = {active = true, total = 0, total_superfreeze = 0, hitfreeze = 0, non_projectile = "*", freeze_details = ""}
end
local function initialize()
player_old = {{}, {}}
register_count, last_frame = 0, 0
initialize_count()
end
local get_attack_state = {
[false] = function(addr) --non-super mode
return game.attacking(addr)
end,
[true] = function(addr) --super mode
return game.supering(addr)
end,
}
local function update_frame_data()
if game.address.projectile_slowdown and
memory.readbyte(game.address[1] + 0x02) ~= 0x04 and memory.readbyte(game.address[2] + 0x02) ~= 0x04 then
memory.writebyte(game.address.projectile_slowdown, 0) --disable projectile slowdown
end
if game.no_frameskip then
game.no_frameskip() --disable frameskip
end
local player = {{}, {}}
for p = 1, 2 do --get the current status of the players from RAM
local addr = game.address[p]
local opp_addr = (p == 1 and game.address[2]) or game.address[1]
player[p].attacking = get_attack_state[super_mode](addr)
player[p].hurt = game.hurt(addr)
player[p].thrown = game.thrown(addr)
player[p].hitfreeze = game.hitfreeze(addr, opp_addr)
player[p].superfreeze = game.superfreeze and game.superfreeze(addr, opp_addr)
end
if not count.active and player[1].attacking and not player_old[1].attacking then --check for start of the attack
initialize_count()
end
if count.active and not count.startup then --check for hit
if player[1].superfreeze and not player_old[1].superfreeze and not count.prefreeze then --superfreeze just started
count.prefreeze = count.total + game.delay.prefreeze
elseif player_old[1].superfreeze and not player[1].superfreeze then --superfreeze just ended
count.superfreeze = count.total - count.prefreeze
end
if player[2].hurt and not player_old[2].hurt then --attack hit
count.startup = count.total + game.delay.startup
elseif player[2].thrown and not player_old[2].thrown then --throw grabbed
count.startup = count.total
elseif not player[1].attacking then --attack whiffed; stop counting
count.active = false
end
end
if count.startup and not count.atkrecov then
if player_old[1].hurt and not player[1].hurt then --check if the attacker got hit/traded
count.atkrecov = count.total - count.hitfreeze - count.startup + game.delay.hit_recover
elseif player_old[1].attacking and not (player[1].attacking or player[1].hurt) then --check for attacker recovery
count.atkrecov = count.total - count.hitfreeze - count.startup + game.delay.atk_recover
end
end
if count.startup and --check for dummy recovery
(player_old[2].hurt or player_old[2].thrown) and not (player[2].hurt or player[2].thrown) then
count.hitstun = count.total - count.hitfreeze - count.startup + game.delay.hit_recover
end
if count.active and count.atkrecov and count.hitstun then --print results and stop counting
count.advantage = count.hitstun - count.atkrecov
if not count.non_projectile then --if it was a projectile...
count.atkrecov = count.atkrecov + count.hitfreeze --...then p1 didn't have any hitfreeze so add it back
end
if count.prefreeze then
count.superfreeze = count.superfreeze + game.delay.superfreeze
count.postfreeze = count.startup - count.superfreeze + game.delay.postfreeze[count.non_projectile]
count.startup = count.prefreeze + count.postfreeze
count.freeze_details = count.prefreeze .. " (" .. count.superfreeze .. ") " .. count.postfreeze
end
print_results(count)
count.active = false
end
player_old = player
if (player[1].superfreeze or player[2].superfreeze) then --check for superfreeze
count.total_superfreeze = count.total_superfreeze + 1
end
if player[1].hitfreeze or player[2].hitfreeze then --check for hitfreeze
count.hitfreeze = count.hitfreeze + 1
end
if player[1].hitfreeze then
count.non_projectile = "" --only a non-projectile would freeze p1 (remove the "*")
end
count.total = count.total + 1
if count.active then --display count if counting
emu.message(count.total .. " (" .. count.total_superfreeze .. ")" .. " [" .. count.hitfreeze .. "]")
end
end
savestate.registerload(function() --prevent strange behavior after loading
initialize()
end)
emu.registerstart( function()
game = nil
emu.registerbefore(function() end)
emu.registerafter(function() end)
super_mode = false
initialize()
print()
for n, module in ipairs(profile) do
for m, shortname in ipairs(module.games) do
if emu.romname() == shortname or emu.parentname() == shortname then
game = module
print("tracking " .. shortname .. " frame data")
if fba and (emu.sourcename() == "CPS1" or emu.sourcename() == "CPS2") then
print("Warning: FBA gives inaccurate results for CPS1/CPS2.")
end
if game.supering then
print("Lua hotkey 2: toggle normal/super-only mode")
end
if game.no_frameskip then
print("* disabling frameskip")
end
if game.address.projectile_slowdown then
print("* disabling projectile slowdown")
end
print()
print_header()
game.update.func(function()
register_count = register_count + 1
if register_count == game.update.cycle then
update_frame_data()
end
if last_frame < emu.framecount() then
register_count = 0
end
last_frame = emu.framecount()
end)
return
end
end
end
print("not prepared for " .. emu.romname() .. " frame data")
end)