Greasy Fork 还支持 简体中文。

桌游模拟器营地牌表导出工具

在营地牌表页添加导出为桌游模拟器存档的按钮

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         桌游模拟器营地牌表导出工具
// @namespace    http://www.iyingdi.com/
// @version      0.3
// @description  在营地牌表页添加导出为桌游模拟器存档的按钮
// @author       hyper
// @match        https://www.iyingdi.com/**
// @grant        none
// ==/UserScript==

async function init(){
    let added;
    window.addEventListener("popstate",() => added = undefined);
    while(true){
        await new Promise(r => setTimeout(r, 1000));
        if(added == location.href)continue;
        if(!location.href.match("deck(d|D)etail")){
            added = undefined; continue;
        }

        const actionBox = $('.deck-detail-box .action-box, .mainDeckArea .action-box');
        if(!actionBox.length)continue;
        createSaveBtn(actionBox);
        added = location.href;
    }
}
init();

async function createSaveBtn(actionBox){
    if($("#tts-export").length)return null;
    const [name,data] = await getSave(getCards());
    const href = window.URL.createObjectURL(new Blob([data]), { type: "text/plain" });
    const e = $(`<div id="tts-export" class="btn-item-w"><a href="${href}" download="${name}">导出桌游模拟器存档</a></div>`);
    
    actionBox.append(e);
}

function getCards(){
    const cards = Array.from($('.card.inline-block')).map(c => {
        const sideboard = c.parentElement.previousSibling.innerText.startsWith("备牌");
        const image = c.dataset.image;
        const id = c.dataset.value;
        const num = parseInt(c.querySelector('.count').innerText.slice(1)) || 1;
        return [id, image, sideboard, num];
    });
    return cards;
}

async function getSave(cards){
    const main = [];
    const side = [];
    let s = 1;
    await Promise.all(cards.map(async (c,i) => {
        let [id, image, sideboard, num] = c;
        const url = 'https://www.iyingdi.com/magic/card/' + id;
        const res = await fetch(url);
        const json = await res.json();
        image = image || json.card.img;

        const stats = json.card.clazz == '生物' ? `\n${json.card.attack}/${json.card.defense}` : '\n';
        const nickname = `${json.card.cname || json.card.ename} - ${json.card.mana}${stats}${json.card.mainType} ~ ${json.card.subType}`;
        const description = `${json.card.rule}`;

        for(let j = 0; j < num; j ++){
            const card = makeTTSCard({name: nickname, oracle: description, face: image, id: s++});
            if(sideboard)side.push(card);
            else main.push(card);
        }
    }));

    const md = makeTTSDeck(main);
    md.Nickname = '主牌';

    const sd = makeTTSDeck(side);
    sd.Nickname = '备牌';
    sd.Transform.posX = 3;

    const save = makeTTSSave(md,sd);
    const name = `[${$('.formatName .format, .format-name .format').text()}]${$('.formatName .name.inline-block, .format-name .name.inline-block').text()} - ${$('.basicInfo .player, .deck-info-box .player').text().slice(3)}`;
    //download(name + ".json", JSON.stringify(save, null, 4));
    return [name + ".json", JSON.stringify(save, null, 4)];
}

function download(filename, text) {
  var element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}

function makeTTSCard({name, oracle, face, id}){
    const card = clone(TTSCard);
    card.Nickname = name;
    card.Description = oracle;
    card.CardID = id * 100;
    card.CustomDeck = {};
    card.CustomDeck[id] = {
        FaceURL: face,
        BackURL: "http://ww1.sinaimg.cn/large/8239391egy1gg9acxj0ryj207d0aldj7.jpg",
        "NumWidth": 1,
        "NumHeight": 1,
        "BackIsHidden": true,
        "UniqueBack": false,
        "Type": 0
    };
    card.GUID = Math.random().toString(16).slice(-6);
    return card;
}

function makeTTSDeck(cards){
    const deck = clone(TTSDeck);
    deck.DeckIDs = cards.map(c => c.CardID);
    deck.CustomDeck = cards.reduce((a,b) => clone(a, b.CustomDeck), {});
    deck.ContainedObjects = cards;
    deck.GUID = Math.random().toString(16).slice(-6);
    return deck;
}

function makeTTSSave(...ObjectStates){
    return clone(TTS, {ObjectStates});
}

const TTS = {
  "SaveName": "",
  "GameMode": "",
  "Date": "",
  "Gravity": 0.5,
  "PlayArea": 0.5,
  "GameType": "",
  "GameComplexity": "",
  "Tags": [],
  "Table": "",
  "Sky": "",
  "Note": "",
  "Rules": "",
  "TabStates": {},
  "LuaScript": "",
  "LuaScriptState": "",
  "XmlUI": "",
  "VersionNumber": "",
}

const TTSDeck = {
    "Name": "Deck",
    "Transform": {
        "posX": 0,
        "posY": 0,
        "posZ": 0,
        "rotX": 0,
        "rotY": 180,
        "rotZ": 180.0,
        "scaleX": 1.0,
        "scaleY": 1.0,
        "scaleZ": 1.0
    },
    "Nickname": "",
    "Description": "",
    "GMNotes": "",
    "ColorDiffuse": {
        "r": 0.713235259,
        "g": 0.713235259,
        "b": 0.713235259
    },
    "Locked": false,
    "Grid": true,
    "Snap": true,
    "IgnoreFoW": false,
    "MeasureMovement": false,
    "DragSelectable": true,
    "Autoraise": true,
    "Sticky": true,
    "Tooltip": true,
    "GridProjection": false,
    "HideWhenFaceDown": true,
    "Hands": false,
    "SidewaysCard": false,
    "LuaScript": "",
    "LuaScriptState": "",
    "XmlUI": "",
}

const TTSCard = {
    "Name": "Card",
    "Transform": {
        "posX": 0,
        "posY": 0,
        "posZ": 0,
        "rotX": 0,
        "rotY": 180,
        "rotZ": 180.0,
        "scaleX": 1.0,
        "scaleY": 1.0,
        "scaleZ": 1.0
    },
    "Nickname": "",
    "Description": "",
    "GMNotes": "",
    "ColorDiffuse": {
        "r": 0.713235259,
        "g": 0.713235259,
        "b": 0.713235259
    },
    "Locked": false,
    "Grid": true,
    "Snap": true,
    "IgnoreFoW": false,
    "MeasureMovement": false,
    "DragSelectable": true,
    "Autoraise": true,
    "Sticky": true,
    "Tooltip": true,
    "GridProjection": false,
    "HideWhenFaceDown": true,
    "Hands": true,
    "CardID": 0,
    "SidewaysCard": false,
    "LuaScript": "",
    "LuaScriptState": "",
    "XmlUI": ""
};

function clone(...objs){
    const obj = {}
    for(const one of objs){
        for(const key in one){
            obj[key] = one[key];
        }
    }
    return obj;
}