Atlantis GameData hyperlinks

Add hyperlinks to GameData

当前为 2021-05-03 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Atlantis GameData hyperlinks
// @namespace    http://atlantis-pbem.com/data
// @version      0.009
// @description  Add hyperlinks to GameData
// @author       Anton
// @match        http://atlantis-pbem.com/data
// @match        http://atlantis-pbem.com/times*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    let scriptName = "Atlantis GameData hyperlinks:";

    let GMAddStyle = (typeof GM_addStyle == 'function') ? GM_addStyle : function (aCss) {
        'use strict';
        let head = document.getElementsByTagName('head')[0];
        if (head) {
            let style = document.createElement('style');
            style.setAttribute('type', 'text/css');
            style.textContent = aCss;
            head.appendChild(style);
            return style;
        }
        return null;
    };

GMAddStyle(`
.gameDataMenu {
  position: fixed;
  right: 0;
  top: 100px;
  width: 350px;
  border-top: 1px solid #CCC;
  border-left: 1px solid #CCC;
  border-bottom: 1px solid #CCC;
  border-top-left-radius: 8px;
  border-bottom-left-radius: 8px;
  padding: 8px;
  background: rgba(255,255,255,0.75);
}
.gameDataMenu > a {
  display: block;
}
.gameDataLink, pre > a.mainlink {
  position: relative;
  top: -80px;
}
#skills-ph, #items-ph, #objects-ph, #infopanel {
  padding-left: 8px;
  font-size: 70%;
}
#filtr {
  margin-left: 8px;
  padding: 1px;
  border: 1px solid #CCC;
}
.gameDataHidden {
  display: none;
}
`);

    let shipTable =
`Class     Capacity  Cost  Sailors  Skill
Longship  150       10    4        1
Raft      450       10    2        1
Cog       750       25    6        2
Galleon   2700      75    15       3`.split("\n");

    let isData = window.location.pathname.toLowerCase().replace('/','') == "data";
    let isTimes = window.location.pathname.toLowerCase().replace('/','').indexOf("times") == 0;

    let body = document.querySelector('body');

    let controlPanel = document.createElement('div');
    controlPanel.className = 'gameDataMenu';
    body.append(controlPanel);

    let timesControllerInit = () => {
        controlPanel.innerHTML = `
<div><label for="filtr">Search: </label><input id="filtr" type="text"></div>
<a href="#top-agdhl">Top</a>
<a href="#quests">Quests</a>
<a href="#articles">Articles</a>
<a href="#rumors">Rumors</a>
<div id="infopanel"></div>
`;
        let container = document.querySelector('div.container.my-5');
        container.style.paddingRight = '350px';

        let paragraphs = container.querySelectorAll('h2');

        // Add links to paragraphs
        if (paragraphs.length == 4) {
            let addLinkToParapgraph = (pp, idx, linkName) => {
                pp[idx].outerHTML = '<a class="gameDataLink" name="' + linkName + '"></a>' + pp[idx].outerHTML
            };
            addLinkToParapgraph(paragraphs, 0, 'top-agdhl');
            addLinkToParapgraph(paragraphs, 1, 'quests');
            addLinkToParapgraph(paragraphs, 2, 'articles');
            addLinkToParapgraph(paragraphs, 3, 'rumors');
        } else {
            console.log(scriptName, "Paragraphs not found!");
        }

        // Fix events
        let events = container.querySelector('pre').parentNode;
        events.style.fontSize = '14px';

        let messages = container.querySelectorAll('.jumbotron');

        // fill info panel
        let infopanel = document.getElementById('infopanel');
        for (let i = 0; i < messages.length; i++) {
            let txt = messages[i].innerText.toLowerCase();
            if (txt.indexOf('players have visited') != -1 && txt.indexOf('of all inhabited regions') != -1) {
                let percentInfo = txt.split(' ').filter(e => e.indexOf('%') != -1);
                if (percentInfo.length > 0) percentInfo = percentInfo[0];
                infopanel.innerHTML += 'Map: ' + percentInfo + '<br/>';
            }
        }
        let allMessagesIncludingParagraph = container.querySelectorAll('div.jumbotron,h2');
        let questsCount = -2;
        for (let i = 0; i < allMessagesIncludingParagraph.length; i++) {
            if (questsCount < 0 && allMessagesIncludingParagraph[i].tagName == 'H2') questsCount++;
            else if (questsCount >= 0) {
                if (allMessagesIncludingParagraph[i].tagName == 'H2') break; else questsCount++;
            }
        }
        infopanel.innerHTML += 'Quests: ' + questsCount + '<br/>';

        let updateLists = (s) => {
            s = s.toLowerCase().trim();
            for (let i = 0; i < messages.length; i++) {
                let txt = messages[i].innerText.toLowerCase();
                messages[i].classList.toggle('gameDataHidden', txt.indexOf(s) == -1);
            }
            events.classList.toggle('gameDataHidden', s != '');
        };

        let inp = document.querySelector('#filtr');
        inp.onkeyup = (e) => {
            updateLists(String(inp.value).toUpperCase());
        };
    };

    let dataControllerInit = () => {
        let container = document.querySelector('pre');
        let data = container.innerHTML;
        let dataLines = data.split(/\r?\n/);

        let shipbuilding1Line = dataLines.findIndex(e => e.indexOf('shipbuilding [SHIP] 2') != -1);

        let skillReportLine = dataLines.indexOf("Skill reports:");
        let itemReportLine = dataLines.indexOf("Item reports:");
        let objectReportLine = dataLines.indexOf("Object reports:");

        controlPanel.innerHTML = `
<div><label for="filtr">Search: </label><input id="filtr" type="text"></div>
<a href="#skills">Skills</a>
<div id="skills-ph"></div>
<a href="#items">Items</a>
<div id="items-ph"></div>
<a href="#objects">Objects</a>
<div id="objects-ph"></div>
`;
        body.append(controlPanel);

        let skillsArray = [];
        let itemsArray = [];
        let objectsArray = [];
        let skillsDescription = {};
        let itemsDescription = {};
        let objectsDescription = {};
        let skillsDesc = {};
        let itemsDesc = {};
        let objectsDesc = {}; // inside: {name, desc}

        let addToArray = (arr, key, item) => {
            if (arr != '' && key != '') {
                if (!arr[key]) arr[key] = [];
                if (arr[key].indexOf(item) == -1) arr[key].push(item);
            }
        };

        let analyzePart = (s, run, prevSkill, prevItem, prevObject) => {
            let parts = s.match(/(.*)(\[(.{3,5})\])(.*)/);
            if (parts && parts.length > 0) {
                let skillAddon = (skillsArray.indexOf(parts[3] + '1') != -1) ? '1' : '';
                addToArray(skillsDescription, prevSkill, parts[3] + skillAddon);
                addToArray(itemsDescription, prevItem, parts[3]);
                addToArray(objectsDescription, prevObject, parts[3]);
                return analyzePart(parts[1], run, prevSkill, prevItem, prevObject) +
                    '<a href="#' + parts[3] + skillAddon + '">' + parts[2] + '</a>' + analyzePart(parts[4], run, prevSkill, prevItem, prevObject);
            }
            return s;
        };

        let clearName = (s) => {
            return s.replaceAll(' ', '_').replaceAll("'", '');
        };

        for (let run = 0; run < 2; run++) {
            let prevLine = '';
            let prevSkill = '';
            let prevItem = '';
            let prevObject = '';
            for (let i = 0; i < dataLines.length; i++) {
                let str = dataLines[i];
                // mining [MINI] 3: A unit with this skill may PRODUCE mithril [MITH] at
                let searchForName = str.match(/(\S.*)(\[(.{3,5})\].(\d)):(.*)/);
                if (searchForName && searchForName.length > 0 && prevLine.trim() == '') {
                    let firstPart = searchForName[1]; // mining
                    let linkText = searchForName[2]; // [MINI] 3
                    let name = searchForName[3]; // MINI
                    let level = parseInt(searchForName[4]); // 3
                    let lastPart = searchForName[5]; // A unit with this skill may PRODUCE mithril [MITH] at
                    let linkName = name + level;
                    prevSkill = name + '1';
                    prevItem = '';
                    prevObject = '';
                    if (skillsArray.indexOf(linkName) == -1) {
                        skillsArray.push(linkName);
                        lastPart = analyzePart(lastPart, run, prevSkill, prevItem, prevObject);
                        dataLines[i] = firstPart + '<a class="mainlink" name="' + linkName + '"></a>' + linkText + ':' + lastPart;
                    }
                } else {
                    // winged horse [WING], weight 50, walking capacity 20, riding capacity
                    searchForName = str.match(/(\S.*)(\[(.{3,5})\])[,\.](.*)/);
                    if (searchForName && searchForName.length > 0 && prevLine.trim() == '') {
                        let firstPart = searchForName[1]; // winged horse
                        let linkText = searchForName[2]; // [WING]
                        let name = searchForName[3]; // WING
                        let lastPart = searchForName[4]; // weight 50, walking capacity 20, riding capacity
                        let linkName = name;
                        prevSkill = '';
                        prevItem = name;
                        prevObject = '';
                        if (itemsArray.indexOf(linkName) == -1) {
                            itemsArray.push(linkName);
                            lastPart = analyzePart(lastPart, run, prevSkill, prevItem, prevObject);
                            dataLines[i] = firstPart + '<a class="mainlink" name="' + linkName + '"></a>' + linkText + ',' + lastPart;
                        }
                    } else {
                        // Magician's Tower: This is a building. Monsters can potentially lair in
                        searchForName = str.match(/^(.+):(.+)/);
                        if (searchForName && searchForName.length > 0 && prevLine.trim() == '' && run == 0) {
                            let name = searchForName[1]; // Magician's Tower
                            let lastPart = searchForName[2]; // This is a building. Monsters can potentially lair in
                            let linkName = clearName(name);
                            prevSkill = '';
                            prevItem = '';
                            prevObject = name;
                            if (objectsArray.indexOf(linkName) == -1) {
                                objectsArray.push(linkName);
                                lastPart = analyzePart(lastPart, run, prevSkill, prevItem, prevObject);
                                dataLines[i] = '<a class="mainlink" name="' + linkName + '"></a>' + name + ':' + lastPart;
                            }
                        } else {
                            if (run == 1) dataLines[i] = analyzePart(str, run, prevSkill, prevItem, prevObject);
                        }
                    }
                }
                if (str.trim() == '') {
                    prevSkill = '';
                    prevItem = '';
                } else if (prevSkill != '' && run == 0) {
                    if (!skillsDesc[prevSkill]) skillsDesc[prevSkill] = str.trim().toUpperCase();
                    else skillsDesc[prevSkill] += ' ' + str.trim().toUpperCase();
                } else if (prevItem != '' && run == 0) {
                    if (!itemsDesc[prevItem]) itemsDesc[prevItem] = str.trim().toUpperCase();
                    else itemsDesc[prevItem] += ' ' + str.trim().toUpperCase();
                } else if (prevObject != '' && run == 0) {
                    let obj_id = clearName(prevObject);
                    if (!objectsDesc[obj_id]) objectsDesc[obj_id] = {desc: str.trim().toUpperCase(), name: prevObject};
                    else objectsDesc[obj_id].desc += ' ' + str.trim().toUpperCase();
                }
                prevLine = str;
            }
        }

        dataLines[skillReportLine] = '<a name="skills" class="gameDataLink"></a>Skill reports:';
        dataLines[itemReportLine] = '<a name="items" class="gameDataLink"></a>Item reports:';
        dataLines[objectReportLine] = '<a name="objects" class="gameDataLink"></a>Object reports:';

        // add shipbuilding description of possible ships
        dataLines.splice(shipbuilding1Line, 0, "");
        for (let i = 0; i < shipTable.length; i++) {
            dataLines.splice(shipbuilding1Line, 0, shipTable[shipTable.length - i - 1]);
        }

        container.innerHTML = dataLines.join("\n");

        let skillHasObject = (skil, filterValue) => {
            let found = skillsDescription[skil] && !!(skillsDescription[skil].find(o => o.indexOf(filterValue) != -1));
            let foundInDesc = skillsDesc[skil] && skillsDesc[skil].indexOf(filterValue) != -1;
            return found || foundInDesc;
        };

        let objectHasSkill = (ob, filterValue) => {
            let found = itemsDescription[ob] && !!(itemsDescription[ob].find(o => o.indexOf(filterValue) != -1));
            let foundInDesc = itemsDesc[ob] && itemsDesc[ob].indexOf(filterValue) != -1;
            return found || foundInDesc;
        };

        let objectHasDescription = (ob, filterValue) => {
            let foundInDesc = objectsDesc[ob].desc && objectsDesc[ob].desc.indexOf(filterValue) != -1;
            return foundInDesc;
        };

        let skillsPH = document.querySelector('#skills-ph');
        let itemsPH = document.querySelector('#items-ph');
        let objectsPH = document.querySelector('#objects-ph');

        let updateLists = (filterValue) => {
            skillsPH.innerHTML = skillsArray
                .filter(e => e[e.length - 1] == 1)
                .filter(e => e.indexOf(filterValue) != -1 || skillHasObject(e, filterValue))
                .map(e => e.substr(0, e.length - 1))
                .map(e => '<a href="#' + e + '1">' + e + '</a>')
                .join(', ');

            itemsPH.innerHTML = itemsArray
                .filter(e => e.indexOf(filterValue) != -1 || objectHasSkill(e, filterValue))
                .map(e => '<a href="#' + e + '">' + e + '</a>')
                .join(', ');

            if (filterValue.trim() == '') objectsPH.innerHTML = '';
            else {
                objectsPH.innerHTML = objectsArray
                    .filter(e => objectHasDescription(e, filterValue))
                    .map(e => '<a href="#' + clearName(e) + '">' + objectsDesc[e].name + '</a>')
                    .join(', ');
            }
        }

        updateLists('');

        let inp = document.querySelector('#filtr');
        inp.onkeyup = (e) => {
            updateLists(String(inp.value).toUpperCase());
        };
    };

    if (isData) dataControllerInit();
    if (isTimes) timesControllerInit();

    console.log(scriptName, 'hello.');
})();