Module:GGST-FrameChart: Difference between revisions

From Dustloop Wiki
(add support for auto-parsing super startup and airgrab recovery)
(support 3x4 kind of active duration specifiers)
 
Line 86: Line 86:
   end
   end


 
   if string.find(duration, "%d+%s*,") ~= nil then
   if string.find(duration, "%d+%s*,") ~= nil then
     -- 1,2,3,4 format - multihit with no gaps
     -- 1,2,3,4 format -- multihit with no gaps
     local totalActive = 0
     local totalActive = 0


Line 110: Line 109:
     -- Done.
     -- Done.
     local out = {active = totalActive}
     local out = {active = totalActive}
     return out
     return out  
  elseif mw.ustring.find(duration, "^%d+[x×]%d+$") ~= nil then
    -- 3x4 format -- also multihit with no gaps
    local a, b = mw.ustring.match(duration, "^(%d+)[x×](%d+)$")
    local out = { active = tonumber(a) * tonumber(b) }
    return out
   elseif string.find(duration, "^%d+%(%d+%)") ~= nil then
   elseif string.find(duration, "^%d+%(%d+%)") ~= nil then
     -- 1(2)3(4)5 format - multihit with gaps
     -- 1(2)3(4)5 format -- multihit with gaps
     local out = {}
     local out = {}
     -- special handling for the first number
     -- special handling for the first number
Line 168: Line 172:


return p
return p
-- tests that you might want to check when majorly touching the code:
-- local f=mw.getCurrentFrame():newChild{args={chara="May", input="5P"}}; mw.log(p.drawFrameData(f))
-- {chara="Chipp Zanuff", input="j.H"}
-- {chara="Giovanna", input="632146H"}
-- {chara="Zato-1", input="j.P"}
-- {chara="Zato-1", input="j.6D or j.4D"}

Latest revision as of 20:49, 5 September 2022

Module:FrameChart-GGST is used to generate frame charts for characters in Guilty Gear: Strive semi-automatically.

It leverages Module:FrameChart under the hood, please see its documentation for further details.

Usage

Any page which uses the FrameChart module should include the template Template:FrameChartKey which explains each of the colored squares generated by the graph.

Creating the frame chart can be done by specifying the character and the input to use. For example:

{{#invoke:GGST-FrameChart|drawFrameData
|chara=May
|input=5P
}}

Results in a neat:

Total: 14

However, sometimes the frame data is not represented by simple numbers, and in those cases one needs to override them explicitly - see Module:FrameChart documentation on specifics.

For example, Sol's Volcanic Viper

chara input name startup active recovery
Sol Badguy 623S S Volcanic Viper 9 14 18+10 Landing

Naive version:

{{#invoke:GGST-FrameChart|drawFrameData
|chara=Sol Badguy
|input=623S
}}

The number of recovery frames is not a simple number (18+10 Landing). Please specify explicitly.

Fixed version:

{{#invoke:GGST-FrameChart|drawFrameData
|chara=Sol Badguy
|input=623S
|recovery=18
|specialRecovery=10
}}
Total: 50

local p = {}
local cargo = mw.ext.cargo
local framechart = require( "Module:FrameChart" )

function p.drawFrameData( frame )
  -- massage the inputs a tiny bit
  local chara = nil
  if frame.args['chara'] ~= nil then
    chara = frame:preprocess(frame.args['chara'])
  end
  local input = nil
  if frame.args['input'] then
    input = frame:preprocess(frame.args['input'])
  end
  if chara == nil then
    chara = string.match(mw.title.getCurrentTitle().text, "/([^/]+)")
  end
  if chara == nil then 
    return '<span class="error">failed to determine chara. Please specify explicitly</span>'
  end
  if input == nil then
    return '<span class="error">input is not specified</span>'
  end

  -- query cargo for frame data
  local cargo_tables = 'MoveData_GGST'
  local fields = 'startup,active,recovery'
  local cargo_args = {
    where = 'chara="' .. chara .. '" AND input="' .. input .. '"'
  }
  local framedata = cargo.query( cargo_tables , fields, cargo_args );
  if framedata[2] ~= nil then
    return '<span class="error">cargo returned more than one move for that query somehow... This is a bug..</span>'
  end
  local fd = framedata[1]
  if fd == nil then
    return '<span class="error">No results</span>'
  end
  
  -- update the cargo's data with manual overrides if supplied
  for _, frameKind in ipairs({"startup", "active", "recovery"}) do
    local duration = fd[frameKind]
    if frame.args[frameKind] ~= nil then
      duration = frame:preprocess(frame.args[frameKind])
    end

    -- Try to be overly clever and deal with framedata that is not a simple number
    -- This is totally optional.
    if duration == nil then
      -- no duration specified anywhere. Just skip it.
    elseif tonumber(duration) ~= nil then
      frame.args[frameKind] = duration
    else
      local parsed = nil
      if frameKind == "active" then
        parsed = parseActive(duration)
      elseif frameKind == "startup" then
        parsed = parseStartup(duration)
      elseif frameKind == "recovery" then
        parsed = parseRecovery(duration)
      end

      if parsed == nil then
        return string.format('<span class="error">The number of %s frames is not a simple number (%s). Please specify explicitly.</span>', frameKind, tostring(duration))
      end
      for k, v in pairs(parsed) do
        frame.args[k] = v
      end
    end
  end

  -- and finally draw it
  return framechart.drawFrameData(frame)
end


-- parses the passed in string to get the active/inactive frames for the move
-- returns nil when parsing failed, and a fancy table otherwise
function parseActive(duration)
  -- The implementation is scary because lua doesn't have good regexes,
  -- and also because I really would rather error than silently misinterpret the dat

  -- simple number
  if tonumber(duration) ~= nil then
    return {active = tonumber(duration)}
  end

  if string.find(duration, "%d+%s*,") ~= nil then
    -- 1,2,3,4 format -- multihit with no gaps
    local totalActive = 0

    -- first match - just a number
    local firstval, pos = string.match(duration, "^(%d+)%s*()")
    if firstval == nil then
      return nil
    end
    -- subsequent matches - coma, then number. Might have spaces between them
    totalActive = totalActive + tonumber(firstval)
    for p1, dur, p2 in string.gmatch(duration, "(),%s*(%d+)%s*()") do
      if pos ~= p1 then
        return nil
      end
      pos = p2
      totalActive = totalActive + tonumber(dur)
    end
    if pos ~= string.len(duration)+1 then
      return nil -- trailing stuff at the end
    end
    -- Done.
    local out = {active = totalActive}
    return out 
  elseif mw.ustring.find(duration, "^%d+[x×]%d+$") ~= nil then
    -- 3x4 format -- also multihit with no gaps
    local a, b = mw.ustring.match(duration, "^(%d+)[x×](%d+)$")
    local out = { active = tonumber(a) * tonumber(b) }
    return out
  elseif string.find(duration, "^%d+%(%d+%)") ~= nil then
    -- 1(2)3(4)5 format -- multihit with gaps
    local out = {}
    -- special handling for the first number
    local firstval, pos = string.match(duration, "^(%d+)()")
    out['active'] = firstval

    local ordinal = 2
    -- then we just have a groups of "(inactive)active"
    for p1, d1, d2, p2 in string.gmatch(duration, "()%((%d+)%)(%d+)()") do
      if pos ~= p1 then
        return nil -- not directly following the previous match, so basically not what we expect
      end
      out['inactive' .. tostring(ordinal)] = d1
      out['active' .. tostring(ordinal+1)] = d2
      ordinal = ordinal + 2
      pos = p2
    end
    if pos ~= string.len(duration)+1 then
      return nil -- trailing stuff at the end
    end
    -- Done.
    return out
  end
  -- unrecognized format
  return nil
end

function parseStartup(duration)
  -- simple number
  if tonumber(duration) ~= nil then
    return {startup = tonumber(duration)}
  end

  -- 1+2 -- common for supers
  if string.find(duration, "^%d+%+%d+$") ~= nil then
    local first, second = string.match(duration, "^(%d+)%+(%d+)$")
    return {startup = tonumber(first) + tonumber(second) }
  end
  return nil
end

function parseRecovery(duration)
  -- simple number
  if tonumber(duration) ~= nil then
    return {startup = tonumber(duration)}
  end

  -- "Until L+10" -- aerial moves, such as grabs.
  if string.find(duration, "^Until L%+%d+$") ~= nil then
    local recovery = string.match(duration, "^Until L%+(%d+)$")
    return { recovery=0, specialRecovery=recovery }
  end
  return nil
end

return p


-- tests that you might want to check when majorly touching the code:
-- local f=mw.getCurrentFrame():newChild{args={chara="May", input="5P"}}; mw.log(p.drawFrameData(f))
-- {chara="Chipp Zanuff", input="j.H"}
-- {chara="Giovanna", input="632146H"}
-- {chara="Zato-1", input="j.P"}
-- {chara="Zato-1", input="j.6D or j.4D"}