Module:Move7: Difference between revisions

From Wavu Wiki, the 🌊 wavy Tekken wiki
No edit summary
No edit summary
Line 154: Line 154:
Standing = {
Standing = {
{ frames, damages = {
{ frames, damages = {
{ type, value }
regular = int
...
rage    = int
wall    = int
} }
} }
...
...
}
}
Crouching = {
Crouching = {
{ frames, damages = {
{ type, value }
...
} }
...
...
}
}
Line 176: Line 173:
--[[
--[[
damages = {
damages = {
{ type('' | rage | wall), value }
regular = int
...
rage   = int
wall    = int
}
}
--]]
--]]
Line 185: Line 183:
local value = tonumber(dmgSplit[1])
local value = tonumber(dmgSplit[1])
local dmgType = ''
local dmgType = 'regular'
if #dmgSplit == 2 then
if #dmgSplit == 2 then
dmgType = dmgSplit[2]
dmgType = dmgSplit[2]
end
end
table.insert(damages, { type = dmgType, value = value })
if damages[dmgType] == nil or damages[dmgType] < value then
damages[dmgType] = value
end
end
end
Line 198: Line 198:
local sameFramePunisherFound = false
local sameFramePunisherFound = false
for _, punisher in block[type] do
for _, p in block[type] do
if punisher.frames == frames then
if p.frames == frames then
sameFramePunisherFound = true
sameFramePunisherFound = true
for _, dmg in damages do
for dmgType, dmg in damages do
table.insert(punisher.damages, dmg)
if p.damages[dmgType] == nil or p.damages[dmgType] < dmg then
p.damages[dmgType] = dmg
end
end
end
end
end
Line 213: Line 215:
end
end
for _, punishers in ipairs(block) do
for _, punishers in ipairs(block) do
for _, punisher in ipairs(punishers) do
table.sort(punisher.damages, function(l, r) return l.value < r.value end)
end
table.sort(punishers, function(l, r) return l.frames < r.frames end)
table.sort(punishers, function(l, r) return l.frames < r.frames end)
end
end
Line 228: Line 227:
local grid = mw.html.create('div'):addClass('punishment-grid')
local grid = mw.html.create('div'):addClass('punishment-grid')
group:node(grid)
group:node(grid)
local dmgClasses = {
regular = 'bg-blue',
rage = 'bg-purple',
wall = 'bg-orange',
}
best = {}
best = {}
for _, punisher in pairs(punishment) do
for _, punisher in pairs(punishment) do
-- don't carry rage/meter punishment, makes viz. look funky
-- don't carry rage/meter punishment, makes viz. look funky
best[data.P_METER] = nil
best[dmgClasses.rage] = nil
for dmgType, dmg in punisher.damages do
for k, v in pairs(punisher) do
local dmgClass = dmgClasses[dmgType]
if best[dmgClass] == nil or best[dmgClass] < dmg then
best[dmgClass] = dmg
if best[v] == nil or best[v] < k then
best[v] = k
end
end
end
end
if best[true] then
if best[dmgClass.regular] then
for k,v in pairs(best) do
for k, v in pairs(best) do
if k ~= true and best[k] <= best[true] then
if k ~= dmgClass.regular and best[k] <= best[dmgClass.regular] then
best[k] = nil
best[k] = nil
end
end
end
end
end
end
if punishment[i] ~= nil or (i >= 10 and i <= 15) then
if punishment[i] ~= nil or (i >= 10 and i <= 15) then
bestSortedKeys = {}
bestSortedKeys = {}
Line 266: Line 268:
for i,k in ipairs(bestSortedKeys) do
for i,k in ipairs(bestSortedKeys) do
local class = k
local class = k
if class == true then class = 'bg-blue' end
bars:node(
bars:node(
mw.html.create('div'):addClass('punishment-bar')
mw.html.create('div'):addClass('punishment-bar')

Revision as of 22:00, 1 September 2023

Documentation for this module may be created at Module:Move7/doc

local p = {};
local cargo = mw.ext.cargo
local tables = 'MoveDataCargoTest'

p.fields = 'id,name,input,target,damage,reach,tracksLeft,tracksRight,startup,recv,tot,crush,block,hit,ch,notes'

-- Get move id for querying, which is the 1st unnamed param of a template.
-- Moves are queried like this: {{TempateName|<moveId>}}
function getQueryId(frame)
	local id = assert(frame:getParent().args[1], '1st unnamed param must be move id')
	return mw.text.trim(id) -- whitespace is stripped only from named params
end

function moveNotFoundMsg(id)
	return "move with id = '" .. id .. "' not found"
end

p.display = function(frame)
	local function appendFrom(dst, src)
		for k, v in pairs(src) do
			dst[k] = v
		end
	end
	local function getLeads(parentId)
		local leads = {}
		while (parentId ~= nil and parentId ~= '') do
			local parent = cargo.query(tables, 'input,target,damage,parent', { where = "id='" .. parentId .. "'" })[1]
			assert(parent, 'parent = "' .. parentId .. '" not found')

			parentId = parent['parent']
			parent['parent'] = nil
			
			for k, v in pairs(parent) do
				local leadName = k .. 'Lead'
				if leads[leadName] == nil then
					leads[leadName] = ''
				end
				
				leads[leadName] = v .. leads[leadName]
			end
		end
		
		return leads
	end
	
	local leads = getLeads(frame:getParent().args['parent'])
	if next(leads) == nil then
		return frame:expandTemplate{ title = 'MoveData', args = frame:getParent().args }
	end
	
	local args = {}
	appendFrom(args, frame:getParent().args)
	appendFrom(args, leads)
	return frame:expandTemplate{ title = 'MoveData', args = args }
end

p.query = function(frame)
	local id = getQueryId(frame)
	local result = cargo.query(tables, p.fields, { where = "id = '" .. id .. "'" })[1]
	assert(result, moveNotFoundMsg)
	
	for k, v in pairs(result) do
		local override = frame:getParent().args[k]
		if override ~= nil then
			result[k] = override
		end
	end
	
	return frame:expandTemplate{ title = 'MoveDataCargoTest/Display', args = result }
end

p.punisher = function(frame)
	local id = getQueryId(frame)
	local frames = frame:getParent().args['frames']
	local rowspan = frame:getParent().args['rowspan']
	local combo = frame:getParent().args['combo']
	local type = frame:getParent().args['type']
	
	local input = ''
	local damage = 0
	local hit = nil
	while (id ~= '') do
		local move = cargo.query(tables, 'parent,input,damage,hit', { where = "id = '" .. id .. "'" })[1]
		assert(move, moveNotFoundMsg(id))
		
		input = move['input'] .. input
		damage = damage + move['damage']
		if hit == nil then
			hit = move['hit']
		end
		
		id = move['parent']
	end
	
	frame:callParserFunction{ name = '#cargo_store', args = {
		'_table=Punisher',
        type = type,
		frames = frames,
		damage = combo or damage
	} }
	
	local row = mw.html.create('tr')
	if rowspan ~= 'skip' then
		row:tag('td'):wikitext(frames):attr('rowspan', rowspan or 1)
	end
	row:tag('td'):wikitext(input)
	if combo ~= nil then
		row:tag('td'):wikitext(damage .. ' (' .. combo .. ')')
	else
		row:tag('td'):wikitext(damage)
	end
	row:tag('td'):wikitext(hit)
	return row
end

p.punishment = function(frame)
	local character = frame:getParent().args[1]
	
	local data = mw.loadData('Module:Fighter/punishment')
	
	local punishers = cargo.query('Punisher', 'type,frames,damage', {
		where = "Punisher._pageName='User:Lume/Cargo testing/" .. character .. " punishers'"
			 .. " AND (type = 'Standing' OR type = 'Crouching')"
	})
	
	local span = 80
	for _, p in pairs(punishers) do
		local damages = mw.text.split(p['damage'], ', ', true)
		for _, d in pairs(damages) do
			if d > span then
				span = d
			end
		end
	end
	if span % 5 ~= 0 then
		span = span + 5 - (span % 5)
	end
	
	local scale = mw.html.create('div'):addClass('punishment-scale')
	for i = 0,span,5 do
		local pip = mw.html.create('div'):addClass('punishment-scalepip')
			:css("width", string.format("%.2f%%", 100 * i / span))
			:node(mw.html.create('div'):addClass('punishment-piplabel'):wikitext(i))
		if i % 50 == 0 then
			pip:addClass('major')
		elseif i % 25 == 0 then
			pip:addClass('minor')
		end
		scale:node(pip)
	end
	
	--[[
	block = {
		Standing = {
			{ frames, damages = {
				regular = int
				rage    = int
				wall    = int
			} }
			...
		}
		Crouching = {
			...
		}
	}
	--]]
	local block = {}
	for _, p in pairs(punishers) do
		local type = p['type']
		local frames = -tonumber(p['frames'])
		local damageStrings = mw.text.split(p['damage'], ', ', true)
		
		--[[
		damages = {
			regular = int
			rage    = int
			wall    = int
		}
		--]]
		local damages = {}
		for _, dmgStr in pairs(damageStrings) do
			local dmgSplit = mw.text.split(dmgStr, ' ', true)
			
			local value = tonumber(dmgSplit[1])
			local dmgType = 'regular'
			if #dmgSplit == 2 then
				dmgType = dmgSplit[2]
			end
			
			if damages[dmgType] == nil or damages[dmgType] < value then
				damages[dmgType] = value
			end
		end
		
		if block[type] == nil then
			block[type] = {}
		end
		
		local sameFramePunisherFound = false
		for _, p in block[type] do
			if p.frames == frames then
				sameFramePunisherFound = true
				
				for dmgType, dmg in damages do
					if p.damages[dmgType] == nil or p.damages[dmgType] < dmg then
						p.damages[dmgType] = dmg
					end
				end
			end
		end
		
		if not sameFramePunisherFound then
			table.insert(block[type], { frames = frames, damages = damages })
		end
	end
	for _, punishers in ipairs(block) do
		table.sort(punishers, function(l, r) return l.frames < r.frames end)
	end
	
	local root = mw.html.create('div'):addClass('punishment')
	for pos,punishment in pairs(block) do
		local group = mw.html.create('div'):addClass('punishment-group')
		root:node(group)
		group:node(mw.html.create("div"):addClass('punishment-label')
			:wikitext(pos .. " punishment")
		)
		local grid = mw.html.create('div'):addClass('punishment-grid')
		group:node(grid)
		
		local dmgClasses = {
			regular = 'bg-blue',
			rage = 'bg-purple',
			wall = 'bg-orange',
		}
		
		best = {}
		for _, punisher in pairs(punishment) do
			-- don't carry rage/meter punishment, makes viz. look funky
			best[dmgClasses.rage] = nil
			for dmgType, dmg in punisher.damages do
				local dmgClass = dmgClasses[dmgType]
				if best[dmgClass] == nil or best[dmgClass] < dmg then
					best[dmgClass] = dmg
				end
			end
			if best[dmgClass.regular] then
				for k, v in pairs(best) do
					if k ~= dmgClass.regular and best[k] <= best[dmgClass.regular] then
						best[k] = nil
					end
				end
			end
			if punishment[i] ~= nil or (i >= 10 and i <= 15) then
				bestSortedKeys = {}
				for k,_ in pairs(best) do
					table.insert(bestSortedKeys, k)
				end
				table.sort(bestSortedKeys, function (a, b)
					-- Larger bars first so that smaller bars are actually visible
					return best[a] > best[b]
				end)
				
				grid:node(mw.html.create('div'):addClass('punishment-startup')
					:wikitext("-" .. i)
				)
				local bars = mw.html.create('div'):addClass('punishment-bars')
				grid:node(bars)
				for i,k in ipairs(bestSortedKeys) do
					local class = k
					bars:node(
						mw.html.create('div'):addClass('punishment-bar')
							:addClass(class)
							:css("width", string.format("%.2f%%", 100 * best[k] / span))
							:wikitext(best[k])
					)
				end
				local median = data.median[pos][i]
				if median ~= nil then
					bars:node(
						mw.html.create('div'):addClass('punishment-median')
							:css('width', string.format("%.2f%%", 100 * median / span))
					)
				end
			end
		end
		grid:node(mw.clone(scale))
	end
	
	return root
end

return p