Module:Memento

local query = mw.ext.cargo.query local CARD_CNDS = mw.loadData('Module:Data/MasterParam/ConceptCardConditions') local UG = mw.loadData('Module:Data/MasterParam/UnitGroup') local CT = mw.loadData('Module:Data/MasterParam/CustomTarget') local statTypeMap = mw.loadData('Module:Data/Extra/StatTypeMap') local statNameMap = mw.loadData('Module:Data/Extra/StatNameMap')

local gsplit = require('Module:TextUtil').gsplit local split = require('Module:TextUtil').split

-- Could support multi params? :D local getLoc = function(_pageName, param) local tables = 'Loc' local fields = 'value' local args = { where = '_pageName = "'.._pageName..'" AND value<>"" AND param = "'..param..'" AND lang IN ("english","japanese")', orderBy = 'lang ASC', }	local l10n = query(tables, fields, args)[1] return l10n.value end

local function renderIcon(args) local filename = args.filename or 'Item IT Unknown.png' local rare = args.rare or 0 local type = args.type or 0 local name = args.name and '|' .. args.name or '' local link = args.link and '|link=' .. args.link or '' local count = args.count or 1 -- TODO: Implement displaying count. return '   ' end

local Buff = {} Buff.__index = Buff

function Buff.new(args) local self = setmetatable({}, Buff) self.type = args.type self.min = args.min self.max = args.max self.per = (self.max - self.min) / (args.maxlevel - 1) return self end

function Buff:atLevel(level) return math.floor((level - 1) * self.per + self.min) end

-- Card class

local Card = {} Card.__index = Card

-- These max levels include Max LB (+10 level cap). -- so at 0 LB, level caps are {1, 15, 20, 25, 30} Card.maxlevels = { 1, 25, 30, 35, 40 }

Card.types = { 'Equipment', 'Enhance EXP', 'Enhance Trust', }

function Card.new(args) local self = setmetatable({}, Card)

-- parse self:parseCard(args)

return self end

function Card:renderUnitIcon( iname ) local unit = query('Unit', 'img,rare', {		where = 'iname="'..iname..'" AND server="'..self.server..'"',	})[1] local name = getLoc('Data:Game/MasterParam/Unit/'..iname:gsub('_', ' '), 'name') local icon = renderIcon{ filename = 'Game,Portraits,'..unit.img..'.png', rare = unit.rare, name = name, link = name, }	return icon end

function Card:parseCard(args) mw.logObject(args, 'args') self.iname = args.iname self.server = args.server or 'gl' self.type = self.types[args.type] or args.type self.rare = args.rare or 0 self.stars = self.rare + 1 -- max LB inclusive level. self.maxlevel = self.maxlevels[self.stars] self.en_cost = args.en_cost self.en_exp = args.en_exp self:parseVisionClearRewards(args) self:parseRelief for i, effect in ipairs(args.effects or {}) do		self:parseEffect(effect) end end

function Card:parseVisionClearRewards(args) self.vcr = self.vcr or {} if not args.trust_artifact_names then return nill end local l = #args.trust_artifact_names for i=1,l do		local vcr_iname = args.trust_artifact_names[i] local vcr_count = args.trust_artifact_counts[i] local vcr = query('Artifact,Pages', 'Pages._pageName=_pageName,icon,rini,server',{			where = 'Artifact.iname="'..vcr_iname..'" AND server = "'..self.server..'"',			join = 'Artifact.iname = Pages.iname',		})[1] if vcr then local filename = vcr.icon:sub(1,3) == 'AF_' and 'Game,ArtiIcon,'..vcr.icon..'.png' or 'Game,ItemIcon,IT UNKNOWN.png' local icon = renderIcon{ filename = filename, rare = vcr.rini, type = 2, name = vcr._pageName, link = vcr._pageName, count = vcr_count, }			self.vcr[#self.vcr+1] = icon end end end

function Card:parseEffect(effect) if effect.card_skill then self:parseSkill(effect) elseif effect.skin then self:parseSkin(effect) elseif effect.statusup_skill then self:parseStatsUpSkill(effect) end end

function Card:getBuff(iname) iname = iname or '' if iname == '' then return nil end

local tables = 'Buff' local fields = 'app_mct,app_type,birth,calc1,calc10,calc11,calc2,calc3,calc4,calc5,calc6,calc7,calc8,calc9,chktgt,cond,custom_targets,eff_range,elem,iname,is_no_bt,is_no_dis,is_up_rep,job,rate,server,sex,tag,timing,tktag1,tktag10,tktag11,tktag2,tktag3,tktag4,tktag5,tktag6,tktag7,tktag8,tktag9,turn,type1,type10,type11,type2,type3,type4,type5,type6,type7,type8,type9,un_group,up_timing,vini1,vini10,vini11,vini2,vini3,vini4,vini5,vini6,vini7,vini8,vini9,vmax1,vmax10,vmax11,vmax2,vmax3,vmax4,vmax5,vmax6,vmax7,vmax8,vmax9,vone1,vone11,vone2,vone3,vone4,vone5,vone6,vone7,vone8' local args = { where = 'iname="'..iname..'" AND server="'..self.server..'"', }	local buff = query(tables, fields, args)[1] return buff end

function Card:parseBuff(buff) local buffs = {} for i=1,11 do		local tp = buff['type'..i]		if tp == nil or tp == '' then break end local tp = statNameMap[statTypeMap[tostring(tp)]] local min = tonumber(buff['vini'..i]) local max = tonumber(buff['vmax'..i]) buffs[i] = Buff.new{ type = tp, min = min, max = max, maxlevel = self.maxlevel, }	end

local out = {}

-- Tabularize local simple = mw.html.create('table'):addClass('wikitable') local tr = simple:tag('tr') tr:tag('th'):wikitext('Type') tr:tag('th'):wikitext('Lvl 1') tr:tag('th'):wikitext('Lvl ' .. (self.maxlevel - 10)) tr:tag('th'):wikitext('Lvl ' .. (self.maxlevel)) for i, buff in ipairs(buffs) do		simple:newline local tr = simple:tag('tr') tr:tag('td'):wikitext(buff.type) tr:tag('td'):wikitext(buff:atLevel(1)) tr:tag('td'):wikitext(buff:atLevel(self.maxlevel - 10)) tr:tag('td'):wikitext(buff:atLevel(self.maxlevel)) end

local detail = mw.html.create('table'):addClass('wikitable frozen-header') local tr = detail:tag('tr') tr:tag('th'):wikitext('Level') for i, buff in ipairs(buffs) do		tr:tag('th'):wikitext(buff.type) end for level = 1, self.maxlevel do		detail:newline local tr = detail:tag('tr') tr:tag('th'):wikitext(level) for i, buff in ipairs(buffs) do			tr:tag('td'):wikitext(buff:atLevel(level)) end end

-- Tabber local content = '|-|Simple=' .. tostring(simple) .. '\n|-|Detailed=' .. tostring(detail) local frame = mw.getCurrentFrame -- mw.log(' '..content..' ') out[#out+1] = frame:extensionTag('tabber', content)

return table.concat(out, '\n') end

function Card:parseAwakeBuff(buff) local buffs = {} for i=1,11 do		local tp = buff['type'..i]		if tp == nil or tp == '' then break end local tp = statNameMap[statTypeMap[tostring(tp)]] local min = tonumber(buff['vini'..i]) local max = tonumber(buff['vmax'..i]) buffs[i] = Buff.new{ type = tp, min = min, max = max, maxlevel = 5, }	end

-- Limit Break local root = mw.html.create('table'):addClass('wikitable') root:tag('caption'):wikitext('Limit Break') local tr = root:tag('tr') tr:tag('th'):wikitext('Type') tr:tag('th'):wikitext('LB1') tr:tag('th'):wikitext('LB2') tr:tag('th'):wikitext('LB3') tr:tag('th'):wikitext('LB4') tr:tag('th'):wikitext('MLB') for i,buff in ipairs(buffs) do		root:newline local tr = root:tag('tr') tr:tag('th'):wikitext(buff.type) tr:tag('td'):wikitext('+'..buff:atLevel(1)) tr:tag('td'):wikitext('+'..buff:atLevel(2)) tr:tag('td'):wikitext('+'..buff:atLevel(3)) tr:tag('td'):wikitext('+'..buff:atLevel(4)) tr:tag('td'):wikitext('+'..buff:atLevel(5)) end return tostring(root) end

function Card:parseSkill(effect) local skill_iname = effect.card_skill or '' if effect.card_skill == '' then return nil end local out = {} mw.logObject(effect, 'effect') local skill = query('Skill', '_pageName, t_buff,s_buff', {where='iname = "'..skill_iname..'" AND server="'..self.server..'"'})[1] if not skill then mw.log("SKILL", skill_iname) return nil end local name = getLoc(skill._pageName, 'name') out[#out+1] = '=== "' .. name .. '" Group Skill ===' -- s_buff = self buff, t_buff = target buff -- This is stored in the skill's t_buff (sometimes s_buff?)

-- Target Buffs local t_buff = skill.t_buff or '' if t_buff ~= '' then local buff = self:getBuff(t_buff) local buff_stats = self:parseBuff(buff) out[#out+1] = buff_stats end local s_buff = skill.s_buff or '' if s_buff ~= '' then local buff = self:getBuff(s_buff) local buff_stats = self:parseBuff(buff) out[#out+1] = buff_stats end

-- Limit break table local awakeBuff = self:getBuff(effect.add_card_skill_buff_awake) if awakeBuff then local buff_stats = self:parseAwakeBuff(awakeBuff) out[#out+1] = buff_stats end

-- Unit Group (Target + Buff) local buff = awakeBuff local cnd = CARD_CNDS[effect.cnds_iname] or {}

local triggered = {} local buffed = {}

if buff.un_group and cnd.un_group and buff.un_group == cnd.un_group then triggered[cnd.un_group] = true end local ct_inames = split(buff.custom_targets or '', '|') if next(ct_inames) and next(cnd) then for i, ct_iname in ipairs(ct_inames) do			local ct = CT[ct_iname] local cnd_iname = cnd.iname; cnd.iname = nil for cnd_key, cnd_val in pairs(cnd) do				if cnd_key == 'un_group' and ct.unit_groups then for i, val in ipairs(ct.unit_groups) do						if val == cnd_val then triggered[ct_iname] = true end end end if cnd_key == 'birth_id' then for i, birth_id in ipairs(cnd_val) do						if ct.birth_id == birth_id then triggered[ct_iname] = true end end end if cnd_key == 'job_group' and ct.job_groups then -- string for i, job_group in ipairs(ct.job_groups) do						if job_group == cnd_val then triggered[ct_iname] = true end end end end if not triggered[ct_iname] then buffed[ct_iname] = true end end end

if next(triggered) then local t = {} for k, v in pairs(triggered) do			t[#t+1] = k		end out[#out+1] = '=== Targets (Trigger + Buff) ===\n* ' .. table.concat(t, '|') end

if next(buffed) then local t = {} for k, v in pairs(buffed) do			t[#t+1] = k		end out[#out+1] = '=== Targets (Buff) ===\n* ' .. table.concat(t, '|') end

-- local cnds_iname = effect.cnds_iname or '' -- local cnd = CARD_CNDS[cnds_iname] or {} -- mw.logObject(cnd, 'cnd') -- local birth_ids = cnd.birth_id or {} -- if #birth_ids > 0 then -- 	local ids = {} -- 	for i, birth_id in ipairs(birth_id) do	-- 		ids[i] = string.format('%q', birth_id) -- 	end -- 	-- It's okay for transformations to not be in this list ^_^ -- 	local units = query('Unit', '_pageName, iname', {	-- 		where = 'ai = "AI_PLAYER" AND (notsmn IS NULL OR hero = 1) AND piece IS NOT NULL AND server="'..self.server..'" AND birth_id IN ('..table.concat(ids, ',')..')',	-- 	}) -- 	local unit_inames = {} -- 	for i, unit in ipairs(units) do	-- 		unit_inames[i] = unit.iname -- 	end -- 	out[#out+1] = table.concat(unit_inames, ', ') -- end -- local un_group = cnd.un_group or '' -- if un_group ~= '' then -- 	local grp = UG[un_group] -- 	mw.logObject(grp, 'grp') -- 	if not grp then return nil end -- 	out[#out+1] = '; Unit Group (Trigger + Buff)' -- 	local units = {} -- 	for i, unit_iname in ipairs(grp.units) do	-- 		-- Unit Group (Trigger + Buff) -- 		units[#units+1] = self:renderUnitIcon(unit_iname) -- 	end -- 	out[#out+1] = table.concat(units, ' ') -- end self.skills = self.skills or {} self.skills[#self.skills+1] = table.concat(out, '\n') end

function Card:parseSkin(effect) local skin_iname = effect.skin local cnd = CARD_CNDS[effect.cnds_iname] local grp = UG[cnd.un_group] local icons = {} for i, unit_iname in ipairs(grp.units) do		local tables = 'Artifact' local fields = '_pageName,asset,iname' local args = { where = 'iname = "'..skin_iname..'" AND server="'..self.server..'" AND asset!="unique"', }	local artifact = query(tables, fields, args)[1] local unit = query('Unit', 'img,rare', {		where='iname="'..unit_iname..'" AND server="'..self.server..'"'	})[1] local name = getLoc(artifact._pageName, 'name') local page = query('Pages', '_pageName', {where='iname="'..self.iname..'"'})[1]

local skinImg = unit.img .. '_' .. artifact.asset -- TODO: handle errors

local icon = renderIcon{ filename = 'Game,Portraits,'..skinImg..'.png', rare = unit.rare, name = name, link = page._pageName, }	icons[#icons+1] = icon end if #icons > 0 then self.skins = self.skins or {} for i, icon in ipairs(icons) do			self.skins[#self.skins+1] = icon end end end

function Card:parseStatsUpSkill(effect) local skill_iname = effect.statusup_skill local tables = 'Skill' local fields = 't_buff' local args = { where = 'iname = "'..skill_iname..'" AND t_buff<>"" AND server="'..self.server..'"', }	local results = query(tables, fields, args) if results[1] then local buff = self:parseBuff(results[1].t_buff) self.statusup_skill = buff end end

function Card:parseRelief if not self.iname then return nil end -- Apparently, it's not in Global yet. local results = query('ConceptCard', '_pageName,iname,birth_id', {where='iname="'..self.iname..'" AND birth_id IS NOT NULL'}) if not next(results) then return nil end local enums = require('Module:Data/Enums') for i,row in ipairs(results) do		local birth_id = tonumber(row.birth_id) local origin = enums.originFromKey(birth_id) -- idk how to detect collab vs christmas. if origin ~= nil and origin:lower ~= 'other' then local origin_iname = 'IT_CONCEPTCARD_COMMON_' .. origin:sub(1,3):upper local relief = query('Item', '_pageName,icon,rare', {where='iname = "'..origin_iname..'"'})[1] if relief then local icon = renderIcon{ filename = 'Game,ItemIcon,'..relief.icon..'.png', rare = relief.rare, }				self.relief = (self.relief or '') .. icon end end end end

-- Export

local p = {}

function p.main(frame) local args = frame.args return p._main(args) end

function p._main(args) local gl = args.gl or '' local jp = args.jp or ''

local out = {}

if jp ~= '' then local json = jp:gsub('', {['('] = '{', [')'] = '}', ['!'] = '|'}) local cardArgs = mw.text.jsonDecode(json) cardArgs.server = 'jp' local card = Card.new(cardArgs) -- out[#out+1] = '{| class="wikitable"' -- out[#out+1] = '|-\n!Type\n|'..card.type -- out[#out+1] = '|-\n!Rank\n|'..card.stars..'★' -- out[#out+1] = '|-\n!Max Level\n|'..card.maxlevel -- out[#out+1] = '|-\n!Enhancer Cost\n|'..card.en_cost -- out[#out+1] = '|-\n!Enhancer EXP\n|'..card.en_exp -- if card.vcr and #card.vcr > 0 then -- 	out[#out+1] = '|-\n!Vision Clear Reward\n|'..table.concat(card.vcr) -- end -- if card.skins and #card.skins > 0 then -- 	out[#out+1] = '|-\n!Skin\n|'..table.concat(card.skins) -- end -- if card.relief then -- 	out[#out+1] = '|-\n!General LB Relief\n|'..card.relief -- end -- out[#out+1] = '|}' -- out[#out+1] = '==Stats==' -- out[#out+1] = card.statusup_skill out[#out+1] = '==Effects==' for i,skill in ipairs(card.skills or {}) do			out[#out+1] = skill end end

if gl ~= '' then local json = jp:gsub('', {['('] = '{', [')'] = '}', ['!'] = '|'}) local cardArgs = mw.text.jsonDecode(json) cardArgs.server = 'gl' local card = Card.new(cardArgs) -- out[#out+1] = '{| class="wikitable"' -- out[#out+1] = '|-\n!Type\n|'..card.type -- out[#out+1] = '|-\n!Rank\n|'..card.stars..'★' -- out[#out+1] = '|-\n!Max Level\n|'..card.maxlevel -- out[#out+1] = '|-\n!Enhancer Cost\n|'..card.en_cost -- out[#out+1] = '|-\n!Enhancer EXP\n|'..card.en_exp -- if card.vcr and #card.vcr > 0 then -- 	out[#out+1] = '|-\n!Vision Clear Reward\n|'..table.concat(card.vcr) -- end -- if card.skins and #card.skins > 0 then -- 	out[#out+1] = '|-\n!Skin\n|'..table.concat(card.skins) -- end -- if card.relief then -- 	out[#out+1] = '|-\n!General LB Relief\n|'..card.relief -- end -- out[#out+1] = '|}' -- out[#out+1] = '==Stats==' -- out[#out+1] = card.statusup_skill out[#out+1] = '==Effects==' for i,skill in ipairs(card.skills or {}) do			out[#out+1] = skill end end return table.concat(out, '\n') end

function p.test -- mw.log(p._main{gl='$($"sell":1000,"rare":4,"iname":"TS_WRATH_SPICA_01","coin_item":0,"en_cost":2000,"effects":[{"card_skill":"SK_GS1_WRATHTRIZ_SPICA_01","add_card_skill_buff_awake":"BUFF_GS1_WRATHTRIZ_SPICA_01_BONUS01","cnds_iname":"CARD_CNDS_SHADOW_MESSIAH"$)$,{"card_skill":"SK_GS2_WRATHTRIZ_SPICA_01","add_card_skill_buff_lvmax":"BUFF_GS2_WRATHTRIZ_SPICA_01_BONUS02","add_card_skill_buff_awake":"BUFF_GS2_WRATHTRIZ_SPICA_01_BONUS01","cnds_iname":"CARD_CNDS_ZAHA_SPIC"},{"abil_iname":"AB_01_WRATHTRIZ_SPICA_01","cnds_iname":"CARD_CNDS_WRATHTRIZ"},{"abil_iname":"AB_01_WRATHTRIZ_SPICA_01","cnds_iname":"CARD_CNDS_UROBOROS_1"},{"statusup_skill":"SK_WRATHTRIZ_SPICA_01"},{"abil_iname":"AB_02_WRATHTRIZ_SPICA_01"},{"abil_iname_lvmax":"AB_TS_WRATH_SPICA_01_MAX_SPD_VA","abil_iname":"AB_TS_WRATH_SPICA_01_MAX_SPD_VA_NON","cnds_iname":"CARD_CNDS_SPIC_SHIR_UROB"},{"skin":"AF_SK_SPIC_BABEL","cnds_iname":"CARD_CNDS_SPIC"}],"en_exp":20000,"type":1,"icon":"TS_WRATH_SPICA_01","trust_item_names":[],"trust_item_counts":[],"trust_artifact_names":["AF_ACCS_HAIR_ORNAMENTS_01"],"trust_artifact_counts":[1]}'}) -- mw.log(p._main{gl='{"sell":1000,"rare":4,"iname":"TS_LOST_ZWEI_01","coin_item":0,"en_cost":2000,"effects":[{"card_skill":"SK_GS1_LOST_ZWEI_01","add_card_skill_buff_awake":"BUFF_GS1_LOST_ZWEI_01_BONUS_01","cnds_iname":"CARD_CNDS_LOST"},{"card_skill":"SK_GS1_LOST_ZWEI_01","add_card_skill_buff_awake":"BUFF_GS1_LOST_ZWEI_01_BONUS_01","cnds_iname":"CARD_CNDS_UROBOROS_2"},{"card_skill":"SK_GS2_LOST_ZWEI_01","add_card_skill_buff_awake":"BUFF_GS2_LOST_ZWEI_01_BONUS_01","cnds_iname":"CARD_CNDS_ZIKKAI_WOMAN_UROB"},{"card_skill":"SK_GS3_LOST_ZWEI_01","add_card_skill_buff_lvmax":"BUFF_GS3_LOST_ZWEI_01_BONUS_02","add_card_skill_buff_awake":"BUFF_GS3_LOST_ZWEI_01_BONUS_01","cnds_iname":"CARD_CNDS_ZWEI_UROB"},{"abil_iname_lvmax":"AB_LOST_ZWEI_01_VA_01_MAX","abil_iname":"AB_LOST_ZWEI_01_VA_01","cnds_iname":"CARD_CNDS_NOIN_ZWEI_JOB_UROB01"},{"abil_iname_lvmax":"AB_LOST_ZWEI_01_MAX_SPD_VA","abil_iname":"AB_LOST_ZWEI_01_MAX_SPD_VA_NON","cnds_iname":"CARD_CNDS_ZWEI_UROB"},{"statusup_skill":"SK_LOST_ZWEI_01"}],"en_exp":20000,"type":1,"icon":"TS_LOST_ZWEI_01","trust_item_names":[],"trust_item_counts":[],"trust_artifact_names":["AF_ACCS_ZWEI_01"],"trust_artifact_counts":[1]}'}) mw.log(p._main{jp='{"iname":"TS_ENVYRIA_GERALD_01","type":1,"icon":"TS_ENVYRIA_GERALD_01","rare":4,"sell":1000,"coin_item":100,"en_cost":2000,"en_exp":20000,"birth_id":1,"leader_skill":"SK_LS_TS_ENVYRIA_GERALD_01","concept_card_groups":["TS_SUBGROUP_HIENKISHI"],"effects":[{"cnds_iname":"CARD_CNDS_ENVIRIA","card_skill":"SK_GS1_ENVYRIA_GERALD_01","add_card_skill_buff_awake":"BUFF_GS1_ENVYRIA_GERALD_01_BONUS_01"},{"cnds_iname":"CARD_CNDS_UROBOROS_2","card_skill":"SK_GS1_ENVYRIA_GERALD_01","add_card_skill_buff_awake":"BUFF_GS1_ENVYRIA_GERALD_01_BONUS_01"},{"cnds_iname":"CARD_CNDS_HIEN_NIGHT","card_skill":"SK_GS2_ENVYRIA_GERALD_01","add_card_skill_buff_awake":"BUFF_GS2_ENVYRIA_GERALD_01_BONUS_01"},{"cnds_iname":"CARD_CNDS_UROBOROS_2","card_skill":"SK_GS2_ENVYRIA_GERALD_01","add_card_skill_buff_awake":"BUFF_GS2_ENVYRIA_GERALD_01_BONUS_01"},{"cnds_iname":"CARD_CNDS_GERALD_UROB","card_skill":"SK_GS3_ENVYRIA_GERALD_01","add_card_skill_buff_awake":"BUFF_GS3_ENVYRIA_GERALD_01_BONUS_01","add_card_skill_buff_lvmax":"BUFF_GS3_ENVYRIA_GERALD_01_BONUS_02"},{"statusup_skill":"SK_ENVYRIA_GERALD_01"},{"cnds_iname":"CARD_CNDS_GERALD_UROB01","abil_iname":"AB_ENVYRIA_GERALD_01_VA","abil_iname_lvmax":"AB_ENVYRIA_GERALD_01_VA_MAX"},{"cnds_iname":"CARD_CNDS_GERALD_UROB","abil_iname":"AB_ENVYRIA_GERALD_01_MAX_SPD_VA_NON","abil_iname_lvmax":"AB_ENVYRIA_GERALD_01_MAX_SPD_VA"}],"trust_item_names":[],"trust_item_counts":[],"trust_artifact_names":["AF_ARMO_GERALD_01"],"trust_artifact_counts":[1]}'}) -- local card = Card.new{} -- card:parseBuff('BUFF_GS1_LOST_ZWEI_01_BONUS_01') end

return p