[sfiii3 sfiii3n sfiii3nr1] Hitbox viewer port

This forum is for posting M.A.M.E. arcade cheats. Requests will be fulfilled here....but please keep the requests to the requests forum.
Post Reply
rabbyzero
Posts: 24
Joined: Thu Nov 16, 2017 3:20 am

[sfiii3 sfiii3n sfiii3nr1] Hitbox viewer port

Post by rabbyzero »

This code is a port from sfiii3 hitbox viewer in mame-rr cps3-hitboxes.lua.
Tested on sfiii3nr1. I believe it also works in other sfiii3 variants.
Just save the code as sfiii3nr1.json or [sfiii3 game].json and put in cheat folder or add to cheat.7z.
Hotkey function in original mame-rr was removed.
mini_axis and blank screen layer are disabled and throw box is enabled by default.


Code: Select all

[{
    "space":{
      "cpup":{
        "type":"program",
        "tag":":maincpu"
      }
    },
    "script":{
      "on":"
 --[[--Street Fighter III 3rd Strike hitbox viewer]]
 --[[--October 27, 2011]]
 --[[--http://code.google.com/p/mame-rr/wiki/Hitboxes]]

boxes = {
        [\"vulnerability\"] = {color = 0x7777FF, fill = 0x40, outline = 0xFF},
   [\"ext. vulnerability\"] = {color = 0x0000FF, fill = 0x40, outline = 0xFF},
    --extended limbs during attacks
               [\"attack\"] = {color = 0xFF0000, fill = 0x40, outline = 0xFF},
  [\"proj. vulnerability\"] = {color = 0x00FFFF, fill = 0x40, outline = 0xFF},
         [\"proj. attack\"] = {color = 0xFF66FF, fill = 0x40, outline = 0xFF},
                 [\"push\"] = {color = 0x00FF00, fill = 0x20, outline = 0xFF},
                [\"throw\"] = {color = 0xFFFF00, fill = 0x40, outline = 0xFF},
            [\"throwable\"] = {color = 0xF0F0F0, fill = 0x20, outline = 0xFF},
}

globals = {
  axis_color           = 0xFFFFFFFF,
  blank_color          = 0xFFFFFFFF,
  axis_size            = 12,
  mini_axis_size       = 2,
  blank_screen         = false,
  draw_axis            = true,
  draw_mini_axis       = false,
  draw_pushboxes       = true,
  draw_throwable_boxes = true,
  no_alpha             = false, --fill = 0x00, outline = 0xFF for all box types
}

--------------------------------------------------------------------------------
-- game-specific modules

DRAW_DELAY    = 1
GROUND_OFFSET = -23
MAX_OBJECTS   = 30
projectile_type = {
              [\"attack\"] = \"proj. attack\",
       [\"vulnerability\"] = \"proj. vulnerability\",
  [\"ext. vulnerability\"] = \"proj. vulnerability\",
}

function any_true(condition)
  for n = 1, #condition do
    if condition[n] == true then return true end
  end
end


profile =
{\tgames = {\"sfiii3\"},
  player = {0x02068C6C, 0x2069104}, --0x498
  object = {initial = 0x02028990, index = 0x02068A96},
  screen = {x = 0x02026CB0, y = 0x02026CB4},
  match  = 0x020154A6,
  scale  = 0x0200DCBA,
  char_id = 0x3C0,
  ptr = {
    valid_object = 0x2A0,
    {offset = 0x2D4, type = \"push\"},
    {offset = 0x2C0, type = \"throwable\"},
    {offset = 0x2A0, type = \"vulnerability\", number = 4},
    {offset = 0x2A8, type = \"ext. vulnerability\", number = 4},
    {offset = 0x2C8, type = \"attack\", number = 4},
    {offset = 0x2B8, type = \"throw\"},
  },
}

for _, box in ipairs(profile.ptr) do
  box.initial = box.initial or 1
  box.number  = box.number  or 1
end

profile.match_active = function()
  return cpup:read_u32(profile.match) > 0x00010000 and cpup:read_u16(profile.match) ~= 0x0009
end

profile.get_screen = function(f)
  f.screen_x = cpup:read_i16(profile.screen.x)
  f.screen_y = cpup:read_i16(profile.screen.y)
end

profile.define_box = function(f, obj, ptr, type)
  if obj.friends > 1 then
   --Yang SA3
    if type ~= \"attack\" then
      return
    end
  elseif obj.projectile then
    type = projectile_type[type] or type
  end

  local box = {
    left   = f.scale * -cpup:read_i16(ptr + 0x0),
    right  = f.scale * -cpup:read_i16(ptr + 0x2),
    bottom = f.scale * -cpup:read_i16(ptr + 0x4),
    top    = f.scale * -cpup:read_i16(ptr + 0x6),
    type   = type,
  }

  if box.left == 0 and box.right == 0 and box.top == 0 and box.bottom == 0 then
    return
  elseif obj.flip_x == 0 then
    box.left  = -box.left
    box.right = -box.right
  end

  box.left   = box.left   + obj.pos_x
  box.right  = box.right  + box.left
  box.bottom = box.bottom + obj.pos_y
  box.top    = box.top    + box.bottom
  box.hval   = (box.left + box.right)/2
  box.vval   = (box.bottom + box.top)/2

  table.insert(obj, box)
end

profile.update_game_object = function(f, obj)
  if cpup:read_u32(obj.base + profile.ptr.valid_object) == 0 then
   --invalid objects
    return
  end

  obj.friends = cpup:read_u8(obj.base + 0x1)
  obj.flip_x = cpup:read_i8(obj.base + 0x0A)
  obj.pos_x = cpup:read_i16(obj.base + 0x64)
  obj.pos_y = cpup:read_i16(obj.base + 0x68)
  obj.pos_x =  obj.pos_x - f.screen_x + screen:width()/2
  obj.pos_y = -obj.pos_y + f.screen_y + screen:height() + GROUND_OFFSET
  obj.char_id = cpup:read_u16(obj.base + profile.char_id)

  for _, box in ipairs(profile.ptr) do
    for i = box.initial, box.number do
      profile.define_box(f, obj, cpup:read_u32(obj.base + box.offset) + (i-1)*8, box.type)
    end
  end

  table.insert(f, obj)
end

profile.read_misc_objects = function(f)
  -- This function reads all game objects other than the two player characters.
  -- This includes all projectiles and even Yang's Seiei-Enbu shadows.

  -- The game uses the same structure all over the place and groups them
  -- into lists with each element containing an index to the next element
  -- in that list. An index of -1 signals the end of the list.

  -- I believe there are at least 7 lists (0-6) but it seems everything we need
  -- (and lots we don't) is in list 3.
  local list = 3
  local obj_index = cpup:read_u16(profile.object.index + (list * 2))
    
  local obj_slot = 1
  while obj_slot <= MAX_OBJECTS and obj_index ~= -1 do
    --local obj = {base = profile.object.initial + bit.lshift(obj_index, 11), projectile = obj_slot}
    local obj = {base = profile.object.initial + (obj_index << 11), projectile = obj_slot}
    profile.update_game_object(f, obj)

    -- Get the index to the next object in this list.
    obj_index = cpup:read_i16(obj.base + 0x1C)
    obj_slot = obj_slot + 1
  end
end

profile.read_throws = function(f) end

for _,box in pairs(boxes) do
  box.fill    = box.color | ((globals.no_alpha and 0x00 or box.fill) << 24)
  box.outline = box.color | ((globals.no_alpha and 0xFF or box.outline) << 24)
end

frame_buffer = {}

function copytable(orig)
  local copy = {}
  for orig_key, orig_value in pairs(orig) do
    copy[orig_key] = orig_value
  end
  return copy
end

function max(a, b)
  if a > b then
    return a
  end
  return b
end

--------------------------------------------------------------------------------
-- prepare the hitboxes

function update_hitboxes()
  for f = 1, DRAW_DELAY do
    frame_buffer[f] = copytable(frame_buffer[f+1])
  end

  frame_buffer[DRAW_DELAY+1] = {match_active = profile and profile.match_active()}
  local f = frame_buffer[DRAW_DELAY+1]

  if not f.match_active then
    return
  end

  profile.get_screen(f)
  f.scale = cpup:read_i16(0x040C006E)
  f.scale = 0x40/(f.scale > 0 and f.scale or 1)

  for p = 1, #profile.player do
    local player = {base = profile.player[p]}
    profile.update_game_object(f, player)
  end
  profile.read_misc_objects(f)

  f = frame_buffer[DRAW_DELAY]
  profile.read_throws(f)

  f.max_boxes = 0
  for _, obj in ipairs(f) do
    f.max_boxes = max(f.max_boxes, #obj)
  end
end

--------------------------------------------------------------------------------
-- draw the hitboxes

function draw_hitbox(hb)
  if not hb or any_true({
    not globals.draw_pushboxes and hb.type == \"push\",
    not globals.draw_throwable_boxes and hb.type == \"throwable\",
  }) then return
  end

  if globals.draw_mini_axis then
    draw_line(screen,hb.hval, hb.vval-globals.mini_axis_size, hb.hval, hb.vval+globals.mini_axis_size, boxes[hb.type].outline)
    draw_line(screen,hb.hval-globals.mini_axis_size, hb.vval, hb.hval+globals.mini_axis_size, hb.vval, boxes[hb.type].outline)
  end

  draw_box(screen,hb.left, hb.top, hb.right, hb.bottom, boxes[hb.type].fill, boxes[hb.type].outline)
end


function draw_axis(obj)
  draw_line(screen,obj.pos_x, obj.pos_y-globals.axis_size, obj.pos_x, obj.pos_y+globals.axis_size, globals.axis_color)
  draw_line(screen,obj.pos_x-globals.axis_size, obj.pos_y, obj.pos_x+globals.axis_size, obj.pos_y, globals.axis_color)
  --draw_text(screen,obj.pos_x+4, obj.pos_y-0x08, string.format(\"%08X\", obj.base))
end


function render_hitboxes()
  local f = frame_buffer[1]
  if not f.match_active then
    return
  end

  if globals.blank_screen then
    draw_box(screen,0, 0, screen:width(), screen:height(), globals.blank_color)
  end

  for entry = 1, f.max_boxes or 0 do
    for _, obj in ipairs(f) do
      draw_hitbox(obj[entry])
    end
  end

  if globals.draw_axis then
    for _, obj in ipairs(f) do
      draw_axis(obj)
    end
  end
end

function initialize_fb()
  for f = 1, DRAW_DELAY+1 do
    frame_buffer[f] = {}
  end
end

initialize_fb()
game = profile
",
    "run":"update_hitboxes() render_hitboxes()"
    },
    "screen":{
      "screen":":screen"
    },
    "desc":"Hitbox viewer"
}]
User avatar
Pugsy
Posts: 3638
Joined: Fri Aug 17, 2001 12:59 am
Location: North Wales, UK.
Has thanked: 1 time
Been thanked: 12 times
Contact:

Re: [sfiii3 sfiii3n sfiii3nr1] Hitbox viewer port

Post by Pugsy »

Thanks, added.
Pugsy

Servicing your cheating needs since 1985 8)

Grab the latest cheat collection:
MAME 0.259 XML cheat collection (6 OCTOBER 2023) from http://www.mamecheat.co.uk or direct from:-
https://mega.nz/file/q4dHGZ6K#i-EUiqIjH ... KMz7hnbTfw (ZIP Archive 3.76MB)
Post Reply