Greasy Fork

Greasy Fork is available in English.

NGA-FF14生产宏排版器

可以解析生产宏序列或是模拟器工序,将其转换为适合在nga展示的多种语言的宏表格

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         NGA-FF14生产宏排版器
// @namespace    http://tampermonkey.net/
// @version      1.1.0
// @description  可以解析生产宏序列或是模拟器工序,将其转换为适合在nga展示的多种语言的宏表格
// @author       浮砂
// @match        http*://bbs.nga.cn/post.php?*action=new*
// @match        http*://bbs.nga.cn/post.php?*_newui
// @match        http*://bbs.nga.cn/post.php?*action=reply*
// @match        http*://bbs.nga.cn/post.php?*action=modify*
// @match        http*://ngabbs.com/post.php?*action=new*
// @match        http*://ngabbs.com/post.php?*_newui
// @match        http*://ngabbs.com/post.php?*action=reply*
// @match        http*://ngabbs.com/post.php?*action=modify*
// @match        http*://nga.178.com/post.php?*action=new*
// @match        http*://nga.178.com/post.php?*_newui
// @match        http*://nga.178.com/post.php?*action=reply*
// @match        http*://nga.178.com/post.php?*action=modify*
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const i18n = [
        // [ simulator-key, zh-name, ja-name, en-name, awaits, simulator-key-2(BestCraft) ]
        ['muscleMemory', '坚信', '確信', 'Muscle Memory', 3, 'muscle_memory'],
        ['reflect', '闲静', '真価', 'Reflect', 3],
        ['trainedEye', '工匠的神速技巧', '匠の早業', 'Trained Eye', 3, 'trained_eye'],
        ['basicSynth', '制作', '作業', 'Basic Synthesis', 3, 'basic_synthesis'],
        ['basicSynth2', '制作', '作業', 'Basic Synthesis', 3, 'basic_synthesis'],
        ['carefulSynthesis', '模范制作', '模範作業', 'Careful Synthesis', 3, 'careful_synthesis'],
        ['carefulSynthesis2', '模范制作', '模範作業', 'Careful Synthesis', 3],
        ['rapidSynthesis', '高速制作', '突貫作業', 'Rapid Synthesis', 3, 'rapid_synthesis'],
        ['rapidSynthesis2', '高速制作', '突貫作業', 'Rapid Synthesis', 3],
        ['groundwork', '坯料制作', '下地作業', 'Groundwork', 3],
        ['groundwork2', '坯料制作', '下地作業', 'Groundwork', 3],
        ['intensiveSynthesis', '集中制作', '集中作業', 'Intensive Synthesis', 3, 'intensive_synthesis'],
        ['prudentSynthesis', '俭约制作', '倹約作業', 'Prudent Synthesis', 3, 'prudent_synthesis'],
        ['delicateSynthesis', '精密制作', '精密作業', 'Delicate Synthesis', 3, 'delicate_synthesis'],
        ['delicateSynthesis2', '精密制作', '精密作業', 'Delicate Synthesis', 3],
        ['basicTouch', '加工', '加工', 'Basic Touch', 3, 'basic_touch'],
        ['hastyTouch', '仓促', 'ヘイスティタッチ', 'Hasty Touch', 3, 'hasty_touch'],
        ['standardTouch', '中级加工', '中級加工', 'Standard Touch', 3, 'standard_touch'],
        ['byregotsBlessing', '比尔格的祝福', 'ビエルゴの祝福', "Byregot's Blessing", 3, 'byregot_s_blessing'],
        ['preciseTouch', '集中加工', '集中加工', 'Precise Touch', 3, 'precise_touch'],
        ['prudentTouch', '俭约加工', '倹約加工', 'Prudent Touch', 3, 'prudent_touch'],
        ['preparatoryTouch', '坯料加工', '下地加工', 'Preparatory Touch', 3, 'preparatory_touch'],
        ['advancedTouch', '上级加工', '上級加工', 'Advanced Touch', 3, 'advanced_touch'],
        ['trainedFinesse', '工匠的神技', '匠の神業', 'Trained Finesse', 3, 'trained_finesse'],
        ['mastersMend', '精修', 'マスターズメンド', "Master's Mend", 3, 'masters_mend'],
        ['wasteNot', '俭约', '倹約', 'Waste Not', 2, 'waste_not'],
        ['wasteNot2', '长期俭约', '長期倹約', 'Waste Not II', 2, 'waste_not_ii'],
        ['manipulation', '掌握', 'マニピュレーション', 'Manipulation', 2],
        ['veneration', '崇敬', 'ヴェネレーション', 'Veneration', 2],
        ['greatStrides', '阔步', 'グレートストライド', 'Great Strides', 2, 'great_strides'],
        ['innovation', '改革', 'イノベーション', 'Innovation', 2],
        ['observe', '观察', '経過観察', 'Observe', 3],
        ['tricksOfTheTrade', '秘诀', '秘訣', 'Tricks of the Trade', 3, 'tricks_of_the_trade'],
        ['finalAppraisal', '最终确认', '最終確認', 'Final Appraisal', 2, 'final_appraisal'],
        ['heartAndSoul', '专心致志', '一心不乱', 'Heart and Soul', 3, 'heart_and_soul'],

        // 7.0 NEW
        ['refinedTouch', '精炼加工', '洗練加工', 'Refined Touch', 3, 'refined_touch'],
        ['daringTouch', '冒进', 'デアリングタッチ', 'Daring Touch', 3, 'daring_touch'],
        ['immaculateMend', '巧夺天工', 'パーフェクトメンド', 'Immaculate Mend', 3, 'immaculate_mend'],
        ['quickInnovation', '快速改革', 'クイックイノベーション', 'Quick Innovation', 3, 'quick_innovation'],
        ['trainedPerfection', '工匠的绝技', '匠の絶技', 'Trained Perfection', 3, 'trained_perfection'],

        // WILL BE DELETED IN 7.0
        ['focusedSynthesis', '注视制作', '注視作業', 'Focused Synthesis', 3],
        ['focusedTouch', '注视加工', '注視加工', 'Focused Touch', 3],
    ]

    // Init common
    const page = typeof unsafeWindow == 'undefined' ? window : unsafeWindow;
    const $ = page.$;
    const _$ = page._$;
    const commonui = page.commonui;
    if (!commonui) { return; }

    function resolveMacro(macro) {
        const actions = []
        macro.split('\n').forEach(line => {
            const action = line.match(/\/ac\s"?(.*?)"?\s<wait\.\d+>/)?.[1]
            if (action) actions.push(action)
        })

        const mapKeys = [1, 2, 3]

        const invalidActions = []
        const results = []
        actions.forEach(action => {
            let pair
            for (let i = 0; i < mapKeys.length; i++) {
                const key = mapKeys[i]
                pair = i18n.find(i => i[key] === action)
                if (pair) break
            }
            if (!pair) invalidActions.push(action)
            else results.push(pair)
        })
        if (invalidActions.length) {
            console.warn('invalidActions:', invalidActions)
            alert(`以下技能的数据未找到,可能脚本需要更新?\n请联系作者并提供以下技能名以获取详情。\n${invalidActions.join('、')}`)
            return false
        }
        console.log('resolveMacro.results', results)
        return results
    }
    function resolveProcedure(procedure) {
        const actions = JSON.parse(procedure)
        if (!Array.isArray(actions) || !actions?.length) {
            alert('工序格式不正确,请复制生产模拟器的‘导出工序’导出的内容'); return false
        }
        const invalidActions = []
        const results = []
        actions.forEach(action => {
            const pair = i18n.find(i => i[0] === action || i[5] === action)
            if (!pair) invalidActions.push(action)
            else results.push(pair)
        })
        if (invalidActions.length) {
            alert(`以下工序的数据未找到,可能脚本需要更新?\n请联系作者并提供以下工序名以获取详情。\n${invalidActions.join('、')}`)
            return false
        }
        return results
    }

    function dealActions(actions, outLangs = ['zh']) {
        if (!actions?.length) return ''
        const columns = []
        while (actions.length > 0) {
            if (actions.length > 15) {
                columns.push(actions.splice(0, 14));
            } else {
                columns.push(actions.splice(0, actions.length));
            }
        }
        let result = ''
        if (outLangs.includes('zh')) {
            result += '[collapse=中文宏]'
            result += dealColumns(columns, 'zh')
            result += '[/collapse]'
        }
        if (outLangs.includes('ja')) {
            result += '[collapse=日文宏]'
            result += dealColumns(columns, 'ja')
            result += '[/collapse]'
        }
        if (outLangs.includes('en')) {
            result += '[collapse=英文宏]'
            result += dealColumns(columns, 'en')
            result += '[/collapse]'
        }
        result += '\n'
        return result

        function dealColumns(columns, lang = 'zh') {
            let keyIndex = 1
            if (lang === 'ja') keyIndex = 2
            if (lang === 'en') keyIndex = 3

            let result = '[table]\n[tr]\n'
            columns.forEach((column, index) => {
                result += `[td width=1 top]\n` // [b]宏${index + 1}[/b][h][/h]\n`
                result += '[code]'
                column.forEach(pair => {
                    result += `/ac "${pair[keyIndex]}" <wait.${pair[4]}>\n`
                })
                if (column.length < 15) {
                    result += `/e 宏${index + 1} 已完成 <se.${index + 1}>\n`
                }
                result = result.slice(0, -1)
                result += '[/code][/td]\n'
            })
            result += '[/tr][/table]'
            return result
        }
    }

    const openWindow = () => {
        const w = commonui.createadminwindow();
        var modeMacro, modeProcedure, content, outZh, outJa, outEn, autoClose;
        w._.addContent(null);
        w._.addContent(
            '输入类型', _$('/br'),
            modeMacro = _$('/input','type','radio','name','mode','checked',1),'宏 ',
            modeProcedure = _$('/input','type','radio','name','mode'),'模拟器工序 ', _$('/br'),
            '输入内容', _$('/br'),
            content = _$('/textarea', 'placeholder', '请输入宏或模拟器工序内容', 'style', 'width:21em;height:14em;'), _$('/br'),
            '输出语言', _$('/br'),
            outZh = _$('/input','type','checkbox','checked',1),'中文 ',
            outJa = _$('/input','type','checkbox','checked',1),'日文 ',
            outEn = _$('/input','type','checkbox','checked',1),'英文 ',
            _$('/br'),
            '其他选项', _$('/br'),
            autoClose = _$('/input','type','checkbox','checked',1),'生成后自动关闭此窗口 ',
            _$('/br'), _$('/br'),
            _$('/button', 'type', 'button', 'class', 'larger', 'innerHTML', '确认', 'onclick', async () => {
                console.warn('ffm', {modeMacro, modeProcedure, content, outZh, outJa, outEn, autoClose})
                const dealFunc = modeProcedure.checked ? resolveProcedure : resolveMacro
                const actions = dealFunc(content.value)
                if (!actions) return
                const outLangs = []
                if (outZh.checked) outLangs.push('zh')
                if (outJa.checked) outLangs.push('ja')
                if (outEn.checked) outLangs.push('en')
                const result = dealActions(actions, outLangs)
                page.postfunc.addText(result)
                if (autoClose.checked) w._.close()
            })
        );
        w._.addTitle('FF14生产宏排版工具');
        w._.show();
    }
    const generateUI = () => {
        const ui_button = document.createElement('button')
        ui_button.type = 'button'
        ui_button.innerHTML = '插入肥肥宏'
        ui_button.addEventListener('click', openWindow)
        for (var i = 0; i < document.getElementsByTagName('button').length; i++) {
            var len_button = document.getElementsByTagName('button')[i];
            if (len_button.innerHTML === '长度') {
                len_button.after(ui_button)
                return
            }
        }
        throw new Error('未找到元素')
    }

    let retry_count = 0
    const retry = (err) => {
        console.warn('[NGA-FFMacro-Generater] generate failed due to', err, ' . Retrying...')
        retry_count++
        generateUI()
    }

    setTimeout(() => {
        try {
            generateUI()
        } catch (e) {
            if (retry_count < 30) {
                retry(e)
            } else {
                console.error('[NGA-FFMacro-Generater] generate failed and retry count goes to its limit.')
            }
        }
    }, 500)
})();