--[[ Auto-pilot script for Star Force. This script works on "MAME Rerecording v0.139" Copyright 2010 Kanobashi -------- BEGIN GPL LICENSE BLOCK -------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -------- END GPL LICENSE BLOCK]] v0.139 ]] local en = {} local fs = {dx = 256, dy = 256} -- x=7800, y=d000 local rd = {} local frameUnit = 3 local r = {} local g = {ts = 0, tx = 0x3000, ty = 0xc000, laStat = 0, ecount = {}, fireTimer = 255, fireCycle = 1, weaponInfo = {-1, -1, -1}, weaponOrder = 0} local edef = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 1}, {1, 1}, {0, 1}, {0, 0}, {0, 1}, {1, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 1}, {1, 1}, {1, 1}, {0, 0}, {1, 1}, {1, 1} } --print(edef[1][1]) local logFp if false then logFp = io.open("sflog.bin", "wb") end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function getBgAddr(x, y) local baseY = (memory.readbyte(0x8163) + math.floor(y / 256)) % 256 if x == -1 then return 0xb060 + math.floor(baseY / 16) -- max: b19x else local baseX = memory.readword(0x815f) + 248 - math.floor(x / 256) baseX = 0xb000 + baseX - (baseX % 16) return baseX + math.floor(baseY / 16) end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function detectKer() if not(g.mgk) then return end local num = memory.readbyte(0x8566) local baseAddr = 0x856c for i = 1, num do while memory.readbyte(baseAddr) == 0 do baseAddr = baseAddr + 6 if baseAddr > 0x8600 then return end end local bgAddr = memory.readword(baseAddr + 1) local bgCode = memory.readbyte(bgAddr) --underconstruct if bgCode == 0xae then g.mgk = false end end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function findMagikka() local bgAddr = getBgAddr(-1, 4096) gui.text(8, 200, string.format("%X", bgAddr)) if g.bgAddr == bgAddr then return end g.bgAddr = bgAddr for i = 0, 0x13 do --memory.writebyte(bgAddr, 0xa0) bgChar = memory.readbyte(bgAddr) --underconstruct if math.floor(bgChar / 16) == 10 then --print("ker") g.bgOffset = memory.readbyte(0x8163) g.mgk = true break end bgAddr = bgAddr + 0x10 end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function getLaStatus() --gui.text(24, 24, string.format("%d %d", en[15].typ, en[7].typ)) if en[15].typ2 == 0x11 then return 1 elseif en[15].typ2 == 0x31 and en[7].typ == -1 then return 2 else return 0 end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function evalPosition(frame) local ef = {} -- for i = 1, 15 do if en[i].typ ~= -1 then local p = {typ = en[i].typ, x = en[i].x + en[i].vx * frame, y = en[i].y + en[i].vy * frame, hasBullet = en[i].hasBullet} table.insert(ef, p) if frame == 4 then gui.text(p.y / 256, 232 - p.x / 256, string.format("%d", en[i].typ2)) end if en[i].typ <= 0x15 and edef[en[i].typ+1][1] == 0 and frame == 4 then local p1 = {typ = 30, x = p.x + en[i].vx, y = p.y + en[i].vy, hasBullet = en[i].hasBullet} ----local zx, zy = fs.x - p.x, fs.y - p.y ----local m = 16 * 256 / math.sqrt(zx * zx + zy * zy) ----local p1 = {typ = 30, x = p.x + m * zx, y = p.y + m * zy} --table.insert(ef, p1) end --gui.text(i * 12, 128, en[i].hasBullet and "x" or "_") --gui.text(i * 12, 132, string.format("%X", en[i].typ)) end end r = {} for i = 1, (frame * 2 + 1) ^ 2 do r[i] = 0 end local mx = fs.dx * frame local my = fs.dy * frame local bx = fs.x - mx local ex = fs.x + mx local by = fs.y - my local ey = fs.y + my local bxi, byi = -frame, -frame while (bx < 0x1800) do bx = bx + fs.dx bxi = bxi + 1 end while (by < 0x4800) do by = by + fs.dy byi = byi + 1 --print(fs.dy) end while (ex >= 0xd800) do ex = ex - fs.dx end while (ey >= 0xe800) do ey = ey - fs.dy end local y = byi + frame for iy = by, ey, fs.dy do local x = bxi + frame for ix = bx, ex, fs.dx do local minD = 0x3fff for i, e in ipairs(ef) do local d = math.max(math.abs(ix - e.x), math.abs(iy - e.y)) if e.typ == 30 then if d >= 0x1fff then d = 0x3fff end elseif d < 8 * 256 then d = 4 * 256 else -- d = math.sqrt((ix - e.x) ^ 2 + (iy - e.y) ^ 2) local dlim = 0x3fff if e.typ == 31 then if d >= 0x1000 then d = dlim end --[[elseif frame >= 2 then d = math.sqrt((ix - e.x) ^ 2 + (iy - e.y) ^ 2) if d >= dlim then d = dlim end]] elseif e.hasBullet then -- if e.typ <= 0x15 and edef[e.typ+1][2] == 0 then if d >= 0x1fff then d = dlim end -- else -- if d >= 0x1000 then d = dlim end -- end else if d >= 0x1000 then d = dlim end end end if minD > d then minD = d end end local idx = y * (frame * 2 + 1) + x + 1 r[idx] = math.floor(minD / 256) -- 0 .. 3f (6bit) x = x + 1 end y = y + 1 end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function evalVector() r = {0, 0, 0, 0, 0, 0, 0, 0, 0} local r2th = (24 * 256) ^ 2 local maxSum = 0 j = 1 for y = -1, 1 do for x = -1, 1 do local nx, ny = fs.x + x * fs.dx, fs.y + y * fs.dy local ipsum = 0 for i = 1, 15 do if en[i].typ ~= -1 then local rx, ry = nx - en[i].x, ny - en[i].y r2 = rx * rx + ry * ry if r2 < r2th then -- local ip = (rx * en[i].vx + ry * en[i].vy) / math.sqrt(en[i].vx * en[i].vx + en[i].vy * en[i].vy) / r2 local ip = (rx * en[i].vx + ry * en[i].vy) / r2 if ip > 0 then ipsum = ipsum + ip end end end end r[j] = ipsum if maxSum < ipsum then --print(ipsum) maxSum = ipsum end j = j + 1 end end if maxSum == 0 then return end for j = 1, 9 do r[j] = 255 - math.floor(r[j] * 255 / maxSum) end --gui.text(0, 64, string.format("%d %d %d %d %d %d %d %d %d ", r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9])) end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function evalTarget() r = {} for i = 1, 9 do r[i] = 255 end local rl, rr = 0x3000, 0xc000 if g.mgk then g.ty = (256 + g.bgOffset - memory.readbyte(0x8163)) % 256 g.ty = g.ty * 256 if g.ty >= 0xd800 then g.mgk = false end rl, rr = 0x2000, 0x7000 end if (g.ty - 0x9800) * (fs.y - 0x9800) > 0 or g.mgk then if g.tx < (rl + rr) / 2 then if fs.x <= (g.tx + 256*frameUnit) then g.tx = rr end else if fs.x >= (g.tx - 256*frameUnit) then g.tx = rl end end end if g.ty < 0x9800 then if g.ecount[3] + g.ecount[4] < 7 then g.ty = 0xc000 end end --[[ if g.ty < 0x9800 then if fs.y <= (g.ty + 256*frameUnit) and g.obTop < 0x7000 then g.ty = 0xc000 end else if g.obBottom >= 0xc000 then g.ty = 0x7000 end end ]] if g.obTop < 0x7000 then g.obw = false end if g.obBottom >= 0xC000 then g.obw = true end local targetx, targety = g.tx, g.ty if g.obw then targety = 0x7000 elseif g.pidx > 0 then -- par targetx, targety = en[g.pidx].x, en[g.pidx].y elseif (en[1].typ % 32) == 24 then -- fin tar targetx, targety = en[1].x + en[1].vx * 4, 0x8000 elseif g.laStat == 1 then targetx, targety = 0x6e00, 0x7000 elseif g.laStat == 2 then targetx, targety = 0x7800, 0x5800 --elseif fs.x >= 0xb800 or fs.x <= 0x3800 or fs.y >= 0xc800 or fs.y < 0x6800 then else local t = ((fs.y > 0x9800) and 2 or 0) + ((fs.x > 0x7800) and 2 or 1) if g.ecount[t] >= 4 or g.mcount[math.floor((t + 1) / 2)] >= 3 then local x, y = fs.x - 0x7800, fs.y - 0x9800 local t = math.abs(y) > math.abs(x) local t2 = t if y > 0 then t = not(t) end g.ty = t and 0x7000 or 0xc000 if x > 0 then t2 = not(t2) end g.tx = t2 and 0xc000 or 0x3000 end end gui.text(targety / 256, 232 - targetx / 256, "x") gui.text(g.ty / 256, 232 - g.tx / 256, "g") -- x frame? local bx = fs.x - fs.dx local ex = fs.x + fs.dx local by = fs.y - fs.dy local ey = fs.y + fs.dy local y = 0 for iy = by, ey, fs.dy do local x = 0 for ix = bx, ex, fs.dx do local d = math.sqrt((ix - targetx) ^ 2 + (iy - targety) ^ 2) if d > 65535 then d = 65535 end local idx = y * 3 + x + 1 r[idx] = 255 - math.floor(d / 256) -- 0 .. 255 x = x + 1 end y = y + 1 end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function evalFire() local svx = (math.floor(memory.readbyte(0x8547) / 128) ~= 0) and 0x800 or 0x600 g.fireCycle = math.floor((fs.y - 0x1000) / svx / 3) -- g.snum1 = 0 local i = 1 for ad = 0x839d, 0x83c5, 20 do if (memory.readbyte(ad) % 2) ~= 0 then if g.weaponInfo[i] == -1 then -- new weapon g.weaponOrder = (g.weaponOrder + 1) % 3 g.weaponInfo[i] = g.weaponOrder end elseif g.weaponInfo[i] ~= -1 then g.weaponInfo[i] = -1 if ((g.weaponInfo[i] + 2) % 3) ~= g.weaponOrder then g.reqFire = true end end i = i + 1 end if g.laStat == 1 and math.abs(0x7800 - fs.x) < 0x1100 then g.inhFire = true elseif g.laStat == 2 then g.reqFire = true end -- g.snum = g.snum1 end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function copyEvalResult(frame, weight) local fm = frame * 2 + 1 for y = -1, 1 do for x = -1, 1 do local maxD = 0 for sy = y - 1, y + 1 do for sx = x - 1, x + 1 do --print((sy + frame) * fm + (sx + frame) + 1) d = r[(sy + frame) * fm + (sx + frame) + 1] if d > maxD then maxD = d end end end local ri = (y + 1) * 3 + x + 2 rd[ri] = rd[ri] + maxD * weight end end end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] local function extractEnemy() g.pidx = 0 g.ecount = {0, 0, 0, 0} -- TopLeft, TRight, BottomL, BR g.mcount = {0, 0} -- extract en info --local b = memory.readbyterange(0x8401, 15*20) local b = {} for i = 1, 15*20 do b[i] = memory.readbyte(0x8401 + i - 1) if logFp then logFp:write(string.char(b[i])) end end local j = 1 g.obBottom = 0 g.obTop = 0xffff for i = 1, 14*20 + 1, 20 do local e = {typ = -1, x = 0, y = 0, vx = 0, vy = 0, hasBullet = false, typ2 = -1} -- visible, collision enabled if (b[i] % 2) ~= 0 and (math.floor(b[i] / 64) % 2) == 0 then e.x = b[i + 4] * 256 + b[i + 6] e.y = b[i + 5] * 256 + b[i + 9] e.vx = (b[i + 8] * 256 + b[i + 7]) e.vy = (b[i + 11] * 256 + b[i + 10]) if e.vx >= 32768 then e.vx = e.vx - 65536 end if e.vy >= 32768 then e.vy = e.vy - 65536 end e.vx = e.vx * frameUnit e.vy = e.vy * frameUnit e.hasBullet = math.floor((b[i] % 16) / 8) == 0 and b[i + 17] > 0 if (b[i + 1] % 32) == 0x15 and math.floor(b[i + 1] / 128) ~= 0 then -- parser? g.pidx = j else if (math.floor(b[i] / 32) % 2) ~= 0 then --bullet e.typ = 31 else e.typ = b[i + 1] % 32 e.typ2 = b[i + 1] if e.typ == 20 then if g.obBottom < e.y then g.obBottom = e.y end if g.obTop > e.y then g.obTop = e.y end local t = fs.y - e.y if math.abs(e.x - fs.x) < 20 * 256 and -9 < t and t < 32 * 256 then g.inhFire = true end elseif e.typ == 5 then local t = ((e.y > 0x9800) and 2 or 1) g.mcount[t] = g.mcount[t] + 1 end if math.abs(e.x - fs.x) < 0x1000 and math.abs(e.y - fs.y) < 0x1000 then g.inhFire = false g.reqFire = true elseif not(g.inhFire) and math.abs(e.x - fs.x) < 12 * 256 then local t = fs.y - e.y if t < 40*256 and 0 < t then g.reqFire = true end end end local t = ((e.y > 0x9800) and 2 or 0) + ((e.x > 0x7800) and 2 or 1) g.ecount[t] = g.ecount[t] + 1 end if e.typ2 == 4 or e.typ2 == 5 or e.typ2 == 7 or e.typ2 == 10 or e.typ2 == 14 then e.vx = 0 elseif e.typ2 == 133 or e.typ2 == 141 then e.vy = 0 end end en[j] = e j = j + 1 end if g.obTop == 0xffff then g.obTop = 0 end if g.obBottom == 0 then g.obBotton = 0xffff end --print(en[1]) --gui.text(64, 64, string.format("%d", b[1])) --gui.text(16, 16, string.format("%d %d %d %d %d", en[1].typ, en[1].x/256, en[1].y/256, en[1].vx, en[1].vy)) gui.text(16, 16, string.format("%d %d %d %d", g.ecount[1], g.ecount[2], g.ecount[3], g.ecount[4])) end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] function onUpdateFrame() g.inhFire = false g.reqFire = false -- extract ship info --local b = memory.readbyterange(0x83ed, 14) local b = {} for i = 1, 14 do b[i] = memory.readbyte(0x83ed + i - 1) end --print(b) fs.x = b[5] * 256 + b[8] * 64 / 15 -- 1800..d800 fs.y = b[6] * 256 + b[7] * 64 / 15 -- 4800..e800 fs.dx = (b[10] * 64 / 15 + 128) * frameUnit fs.dy = (b[9] * 64 / 15) * frameUnit -- print(fs.x, fs.y) --gui.text(32, 32, string.format("%X %X\n", fs.x, fs.y)) if fs.dy == 0 then return end findMagikka() detectKer() extractEnemy() g.laStat = getLaStatus() --|31 |23 |15 |7 0| --1111 1122 2222 3333 3344 4444 TTTT TTTT rd = {} evalPosition(1) for i = 1, 9 do rd[i] = r[i] * 67108864 -- 2^26 end --gui.text(64, 64, string.format("%d %d %d %d %d %d %d %d %d", rd[1], rd[2], rd[3], rd[4], rd[5], rd[6], rd[7], rd[8], rd[9])) evalPosition(2) copyEvalResult(2, 1048576) -- 2^20 evalPosition(3) copyEvalResult(3, 16384) -- 2^14 evalPosition(4) copyEvalResult(4, 256) -- 2^8 --[[ Null effect evalVector() for i = 1, 9 do rd[i] = rd[i] + r[i] * 256 end ]] evalTarget() for i = 1, 9 do rd[i] = rd[i] + r[i] end --print(rd) local maxD = 0 local maxI = 0 for i = 1, 9 do if rd[i] > maxD then maxD = rd[i] maxI = i end end maxI = maxI - 1 maxIY = math.floor(maxI / 3) maxIX = maxI % 3 local jp = joypad.get() --print(jp) jp["P1 Up"] = (maxIY == 0) jp["P1 Down"] = (maxIY == 2) jp["P1 Left"] = (maxIX == 0) jp["P1 Right"] = (maxIX == 2) evalFire() --gui.text(48, 48, string.format("%d %d %d %d", g.fireTimer, g.fireCycle, g.inhFire and 1 or 0, g.reqFire and 1 or 0)) --gui.text(48, 48, string.format("%d %d %d", g.weaponInfo[1], g.weaponInfo[2], g.weaponInfo[3])) g.fireTimer = g.fireTimer + 1 local btn = false if (g.fireTimer >= g.fireCycle and not(g.inhFire)) or (g.fireTimer > 1 and g.reqFire) then --print(1) if g.weaponInfo[1] == -1 or g.weaponInfo[2] == -1 or g.weaponInfo[3] == -1 then btn = true g.fireTimer = 0 end end jp["P1 Button 1"] = btn --[[ local bcoin, bstart = false, false local kbd = input.get() if kbd["capslock"] ~= nil then if math.floor(memory.readbyte(0x8018) / 128) == 0 then if memory.readbyte(0x8037) == 0 then bcoin = not(jp["Coin 1"]) else bstart = true end end end gui.text(100, 100, string.format("%d %d", bcoin and 1 or 0, bstart and 1 or 0)) jp["1 Player Start"] = bstart jp["Coin 1"] = bcoin ]] joypad.set(jp) end --[[------------------------------------------------------------------------- ---------------------------------------------------------------------------]] emu.registerafter(function() --gui.register(function() onUpdateFrame() end)