来自魔法纪录中文Wiki
跳转至: 导航 搜索
# coding=utf-8
# get json from: https://gitlab.com/EnkanRec/magireco-data-json/-/tree/master/
# version beta-231219
import json

MEMORIA_JSON = "memoria.json"
CHARA_JSON = "charaCard.json"
OUTPUT = "wikitext.txt"
WD = ""
ABBR = {"CHARA_SKILL": "S", "CHARA_ABILITY": "A", "CHARA_CONNECT": "C", "CHARA_MAGIA": "M", "CHARA_DOPPEL": "D"}
CHARA_NAME_DICT = {1001: "环伊吕波",1002: "七海八千代",1003: "由比鹤乃",1004: "二叶莎奈",1005: "深月菲莉希亚",1006: "梓美冬",1007: "里见灯花",1008: "阿莉娜·格雷",1009: "水波玲奈",1010: "十咎桃子",1011: "秋野枫",1012: "御园花凛",1013: "龙城明日香",1014: "柊音梦",1015: "环忧",1016: "和泉十七夜",1017: "八云御魂",1018: "天音月夜",1019: "天音月咲",1020: "佐鸟笼目",1021: "红晴结菜",1022: "煌里光",1023: "笠音青",1024: "大庭树里",1025: "时女静香",1026: "广江千春",1027: "土岐沙绪",1028:"蓝家姬奈",1029: "宫尾时雨",1030: "安积育梦",1031:"神乐灿",1032:"游狩美由利",1033:"冰室拉比", 1034:"三浦旭", 1035:"栗栖亚历山德拉", 1036:"有爱丽", 1037:"里见那由他",1038: "八云御影",1039: "佐和月出里",1040: "篠目夜鹤",1041: "莉薇娅·梅黛洛斯",1042: "小丘比",1043: "黑江",1044: "濑奈命",1045: "水名露",1046: "千鹤",1047: "小黑",1048: "埃博妮",1049: "奥尔加",1050: "冈希尔德",1051: "赫露迦",1052: "台与",1053: "阿玛琉莉丝",1101: "环伊吕波(泳装ver.)",1102: "八千代·美冬(起始ver.)",1103: "谣鹤乃",1104: "谣莎奈",1105: "小菲莉希亚",1107: "灯花·音梦(圣夜ver.)",1108: "圣阿莉娜",1109: "小玲奈(偶像ver.)",1110: "十咎桃子(修女ver.)",1112: "花凛·阿莉娜(万圣ver.)",1116: "和泉十七夜(吸血鬼ver.)",1117: "八云御魂(晴着ver.)",1118: "天音姐妹(泳装ver.)",1120: "佐鸟笼目(百鬼夜行ver.)",1121: "结菜·树里(吸血鬼ver.)",1125: "时女静香(元旦日出ver.)",1133: "冰室拉比(心魔ver.)",1137: "那由他·御影(圣诞ver.)",1139: "佐和月出里(情人节ver.)",1143: "黑江(泳装ver.)",1144: "谣命",1201: "小伊吕波",1202: "七海八千代(七夕ver.)",1203: "鹤乃·菲莉希亚(快递ver.)",1209: "玲奈·枫(泳装ver.)",1210: "桃子·御魂(人鱼ver.)",1216: "和泉十七夜(常暗ver.)",1217: "八云御魂(常暗ver.)",1301: "伊吕波·八千代(决战ver.)",1302: "七海八千代(动画ver.)",1303: "谣鹤乃(动画ver.)",1309: "水波玲奈(动画ver.)",1401: "伊吕波·忧(巫女ver.)",1501: "环伊吕波(动画ver.)",1502: "七海八千代(历史ver.)",1601: "∞伊吕波",1701: "无限大小伊吕波",2001: "鹿目圆",2002: "晓美焰",2003: "晓美焰(眼镜ver.)",2004: "美树沙耶香",2005: "巴麻美",2006: "佐仓杏子",2007: "百江渚",2009: "爱生眩",2100: "鹿目圆(晴着ver.)",2101: "圆神",2102: "小圆前辈",2103: "究极小圆前辈",2104: "圆·伊吕波",2105: "鹿目圆(scene0 ver.)",2106: "鹿目圆(泳装ver.)",2201: "恶魔小焰",2202: "恶魔焰",2203: "晓美焰(晴着ver.)",2300: "晓美焰(泳装ver.)",2400: "美树沙耶香(晴着ver.)",2401: "美树沙耶香(冲浪ver.)",2402: "美树沙耶香(scene0 ver.)",2500: "圣麻美",2501: "巴麻美(泳装ver.)",2502: "圣麻美(动画ver.)",2600: "佐仓杏子(泳装ver.)",2601: "佐仓杏子(scene0 ver.)",2602: "佐仓杏子(魔女化身ver.)",2700: "百江渚(情人节ver.)",2701: "百江渚(泳装ver.)",3001: "矢宵鹿乃子",3002: "空穗夏希",3003: "都雏乃",3004: "美凪纱纱罗",3005: "常盘七夏",3006: "木崎衣美里",3007: "保澄雫",3008: "志伸晶",3009: "胡桃爱香",3010: "阿见莉爱",3011: "夏目佳子",3012: "纯美雨",3013: "伊吹丽良",3014: "桑水清佳",3015: "相野未都",3016: "粟根心",3017: "七濑幸佳",3018: "更纱帆奈",3019: "毬子彩花",3020: "真尾日美香",3021: "铃鹿朔夜",3022: "伊并满",3023: "江利爱实",3024: "若菜纺",3025: "五十铃怜",3026: "静海木叶",3027: "游佐叶月",3028: "三栗菖蒲",3029: "加贺见真良",3030: "春名木乃美",3031: "绫野梨花",3032: "梢麻友",3033: "史乃沙优希",3034: "惠萌花",3035: "千秋理子",3036: "由贵真里爱",3037: "安名梅露",3038: "古町美仓",3039: "三穗野星罗",3040: "吉良手鞠",3041: "柚希步鸟",3042: "枇枇木巡",3043: "万年樱之谣",3044: "智珠兰华",3045: "柚希理音",3046: "观鸟令",3047: "青叶知花",3048: "由良萤",3049: "雪野加奈惠",3050: "香春优奈",3051: "饰利润",3052: "阿什莉·泰勒",3053: "牧野郁美",3054: "三轮光音",3055:"桐野纱枝",3056: "水树垒",3057: "真井灯", 3058: "南津凉子",3059: "入名库什",3501: "梨花·怜(圣诞ver.)",3502: "万年樱之谣(泳装ver.)",3503:"木叶·叶月",3504:"真良·心(花嫁ver.)",3900: "黑",4001: "美国织莉子",4002: "吴纪里香",4003: "千岁由麻",4004: "美国织莉子(ver.Final)",4011: "和美",4012: "御崎海香",4013: "牧薰",4014: "昴和美",4021: "塔鲁特",4022: "莉兹",4023: "梅丽莎",4024: "米诺",4025: "可鲁波",4026: "爱丽莎",4027: "拉皮努",4028: "塔鲁特(ver.Final)",4029: "佩尔内勒",4121: "伊莎贝拉(魔女ver.)",4122:"伊莎贝拉",4031: "天乃铃音",4032: "日向茉莉",4033: "成见亚里纱",4034: "诗音千里",4035: "奏遥香",4036:"美琴椿", 4041: "战场原黑仪",4042: "八九寺真宵",4043: "神原骏河",4044: "千石抚子",4045: "羽川翼",4046: "忍野忍",4051: "高町奈叶",4052: "菲特",4053: "八神疾风",4061: "锦木千束", 4062: "井之上泷奈"}
EXCLUDE_ID_LIST = []


def get_from_dict(d_ict, key, default=None):
    '''从dict中根据key取值,取不到返回default'''
    if key not in d_ict:
        return default
    return d_ict[key]

def chara_fix(chara_json, memo_json):
    '''根据ID获得烧酒数据'''
    new_dict = {}
    new_chara_json = {} #chara_json.copy()
    def get_skill_data(data, art_list, chara_id, suffix, type):
        new_piece = {}
        for idx in range(len(art_list)):
            art_name = "art%d"%idx
            artid_name = "artId%d"%idx
            if "effectValue" not in art_list[idx] or art_list[idx]["effectValue"] == 0:
                art_list[idx]["effectValue"] = 1
            new_piece[art_name] = art_list[idx]
            new_piece[artid_name] = art_list[idx]["artId"]
            idx += 1
        c_id = chara_id + suffix
        new_data = data.copy()
        new_data["pieceType"] = type
        new_data["pieceId"] = c_id
        new_data["pieceSkill2"] = new_piece
        return new_data

    for chara_id, data in chara_json.items():
        if chara_id in EXCLUDE_ID_LIST:
            continue
        c_id = "CHARA_" + chara_id
        data["pieceId"] = c_id
        data["pieceType"] = "CHARA"
        data["pieceName"] = data["name"]
        title = get_from_dict(data, "title")
        if title != None:
            data["pieceName"] += "(%s)"%title
        art_list = []
        active_art_list = []
        passive_art_list = []
        connect_art_list = []
        magia_art_list = []
        doppel_art_list = []

        for evo_idx in range(5, 0, -1):
            evo_key = "evolutionCard%d"%evo_idx
            if evo_key in data or evo_idx == 1:
                if evo_key not in data:
                    max_data = data["defaultCard"]
                else:
                    max_data = data[evo_key]
                data["rank"] = max_data["rank"]
                
                # connect
                if "cardSkill" in max_data:
                    connect_data = max_data["cardSkill"]
                    for art_idx in range(1, 100):
                        art_key = "art%d"%art_idx
                        if art_key not in connect_data:
                            break
                        if get_from_dict(connect_data[art_key], "targetId") == "LIMITED":
                            continue
                        connect_art_list.append(connect_data[art_key])
                
                # Magia
                if "cardMagia" in max_data:
                    magia_data = max_data["cardMagia"]
                    for art_idx in range(1, 100):
                        art_key = "art%d"%art_idx
                        if art_key not in magia_data:
                            break
                        art = magia_data[art_key]
                        if "growPoint" in art and "effectValue" in art:
                            art["effectValue"] += art["growPoint"] * 4
                        magia_art_list.append(art)
                
                # Doppel
                if "doppelCardMagia" in max_data:
                    doppel_data = max_data["doppelCardMagia"]
                    for art_idx in range(1, 100):
                        art_key = "art%d"%art_idx
                        if art_key not in doppel_data:
                            break
                        art = doppel_data[art_key]
                        if "growPoint" in art and "effectValue" in art:
                            art["effectValue"] += art["growPoint"] * 4
                        doppel_art_list.append(art)
                
                # EX
                if "maxPieceSkillList" in max_data:
                    ex_data_list = max_data["maxPieceSkillList"]
                    for ex_data in ex_data_list:
                        for art_idx in range(1, 100):
                            art_key = "art%d"%art_idx
                            if art_key not in ex_data:
                                break
                            passive_art_list.append(ex_data[art_key])
                
                break

        # 精神强化
        if "enhancementCellList" in data:
            enhance_list = data["enhancementCellList"]
            for enhance_cell in enhance_list:
                if enhance_cell["enhancementType"] == "SKILL":
                    enhance_skill = enhance_cell["emotionSkill"]
                    if get_from_dict(enhance_skill, "type") == "SKILL":
                        for art_idx in range(1, 100):
                            art_key = "art%d"%art_idx
                            if art_key not in enhance_skill:
                                break
                            active_art_list.append(enhance_skill[art_key])
                    else:
                        for art_idx in range(1, 100):
                            art_key = "art%d"%art_idx
                            if art_key not in enhance_skill:
                                break
                            passive_art_list.append(enhance_skill[art_key])

        piece = {}
        for idx in range(len(art_list)):
            art_name = "art%d"%idx
            artid_name = "artId%d"%idx
            piece[art_name] = art_list[idx]
            piece[artid_name] = art_list[idx]["artId"]
        data["pieceSkill2"] = piece

        active_data = get_skill_data(data, active_art_list, c_id, "_ACTIVE", "CHARA_SKILL")
        new_dict[active_data["pieceId"]] = active_data

        passive_data = get_skill_data(data, passive_art_list, c_id, "_PASSIVE", "CHARA_ABILITY")
        new_dict[passive_data["pieceId"]] = passive_data

        connect_data = get_skill_data(data, connect_art_list, c_id, "_CONNECT", "CHARA_CONNECT")
        new_dict[connect_data["pieceId"]] = connect_data

        magia_data = get_skill_data(data, magia_art_list, c_id, "_MAGIA", "CHARA_MAGIA")
        new_dict[magia_data["pieceId"]] = magia_data
        
        doppel_data = get_skill_data(data, doppel_art_list, c_id, "_DOPPEL", "CHARA_DOPPEL")
        new_dict[doppel_data["pieceId"]] = doppel_data

        if memo_json is not None:
            memo_json[c_id] = data
            memo_json[active_data["pieceId"]] = active_data
            memo_json[passive_data["pieceId"]] = passive_data
            memo_json[connect_data["pieceId"]] = connect_data
            memo_json[magia_data["pieceId"]] = magia_data
            memo_json[doppel_data["pieceId"]] = doppel_data
    
    for k, v in new_dict.items():
        new_chara_json[k] = v

    return new_chara_json

def read_json(fname):
    f = open(WD + fname, 'r', encoding='utf-8')
    data = f.read()
    f.close()
    return json.loads(data)

def write_wiki(fname, text):
    f = open(WD + fname, 'w', encoding='utf-8')
    f.write(text)
    f.close()

def table_header(columns, column_certain=False, percentage=True, icon_width='30px', table_style=None):
    text = '{| class="wikitable"'
    if table_style: text += ' style="%s"' % table_style
    text += '\n'
    text += table_caption(columns, column_certain=column_certain, percentage=percentage, firstRow=True, icon_width=icon_width)
    return text

def table_hr(columns, icon_width='30px'):
    text = '|-\n'
    text += '| style="width: %s" colspan="%d" | \n' % (icon_width, len(columns)+1)
    return text

def table_caption(columns, column_certain=False, percentage=True, firstRow=False, icon_width='30px'):
    text = '' if firstRow else '|-\n'
    text += '! style="width: %s" | !! 技能' % icon_width
    if column_certain:
        text += ' !! 必定'
    for i in columns:
        if percentage:
            text += ' !! %g%%' % (i/10)
        else:
            text += ' !! %g' % (i/10)
    text += ' !! 其他\n'
    return text

def memoriaSort(m):
    if type(m) == type(()): m = m[1]
    art = m['art']
    a = ['ABILITY', 'SKILL', 'CHARA', 'CHARA_SKILL', 'CHARA_ABILITY', 'CHARA_CONNECT', 'CHARA_MAGIA', 'CHARA_DOPPEL'].index(m['pieceType'])
    b = ['ALL', 'TARGET', 'SELF', 'CONNECT', 'ONE', 'DYING', 'LIMITED'].index(art['targetId'])
    c = -get_from_dict(art, "enableTurn", 1)
    d = -get_from_dict(art, "effectValue", 0)
    e = ['RANK_5', 'RANK_4', 'RANK_3', 'RANK_2', 'RANK_1'].index(m['rank'])
    return e + 6 * (d + 1500*(c + 10*(b + 8*a)))

def table_row(icon, name, data, column_certain=False, bad_target=None, mark_chara=False):
    text = '|-\n| '
    if len(icon) > 0:
        if type(icon) != type([]):
            text += '[[文件:%s|30px]]' % icon
        else:
            for i in icon:
                text += '[[文件:%s|30px]]' % i
    text += '\n| '
    if len(name) > 0: text += name
    for cell in data:
        text += '\n| '
        # if len(cell) > 0:
            # text += 'style="padding: 0;" | '
        cell.sort(key=memoriaSort)
        for memo in cell:
            rubyValue = None
            if type(memo) == type(()):
                rubyValue = memo[0]
                memo = memo[1]
            if get_from_dict(memo, "pieceType") in ["SKILL", "ABILITY"]:
                uText = '{{记忆数据表/%s|icon-small}}' % get_name(memo)
            else:
                sub = get_from_dict(ABBR, get_from_dict(memo, "pieceType"), "")
                uText = '{{IPic|%s|%s}}' % (get_chara_pic(memo), sub)
            style = ''
            boxShadow = []
            if get_from_dict(memo['art'], 'enableTurn', 1) > 1:
                boxShadow.append('2px 2px 6px #00f')
            if memo['art']['targetId'] == bad_target:
                boxShadow.append('margin: 2px; box-shadow: 2px 0 15px #000, 0 2px 15px #000; ')
            if memo['art']['targetId'] == 'ALL':
                boxShadow.append('0 2px 15px #f80, 0 -2px 15px #f80, -2px 0 15px #f80, 2px 0 15px #f80')
            if len(boxShadow) > 0:
                style += 'margin: 2px; box-shadow: %s; ' % (', '.join(boxShadow))
            if 'charaList' in memo.keys():
                style += 'border: 2px solid #0f0;'
            if style != '':
                uText = '<div style="display: inline-block; width: 35px; height: 35px; %s">%s</div>' % (style, uText)
            if not rubyValue and mark_chara and 'charaList' in memo.keys():
                rubyValue = '、'.join([c['name'] for c in memo['charaList']])
            if rubyValue:
                text += '{{Ruby|1=%s|2=%s}}' % (uText, rubyValue)
            else:
                text += uText
    text += '\n'
    return text

def get_name(memo):
    if memo['pieceId'] == 1001:
        return memo['pieceName'] + ' (1)'
    elif memo['pieceId'] == 1002:
        return memo['pieceName'] + ' (2)'
    else:
        return memo['pieceName']

def get_chara_pic(chara):
    chara_id = get_from_dict(chara, "id", 0)
    if chara_id == 0:
        return "{{角色数据表/%s|icon|35px}}" % (get_name(chara))
    chara_zh_name = get_from_dict(CHARA_NAME_DICT, chara_id, None)
    if chara_zh_name is None:
        return "{{角色数据表/%s|icon|35px}}" % (get_name(chara))
    final_rank = get_from_dict(chara, "rank", "")
    if len(final_rank) < 1:
        return "{{角色数据表/%s|icon|35px}}" % (get_name(chara))
    return "[[File:%s%s星头像.png|35px|link=%s]]" % (chara_zh_name, final_rank[-1:], chara_zh_name)

def table_footer():
    return '|}\n'

def check(memo, condition):
    if 'pieceSkill2' not in memo.keys(): return False
    if 'art' in memo:
        del memo['art']
    success = False
    checked_art_list = []
    for key, art in memo['pieceSkill2'].items():
        if key[:3] == 'art' and key[:5] != 'artId':
            checked = True
            for cKey, cValue in condition.items():
                if cKey != 'EXCEPT':
                    if get_from_dict(art, cKey, None) != cValue:
                        checked = False
                else:
                    for cKey2, cValue2 in cValue.items():
                        if isinstance(cValue2, str):
                            if cKey2 in art.keys() and art[cKey2] == cValue2:
                                checked = False
                        elif isinstance(cValue2, list):
                            if cKey2 in art.keys() and art[cKey2] in cValue2:
                                checked = False
            if checked:
                checked_art_list.append(art)
                success = True
    memo_list = []
    if success:
        checked_size = len(checked_art_list)
        if checked_size == 1:
            memo['art'] = checked_art_list[0]
            memo_list.append(memo)
        else:
            sample_art = checked_art_list[0]

            merge_effect_value = get_from_dict(sample_art, "verbCode") in ["BUFF", "DEBUFF", "INITIAL", "BUFF_DYING"]
            merge_effect_value |= (get_from_dict(sample_art, "verbCode") == "IGNORE" 
                and get_from_dict(sample_art, "effectCode") in ["DEBUFF", "CONDITION_BAD", "CRITICAL"])
            merge_effect_value |= (get_from_dict(sample_art, "verbCode") == "CONDITION_GOOD" 
                and get_from_dict(sample_art, "effectCode") in ["MP_PLUS_DAMAGED", "MP_PLUS_WEAKED", "MP_PLUS_BLAST", "DAMAGE_UP_BAD"])

            if merge_effect_value:
                print_art = sample_art.copy()
                effect = get_from_dict(print_art, "effectValue", 0)
                for idx in range(1, checked_size):
                    art = checked_art_list[idx]
                    effect += get_from_dict(art, "effectValue", 0)
                print_art["effectValue"] = effect
                memo['art'] = print_art
                memo_list.append(memo)
            else:
                for art in checked_art_list:
                    memo_copy = memo.copy()
                    memo_copy['art'] = art
                    memo_list.append(memo_copy)
    return (success, memo_list)

def select(baseMemoria, pieceType, mFilters):
    memoria = []
    for k, v in baseMemoria.items():
        if pieceType:
            if 'pieceType' in v.keys() and v['pieceType']==pieceType:
                check_result = check(v, mFilters)
                if check_result[0]:
                    memoria += check_result[1]
        else:
            check_result = check(v, mFilters)
            if check_result[0]:
                memoria += check_result[1]
    return memoria

def separate(baseMemoria, columnKey, columnValues, percentage=True):
    data = []
    if columnKey == 'probability':
        cell = [memo for memo in baseMemoria if memo['art']['probability'] == 1000]
        baseMemoria[:] = [memo for memo in baseMemoria if memo not in cell]
        data.append(cell)
    for v in columnValues:
        cell = [memo for memo in baseMemoria if memo['art'][columnKey] == v]
        baseMemoria[:] = [memo for memo in baseMemoria if memo not in cell]
        data.append(cell)
    if percentage:
        cell = [('%g%%' % (memo['art'][columnKey]/10), memo) for memo in baseMemoria]
    else:
        cell = [('%g' % (memo['art'][columnKey]/10), memo) for memo in baseMemoria]
    data.append(cell)
    return data

def png(num):
    return "Icon skill %g.png" % num

def active_ailments(memoria, effectCode, name, icon, columns):
    data = separate(select(memoria, None, {"verbCode": "CONDITION_BAD", "effectCode": effectCode}), 'probability', columns)
    return table_row(icon, name, data, column_certain=True, bad_target='SELF')

def passive_ailments(memoria, effectCode, name, icon, columns):
    base = select(memoria, None, {"verbCode": "ENCHANT", "effectCode": effectCode})
    data = separate(base, 'probability', columns)
    return table_row(icon, name, data, column_certain=True)

def ignore(memoria, effectCode, name, icon, columns, percentage=True):
    data = separate(select(memoria, None, {"verbCode": "IGNORE", "effectCode": effectCode}), 'probability', columns, percentage=percentage)
    return table_row(icon, name, data, column_certain=True, bad_target='TARGET')

def ignore_v(memoria, effectCode, name, icon, columns, percentage=True):
    data = separate(select(memoria, None, {"verbCode": "IGNORE", "effectCode": effectCode}), 'effectValue', columns, percentage=percentage)
    return table_row(icon, name, data, column_certain=True, bad_target='TARGET')

def condition_good_p(memoria, effectCode, name, icon, columns, percentage=True, pieceType=''):
    data = separate(select(memoria, pieceType, {"verbCode": "CONDITION_GOOD", "effectCode": effectCode}), 'probability', columns, percentage=percentage)
    return table_row(icon, name, data, column_certain=True, bad_target='TARGET')

def condition_good_v(memoria, effectCode, name, icon, columns, percentage=True, pieceType=''):
    data = separate(select(memoria, pieceType, {"verbCode": "CONDITION_GOOD", "effectCode": effectCode}), 'effectValue', columns, percentage=percentage)
    return table_row(icon, name, data, bad_target='TARGET')

def buff(memoria, effectCode, name, icon, columns, pieceType=''):
    data = separate(select(memoria, pieceType, {"verbCode": "BUFF", "effectCode": effectCode}), 'effectValue', columns)
    return table_row(icon, name, data, bad_target='TARGET')

def debuff(memoria, effectCode, name, icon, columns, pieceType=''):
    data = separate(select(memoria, pieceType, {"verbCode": "DEBUFF", "effectCode": effectCode}), 'effectValue', columns)
    return table_row(icon, name, data, bad_target='SELF')

def draw(memoria, effectCode, name, icon):
    data = separate(select(memoria, None, {"verbCode": "DRAW", "effectCode": effectCode}), 'probability', [])
    return table_row(icon, name, data, column_certain=True, mark_chara=True)

def sanityCheck(memo):
    try:
        if 'pieceId' not in memo.keys():
            raise Exception('Unexpected: pieceId key not found in:', memo)
        if 'pieceName' not in memo.keys():
            raise Exception('Unexpected: pieceName key not found in:', memo)
        pieceId = memo['pieceId']
        pieceName = memo['pieceName']
        if 'pieceType' not in memo.keys():
            raise Exception('Unexpected: pieceType key not found in %g %s' % (pieceId, pieceName))
        pieceType = memo['pieceType']
        if pieceType != 'SKILL' and pieceType != 'ABILITY':
            raise Exception('Unexpected: pieceType value %s in %g %s' % (pieceType, pieceId, pieceName))
        if 'pieceSkill2' not in memo.keys():
            raise Exception('Unexpected: pieceSkill2 key not found in %g %s' % (pieceId, pieceName))
        hasArt = False
        for key, art in memo['pieceSkill2'].items():
            if key[:3] == 'art' and key[:5] != 'artId':
                hasArt = True
                if 'verbCode' not in art.keys():
                    raise Exception('Unexpected: missing verbCode for %s in %g %s' % (key, pieceId, pieceName))
                if 'effectCode' not in art.keys():
                    raise Exception('Unexpected: missing effectCode for %s in %g %s' % (key, pieceId, pieceName))
                verbCode = art['verbCode']
                effectCode = art['effectCode']
                if verbCode == 'BUFF' or verbCode == 'DEBUFF' or verbCode == 'BUFF_HPMAX' or verbCode == 'BUFF_DYING' or verbCode == 'BUFF_DIE':
                    if effectCode not in ['ATTACK', 'DEFENSE', 'DAMAGE', 'ACCEL', 'BLAST', 'CHARGE', 'MAGIA', 'MP_GAIN', 'RESIST', 'ATTACK_FIRE']:
                        raise Exception('Unexpected: %s %s for %s in %g %s' % (verbCode, effectCode, key, pieceId, pieceName))
                    if verbCode == 'DEBUFF' and effectCode == 'CHARGE':
                        raise Exception('Report: %s %s realized for %s in %g %s' % (verbCode, effectCode, key, pieceId, pieceName))
                    if verbCode in ['BUFF_HPMAX', 'BUFF_DYING', 'BUFF_DIE'] and effectCode not in ['ATTACK', 'DEFENSE']:
                        raise Exception('Report: %s %s realized for %s in %g %s' % (verbCode, effectCode, key, pieceId, pieceName))
                elif verbCode == 'CONDITION_BAD' or verbCode == 'ENCHANT':
                    if effectCode not in ["CHARM", "STUN", "RESTRAINT", "POISON", "BURN", "CURSE", "FOG", "DARKNESS", "BLINDNESS", "BAN_SKILL", "BAN_MAGIA"]:
                        raise Exception('Unexpected: %s %s for %s in %g %s' % (verbCode, effectCode, key, pieceId, pieceName))
                    if verbCode == 'CONDITION_BAD' and pieceType == 'ABILITY':
                        raise Exception('Unexpected: ABILITY (passive) CONDITION_BAD %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                    if verbCode == 'ENCHANT' and pieceType == 'SKILL':
                        raise Exception('Unexpected: SKILL (active) ENCHANT %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'CONDITION_GOOD':
                    if effectCode not in ["DAMAGE_UP", "DAMAGE_UP_BAD", "DAMAGE_DOWN", "DAMAGE_DOWN_BLAST", "DAMAGE_DOWN_NODISK", "CRITICAL", "DEFENSE_IGNORED", "AVOID", "COUNTER", "PROVOKE",  "PROTECT", "PURSUE", "GUTS", "SKILL_QUICK", "C_COMBO_PLUS", "AUTO_HEAL", "MP_PLUS_WEAKED", "MP_PLUS_BLAST"]:
                        raise Exception('Unexpected: CONDITION_GOOD %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'IGNORE':
                    if effectCode not in ["DAMAGE_DOWN", "AVOID", "COUNTER", "PROVOKE", "CHARM", "STUN", "RESTRAINT", "POISON", "BURN", "CURSE", "FOG", "DARKNESS", "BLINDNESS", "BAN_SKILL", "BAN_MAGIA", "DEBUFF"]:
                        raise Exception('Unexpected: IGNORE %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'HEAL':
                    if effectCode not in ['HP', 'MP', 'MP_DAMAGE']:
                        raise Exception('Unexpected: HEAL %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                    if effectCode == 'HP' and art['targetId'] not in ["SELF", "ONE"]:
                        raise Exception('Unexpected: HEAL HP %s for %s in %g %s' % (art['targetId'], key, pieceId, pieceName))
                    if pieceType == 'ABILITY':
                        raise Exception('Unexpected: ABILITY (passive) HEAL %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'DRAW':
                    if effectCode not in ["AGAIN", "CHARACTER", "ACCEL", "BLAST", "CHARGE", "ALIGNMENT"]:
                        raise Exception('Unexpected: DRAW %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                    if pieceType == 'ABILITY':
                        raise Exception('Unexpected: ABILITY (passive) DRAW %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'REVOKE':
                    if effectCode not in ["BAD", "DEBUFF", "BUFF", "CONDITION_GOOD"]:
                        raise Exception('Unexpected: REVOKE %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                    if pieceType == 'ABILITY':
                        raise Exception('Unexpected: ABILITY (passive) REVOKE %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'INITIAL':
                    if effectCode != "MP":
                        raise Exception('Unexpected: INITIAL %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                elif verbCode == 'OTHER':
                    if effectCode not in ['EPISODE_UP', 'GOLD_UP']:
                        raise Exception('Unexpected: OTHER %s for %s in %g %s' % (effectCode, key, pieceId, pieceName))
                else:
                    raise Exception('Unexpected: %s %s for %s in %g %s' % (verbCode, effectCode, key, pieceId, pieceName))
    except Exception as e:
        if len(e.args) == 2:
            print(e.args[0])
            print(e.args[1])
        else:
            print(e)

def main():
    memoria = read_json(CHARA_JSON)
    memoria = chara_fix(memoria, None)

    wikitext = ""

    wikitext += "\n===攻击系===\n"
    columns = [700, 600, 550, 400, 350, 300, 200, 150]#range(500, 150-50, -50)
    wikitext += table_header(columns, column_certain=True)
    wikitext += condition_good_p(memoria, "CRITICAL", "暴击", png(1121), columns)
    wikitext += condition_good_p(memoria, "DEFENSE_IGNORED", "无视防御", png(1123), columns)
    columns = [800, 600, 450, 400]#range(800, 450-50, -50)
    wikitext += table_caption(columns, column_certain=True)
    wikitext += ignore(memoria, "DAMAGE_DOWN", "无视伤害削减", png(1177), columns)
    wikitext += table_footer()
    
    columns = range(500, 50-50, -50)
    wikitext += table_header(columns)
    wikitext += buff(memoria, "ATTACK", "攻击力UP", png(1085), columns, pieceType=None)
    columns = [475, 450, 425, 400, 350, 300, 200, 150, 100, 50] # range(400, 100-50, -50)
    wikitext += table_caption(columns)
    wikitext += buff(memoria, "DAMAGE", "造成伤害UP", png(1087), columns, pieceType=None)
    wikitext += condition_good_v(memoria, "DAMAGE_UP", "伤害上升状态", png(1119), columns, pieceType=None)
    wikitext += condition_good_v(memoria, "DAMAGE_UP_BAD", "敌方状态异常时伤害上升", png(1172), columns)
    wikitext += table_row(png(1213), "对魔女伤害上升", separate(select(memoria, '', {"verbCode": "LIMITED_ENEMY_TYPE", "effectCode": "DAMAGE_UP", "genericValue": "WITCH"}), 'effectValue', columns, percentage=True))
    wikitext += table_footer()

    columns = range(500, 150-50, -50)
    wikitext += table_header(columns)
    wikitext += debuff(memoria, "DEFENSE", "防御力DOWN", png(1095), columns)
    wikitext += table_footer()
    
    columns = [180,110,50]
    wikitext += table_header(columns)
    wikitext += debuff(memoria, "WEAK_WATER", "水属性耐性DOWN", png(1226), columns)
    wikitext += debuff(memoria, "WEAK_LIGHT", "光属性耐性DOWN", png(1228), columns)
    wikitext += debuff(memoria, "WEAK_DARK", "暗属性耐性DOWN", png(1230), columns)
    wikitext += debuff(memoria, "WEAK_TIMBER", "木属性耐性DOWN", png(1238), columns)
    wikitext += debuff(memoria, "WEAK_FIRE", "火属性耐性DOWN", png(1243), columns)
    wikitext += debuff(memoria, "WEAK_BLAST", "受Blast伤害UP", png(1222), columns)
    wikitext += debuff(memoria, "WEAK_CHARGE_DONE", "受Charge后伤害UP", png(1223), columns)
    wikitext += table_footer()
    
    columns = [650, 600, 550, 500, 450, 400, 350, 300, 250, 200, 165, 150] # range(600, 200-50, -50)
    wikitext += table_header(columns)
    wikitext += buff(memoria, "BLAST", "Blast伤害UP", png(1092), columns)
    columns = range(400, 100-25, -25)
    wikitext += table_caption(columns)
    wikitext += buff(memoria, "CHARGE", "Charge后伤害UP", png(1091), columns)
    columns = range(500, 100-50, -50)
    wikitext += table_caption(columns)
    wikitext += buff(memoria, "CHARGING", "Charge盘伤害UP", png(1214), columns)
    wikitext += table_footer()

    columns = range(375, 75-25, -25)
    wikitext += table_header(columns)
    wikitext += buff(memoria, "MAGIA", "Magia伤害UP", png(1088), columns)
    wikitext += buff(memoria, "DOPPEL", "Doppel伤害UP", png(1220), columns)
    wikitext += table_footer()

    columns = [600, 300, 200, 150, 100, 50]
    wikitext += table_header(columns)
    wikitext += table_row(png(1085), "HP最大时攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF_HPMAX", "effectCode": "ATTACK"}), 'effectValue', columns), bad_target='TARGET')
    wikitext += table_row(png(1241), "濒死时攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF_DYING", "effectCode": "ATTACK"}), 'effectValue', columns), bad_target='TARGET')
    wikitext += table_row(png(1085), "队友战斗不能时获得攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF_PARTY_DIE", "effectCode": "ATTACK"}), 'effectValue', columns))
    columns = [450, 400, 350, 300, 250, 200, 150, 75, 50]
    wikitext += table_caption(columns)
    wikitext += table_row(png(1233), "火属性攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF", "effectCode": "ATTACK_FIRE"}), 'effectValue', columns))
    wikitext += table_row(png(1236), "水属性攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF", "effectCode": "ATTACK_WATER"}), 'effectValue', columns))
    wikitext += table_row(png(1237), "木属性攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF", "effectCode": "ATTACK_TIMBER"}), 'effectValue', columns))
    wikitext += table_row(png(1235), "光属性攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF", "effectCode": "ATTACK_LIGHT"}), 'effectValue', columns))
    wikitext += table_row(png(1234), "暗属性攻击力UP", separate(select(memoria, '', {"verbCode": "BUFF", "effectCode": "ATTACK_DARK"}), 'effectValue', columns))
    wikitext += table_footer()
    
    wikitext += table_header([30000], percentage=False)
    wikitext += debuff(memoria, "ADD_DAMAGE", "受伤害上升", png(1247), [3000])
    wikitext += table_footer()
    
    wikitext += "\n===防守系===\n"
    columns = [1050, 550, 475, 450, 375, 225, 150, 75] # range(600, 150-75, -75)
    wikitext += table_header(columns)
    wikitext += buff(memoria, "DEFENSE", "防御力UP", png(1086), columns, pieceType=None)

    columns = range(500, 100-50, -50)
    wikitext += table_caption(columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN", "伤害削减", png(1120), columns, pieceType=None)
    wikitext += debuff(memoria, "ATTACK", "攻击力DOWN", png(1094), columns)
    wikitext += debuff(memoria, "DAMAGE", "造成伤害DOWN", png(1096), columns)
    wikitext += debuff(memoria, "MAGIA", "Magia伤害DOWN", png(1097), columns)
    wikitext += table_footer()

    columns = range(450, 100-50, -50)
    wikitext += table_header(columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_ACCEL", "Accele伤害削减", png(1201), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_BLAST", "Blast伤害削减", png(1202), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_NODISK", "Magia伤害削减状态", png(1205), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_FIRE", "火属性伤害削减", png(1206), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_WATER", "水属性伤害削减", png(1207), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_TIMBER", "木属性伤害削减", png(1208), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_LIGHT", "光属性伤害削减", png(1209), columns)
    wikitext += condition_good_v(memoria, "DAMAGE_DOWN_DARK", "暗属性伤害削减", png(1210), columns)
    columns = range(350, 200-50, -50)
    wikitext += table_caption(columns)
    wikitext += debuff(memoria, "BLAST", "Blast伤害DOWN", png(1101), columns)
    columns =[400, 300, 200, 100, 76]
    wikitext += table_caption(columns)
    wikitext += table_row(png(1086), "HP最大时防御力UP", separate(select(memoria, '', {"verbCode": "BUFF_HPMAX", "effectCode": "DEFENSE"}), 'effectValue', columns), bad_target='TARGET')
    wikitext += table_row(png(1242), "濒死时防御力UP", separate(select(memoria, '', {"verbCode": "BUFF_DYING", "effectCode": "DEFENSE"}), 'effectValue', columns), bad_target='TARGET')
    wikitext += table_row(png(1086), "队友战斗不能时获得防御力UP", separate(select(memoria, '', {"verbCode": "BUFF_PARTY_DIE", "effectCode": "DEFENSE"}), 'effectValue', columns))
    wikitext += table_footer()
    
    wikitext += table_header([200000, 150000, 120000, 100000, 80000, 50000], percentage=False)
    wikitext += condition_good_v(memoria, "BARRIER", "给予屏障", png(1240), [20000, 15000, 12000, 10000, 8000, 5000])
    wikitext += table_footer()
    
    wikitext += "\n===MP获取与阻碍===\n"
    columns = [500, 450, 400, 375, 350, 300, 250, 200, 150, 125]
    wikitext += table_header(columns)
    wikitext += buff(memoria, "ACCEL", "Accele MPUP", png(1090), columns, pieceType=None)
    columns = range(300, 75-25, -25)
    wikitext += table_caption(columns)
    wikitext += buff(memoria, "MP_GAIN", "MP获得量UP", png(1093), columns)
    wikitext += table_footer()
    
    columns = [250, 200, 150, 100]
    wikitext += table_header(columns)
    wikitext += table_row(png(1130), "战斗开始时拥有MP", separate(select(memoria, '', {"verbCode": "INITIAL", "effectCode": "MP"}), 'effectValue', columns))
    columns = [490, 390, 365, 340, 300, 290, 250]
    wikitext += table_caption(columns, percentage=False)
    wikitext += table_row(png(1079), "回复MP", separate(select(memoria, '', {"verbCode": "HEAL", "effectCode": "MP"}), 'effectValue', columns, percentage=False))
    columns = [700, 640, 500, 390, 120, 115, 90, 60, 45]
    wikitext += table_caption(columns, percentage=False)
    wikitext += table_row(png(1180), "自动回复MP", separate(select(memoria, '', {"verbCode": "CONDITION_GOOD", "effectCode": "AUTO_HEAL", "genericValue": "MP"}), 'effectValue', columns, percentage=False))
    wikitext += table_footer()

    columns = [150, 100, 80, 70, 60]#range(60, 20-10, -10)
    wikitext += table_header(columns, percentage=False)
    wikitext += condition_good_v(memoria, "MP_PLUS_DAMAGED", "被攻击时MPUP", png(1227), columns, percentage=False)
    wikitext += condition_good_v(memoria, "MP_PLUS_WEAKED", "被弱点属性攻击时MPUP", png(1132), columns, percentage=False)
    columns = [50, 30, 20]
    wikitext += table_caption(columns, percentage=False)
    wikitext += condition_good_v(memoria, "MP_PLUS_BLAST", "Blast攻击时MP获得", png(1225), columns, percentage=False)
    columns = [800, 600, 500, 350, 250]
    wikitext += table_caption(columns)
    wikitext += debuff(memoria, "MP_GAIN", "MP获得量DOWN", png(1102), columns)
    columns = [500, 125]
    wikitext += table_caption(columns)
    wikitext += debuff(memoria, "ACCEL", "Accele MP DOWN", png(1099), columns)
    columns = [1000, 300]
    wikitext += table_caption(columns, percentage=False)
    wikitext += table_row(png(1176), "MP伤害", separate(select(memoria, '', {"verbCode": "HEAL", "effectCode": "MP_DAMAGE"}), 'effectValue', columns, percentage=False), bad_target='SELF')
    wikitext += table_footer()
    
    wikitext += "\n===特殊行动、特殊对策===\n"
    columns = range(550, 150-50, -50)
    wikitext += table_header(columns, column_certain=True)
    wikitext += condition_good_p(memoria, "AVOID", "回避", png(1124), columns)
    wikitext += condition_good_p(memoria, "COUNTER", "反击", png(1125), columns)
    wikitext += condition_good_p(memoria, "PROVOKE", "挑衅", png(1128), columns)
    wikitext += condition_good_p(memoria, "PROTECT", "保护", png(1170), columns)
    data = separate(select(memoria, None, {"verbCode": "CONDITION_GOOD", "effectCode": "PURSUE"}), 'probability', columns)
    wikitext += table_row(png(1126), "追击", data, column_certain=True)
    wikitext += table_footer()
    
    columns = [800, 600, 550, 500, 350]
    wikitext += table_header(columns, column_certain=True)
    wikitext += ignore(memoria, "AVOID", "回避无效", png(1134), columns)
    wikitext += ignore(memoria, "COUNTER", "反击无效", png(1135), columns)
    wikitext += ignore(memoria, "PROVOKE", "无视挑衅", png(1219), columns)
    wikitext += table_footer()
    
    wikitext += "\n===异常、抗异常===\n"
    ailmentEffectCodes = ["CHARM", "STUN", "RESTRAINT", "POISON", "BURN", "CURSE", "FOG", "DARKNESS", "BLINDNESS", "BAN_SKILL", "BAN_MAGIA", "DAMAGE_UP_BAD_NUM"]
    ailmentNames = ["魅惑", "眩晕", "拘束", "毒", "烧伤", "诅咒", "雾", "黑暗", "幻惑", "技能封印", "Magia封印", "虚弱"]
    icons = [png(i) for i in range(1103, 1114)]
    icons.append(png(1239))
    columns = range(525, 300-25, -25)
    wikitext += table_header(columns, column_certain=True)
    wikitext += ''.join([active_ailments(memoria, *i, columns) for i in zip(ailmentEffectCodes, ["赋予"+x for x in ailmentNames], icons)])
    wikitext += table_caption(columns, column_certain=True)
    wikitext += table_footer()
    
    icons = [png(i) for i in range(1145, 1155)] + [""]
    columns = range(500, 150-50, -50)
    wikitext += table_header(columns, column_certain=True)
    for idx in range(len(icons)):
        code = ailmentEffectCodes[idx]
        name = ailmentNames[idx]
        icon = icons[idx]
        desc_attack = "攻击时给予%s状态"%name
        wikitext += passive_ailments(memoria, code, desc_attack, icon, columns)
    wikitext += table_caption(columns, column_certain=True)
    wikitext += table_footer()
    
    icons = [png(i) for i in list(range(1136, 1145))+[1183, 1155]]
    columns = [700, 550, 500]
    wikitext += table_header(columns, column_certain=True)
    wikitext += ''.join([ignore(memoria, *i, columns) for i in zip(ailmentEffectCodes, [x+"无效" for x in ailmentNames], icons)])
    wikitext += table_caption(columns, column_certain=True)
    wikitext += table_footer()
    
    columns = range(600, 250-50, -50)
    wikitext += table_header(columns)
    wikitext += buff(memoria, "RESIST", "状态异常耐性UP", png(1089), columns)
    columns = [450, 400, 350, 300, 250, 200]
    wikitext += table_caption(columns)
    wikitext += debuff(memoria, "RESIST", "状态异常耐性DOWN", png(1098), columns)
    wikitext += table_footer()
    
    wikitext += "\n===重抽盘===\n"
    wikitext += table_header([], column_certain=True, icon_width='150px')
    wikitext += draw(memoria, "AGAIN", "重抽盘", png(1169))
    wikitext += draw(memoria, "CHARACTER", "重抽自己盘", [png(1161), png(1162), png(1163), png(1164), png(1165)])
    wikitext += draw(memoria, "ACCEL", "重抽Accele盘", png(1166))
    wikitext += draw(memoria, "BLAST", "重抽Blast盘", png(1168))
    wikitext += draw(memoria, "CHARGE", "重抽Charge盘", png(1167))
    wikitext += draw(memoria, "ALIGNMENT", "重抽同属性盘", [png(1156), png(1157), png(1158), png(1159), png(1160)])
    wikitext += table_footer()
    
    wikitext += "\n===回复、状态恢复===\n"
    columns = [1000, 580, 530, 525, 500, 480, 475, 450, 425, 380, 355, 330, 305, 280] #range(400, 100-50, -50)
    wikitext += table_header(columns, percentage=False)
    wikitext += table_row(png(1078), "回复HP(己/全)", separate(select(memoria, '', {"verbCode": "HEAL", "effectCode": "HP", "EXCEPT": {"targetId": "ONE"}}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_footer()

    columns = [1000, 540, 440]
    wikitext += table_header(columns, percentage=False)
    wikitext += table_row(png(1078), "回复HP(队友)", separate(select(memoria, '', {"verbCode": "HEAL", "effectCode": "HP", "targetId": "ONE"}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_row(png(1080), "苏生(队友)", separate(select(memoria, '', {"verbCode": "RESURRECT", "targetId": "ONE"}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_footer()

    columns = [400, 350, 250, 200, 100]
    wikitext += table_header(columns)
    wikitext += table_row(png(1174), "Survive", separate(select(memoria, '', {"verbCode": "CONDITION_GOOD", "effectCode": "SURVIVE"}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_footer()

    columns = range(160, 30-10, -10)
    wikitext += table_header(columns)
    wikitext += table_row(png(1129), "自动回复HP", separate(select(memoria, '', {"verbCode": "CONDITION_GOOD", "effectCode": "AUTO_HEAL", "EXCEPT": {"genericValue": "MP"}}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_footer()
    
    wikitext += table_header([])
    wikitext += table_row(png(1084), "状态异常解除", [select(memoria, None, {"verbCode": "REVOKE", "effectCode": "BAD"})], bad_target='TARGET')
    wikitext += table_row(png(1083), "状态强化解除", [select(memoria, None, {"verbCode": "REVOKE", "effectCode": "GOOD"})], bad_target='SELF')
    wikitext += table_row(png(1082), "Debuff解除", [select(memoria, None, {"verbCode": "REVOKE", "effectCode": "DEBUFF"})], bad_target='TARGET')
    wikitext += table_row(png(1081), "Buff解除", [select(memoria, None, {"verbCode": "REVOKE", "effectCode": "BUFF"})], bad_target='SELF')
    wikitext += table_footer()
    
    wikitext += "\n===其他===\n"
    columns = [150, 100]
    wikitext += table_header(columns, column_certain=True)
    wikitext += condition_good_p(memoria, "SKILL_QUICK", "技能冷却加速", png(1131), columns)
    wikitext += table_footer()
    
    columns = [801]
    wikitext += table_header(columns)
    wikitext += table_row(png(1241), "对自己伤害", separate(select(memoria, '', {"verbCode": "DAMAGE", "effectCode": "ALLY"}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_footer()

    wikitext += table_header([], column_certain=True)
    wikitext += condition_good_p(memoria, "GUTS", "忍耐", png(1127), [], percentage=False)
    wikitext += condition_good_p(memoria, "IMITATE_ATTRIBUTE", "Variable", png(1229), [], percentage=False)
    wikitext += condition_good_p(memoria, "NO_COST_CHARGE", "Charge无消耗", png(1232), [], percentage=False)
    wikitext += table_footer()

    wikitext += table_header([30, 20, 10], percentage=False)
    wikitext += ignore_v(memoria, "CRITICAL", "暴击无效", png(1212), [3, 2, 1], percentage=False)
    wikitext += ignore_v(memoria, "DEBUFF", "Debuff无效", png(1218), [3, 2, 1], percentage=False)
    wikitext += ignore_v(memoria, "CONDITION_BAD", "异常状态无效", png(1231), [3, 2, 1], percentage=False)
    wikitext += condition_good_v(memoria, "REFLECT_DEBUFF", "Debuff效果反射", png(1244), [3, 2, 1], percentage=False)
    wikitext += table_footer()
    
    wikitext += table_header([50, 40, 30, 20, 10], percentage=False)  # 记忆的效果数值是2000,既不代表200%也不代表200ptHP或MP,而是代表2层charge combo,为了让header显示出2需要输入20
    wikitext += condition_good_v(memoria, "C_COMBO_PLUS", "Charge combo时Charge数增加", png(1217), [5000, 4000, 3000, 2000, 1000])
    wikitext += table_footer()

    columns = [1]
    wikitext += table_header([10], percentage=False)
    wikitext += table_row(png(1245), "Buff效果延长", separate(select(memoria, '', {"verbCode": "TURN_ALLY", "effectCode": "BUFF"}), 'effectValue', columns, percentage=False), bad_target='TARGET')
    wikitext += table_row(png(1246), "Debuff效果延长", separate(select(memoria, '', {"verbCode": "TURN_ENEMY", "effectCode": "DEBUFF"}), 'effectValue', columns, percentage=False), bad_target='SELF')
    wikitext += table_footer()

    write_wiki(OUTPUT, wikitext)

if __name__ == '__main__':
    main()