您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Travian Kingdoms Tools: Search for valleys and oases
// ==UserScript== // @name Travian Kingdoms Tools // @description Travian Kingdoms Tools: Search for valleys and oases // @match https://*.kingdoms.com/* // @version 1.0.1 // @namespace https://greasyfork.org/users/563852 // ==/UserScript== (async () => { 'use strict'; let session = undefined; let searching = false; (function(request) { XMLHttpRequest.prototype.send = function() { const argument = arguments[0]; if (typeof argument === 'string' || argument instanceof String) { if (argument.includes('session')) { session = JSON.parse(argument).session; } } request.apply(this, arguments); }; })(XMLHttpRequest.prototype.send); setInterval(async () => { const node = document.querySelector('.travian-kingdoms-tools'); if (window.location.href.includes('page:map')) { if (!node) { const { text, node, nodeOne, nodeTwo } = createFooter(); node.addEventListener('click', async () => { const timestamp = new Date(); if (searching) { text.innerText = 'Search for valleys and oases'; nodeOne.style.display = 'none'; nodeTwo.style.display = 'none'; } else { text.innerText = 'Searching will take a moment...'; const { valleys: mapValleys, oases: mapOases, names } = await processMap(); const { valleys: oasesValleys, oases } = await processOases(session, mapValleys, mapOases, names); const valleys = await processValleys(oasesValleys, oases); const { valleyTable, oasisTable } = createTables(); oases.map((oasis, index) => createRow(oasisTable, index + 1, createOasisContent(oasis))); valleys.map((valley, index) => createRow(valleyTable, index + 1, createValleyContent(valley))); text.innerText = `Searching was successfully completed in ${parseInt(((new Date() - timestamp) / 1000).toString())} seconds!`; nodeOne.appendChild(valleyTable); nodeTwo.appendChild(oasisTable); nodeOne.style.display = 'block'; nodeTwo.style.display = 'block'; } searching = !searching; }); } } else { if (node) { node.remove(); document.querySelector('.travian-kingdoms-tools-table-one').remove(); document.querySelector('.travian-kingdoms-tools-table-two').remove(); } } }, 1000); })(); const processMap = async () => { const mapVillageId = /villId:(\d+)/.exec(window.location.toString()); const { response: { privateApiKey } } = await (await fetch(`https://${window.location.hostname}/api/external.php?action=requestApiKey&[email protected]&siteName=Example&siteUrl=https://example.com&public=0`)).json(); const { response: { map: { cells }, players } } = await (await fetch(`https://${window.location.hostname}/api/external.php?action=getMapData&privateApiKey=${privateApiKey}`)).json(); const valleys = []; const oases = []; let villageX = 0; let villageY = 0; if (mapVillageId) { players.map(({ villages }) => { villages.map(({ villageId, x, y }) => { if (villageX === 0 && villageY === 0 && villageId === mapVillageId[1]) { villageX = parseInt(x); villageY = parseInt(y); } }); }); } const names = (await Promise.all(cells.map(async ({ id, x, y, resType, oasis }) => { if (['10', '11', '20', '21', '30', '31', '40', '41'].includes(oasis)) { oases.push({ externalId: parseInt(id), type: parseInt(oasis), x: parseInt(x), y: parseInt(y), distance: parseInt(Math.sqrt(Math.pow(x - villageX, 2) + Math.pow(y - villageY, 2)).toString()), elephant: 0, tiger: 0, crocodile: 0, bear: 0, wolf: 0, boar: 0, bat: 0, snake: 0, spider: 0, rat: 0, }); return `MapDetails:${id}`; } if (['11115', '3339'].includes(resType)) { valleys.push({ externalId: parseInt(id), type: parseInt(resType), x: parseInt(x), y: parseInt(y), bonus: 0, distance: parseInt(Math.sqrt(Math.pow(x - villageX, 2) + Math.pow(y - villageY, 2)).toString()), occupied: false, oases: [], }); return `MapDetails:${id}`; } }))).filter(content => content !== undefined); return { valleys, oases, names }; }; const processOases = async (session, valleys, oases, names) => { const maximum = 999; const { cache } = await (await fetch(`https://${window.location.hostname}/api/?c=cache&a=get`, { method: 'POST', body: JSON.stringify({ controller: 'cache', action: 'get', params: { names, session } }), })).json(); cache.map(({ name, data: { isOasis, isHabitable, oasisStatus, hasVillage, hasNPC, troops: { units } = { units: {} } } }) => { const innerExternalId = parseInt(name.split(':')[1]); if (isHabitable) { const valley = valleys.filter(({ externalId }) => externalId === innerExternalId)[0]; valley.occupied = parseInt(hasVillage) === 1 || parseInt(hasNPC) === 1; } if (isOasis && !['1'].includes(oasisStatus)) { const oasis = oases.filter(({ externalId }) => externalId === innerExternalId)[0]; if (units[10]) { oasis.elephant = parseInt(units[10]); oasis.elephant = oasis.elephant > maximum ? maximum : oasis.elephant; } if (units[9]) { oasis.tiger = parseInt(units[9]); oasis.tiger = oasis.tiger > maximum ? maximum : oasis.tiger; } if (units[8]) { oasis.crocodile = parseInt(units[8]); oasis.crocodile = oasis.crocodile > maximum ? maximum : oasis.crocodile; } if (units[7]) { oasis.bear = parseInt(units[7]); oasis.bear = oasis.bear > maximum ? maximum : oasis.bear; } if (units[6]) { oasis.wolf = parseInt(units[6]); oasis.wolf = oasis.wolf > maximum ? maximum : oasis.wolf; } if (units[5]) { oasis.boar = parseInt(units[5]); oasis.boar = oasis.boar > maximum ? maximum : oasis.boar; } if (units[4]) { oasis.bat = parseInt(units[4]); oasis.bat = oasis.bat > maximum ? maximum : oasis.bat; } if (units[3]) { oasis.snake = parseInt(units[3]); oasis.snake = oasis.snake > maximum ? maximum : oasis.snake; } if (units[2]) { oasis.spider = parseInt(units[2]); oasis.spider = oasis.spider > maximum ? maximum : oasis.spider; } if (units[1]) { oasis.rat = parseInt(units[1]); oasis.rat = oasis.rat > maximum ? maximum : oasis.rat; } } }); oases.sort((one, two) => { let sortation = undefined; ['elephant', 'tiger', 'crocodile', 'bear', 'wolf', 'boar', 'bat', 'snake', 'spider', 'rat'].map(animal => { if (one[animal] !== two[animal] && !sortation) { sortation = one[animal] > two[animal] ? -1 : 1; } }); return sortation ? sortation : one.distance > two.distance ? 1 : -1; }); return { valleys, oases }; }; const processValleys = (valleys, oases) => { const innerValleys = valleys.map(valley => { const bonuses = []; oases .filter(({ x, y }) => x > valley.x - 4 && x < valley.x + 4 && y > valley.y - 4 && y < valley.y + 4) .map(({ type }) => { switch (type) { case 41: bonuses.push(50); break; case 40: case 31: case 21: case 11: bonuses.push(25); break; default: bonuses.push(0); break; } valley.oases.push({ type }); }); bonuses.sort((one, two) => two - one); valley.oases.sort(({ type: oneType }, { type: twoType }) => { if ([41, 31, 21, 11].includes(twoType)) { return [41, 31, 21, 11].includes(oneType) ? twoType - oneType : 1; } else { return [41, 31, 21, 11].includes(oneType) ? -1 : twoType - oneType; } }); return { ...valley, bonus: bonuses.slice(0, 3).reduce((accumulator, bonus) => accumulator + bonus, 0) }; }); innerValleys.sort(( { type: oneType, bonus: oneBonus, distance: oneDistance, occupied: oneOccupied }, { type: twoType, bonus: twoBonus, distance: twoDistance, occupied: twoOccupied }, ) => { if (oneOccupied !== twoOccupied) { return oneOccupied - twoOccupied; } else { if (oneType !== twoType) { return twoType - oneType; } else { return oneBonus !== twoBonus ? twoBonus - oneBonus : oneDistance - twoDistance; } } }, ); return innerValleys; }; const createFooter = () => { const body = document.querySelector('body'); const text = document.createElement('div'); text.innerText = 'Search for valleys and oases'; const node = document.createElement('div'); node.style.position = 'fixed'; node.style.bottom = '0'; node.style.right = '0'; node.style.left = '0'; node.style.backgroundColor = '#000000'; node.style.color = '#FFFFFF'; node.style.lineHeight = '25px'; node.style.textAlign = 'center'; node.style.cursor = 'pointer'; node.style.zIndex = '10000'; node.classList.add('travian-kingdoms-tools'); const nodeOne = document.createElement('div'); nodeOne.style.display = 'none'; nodeOne.style.float = 'left'; nodeOne.style.width = '50%'; nodeOne.style.position = 'fixed'; nodeOne.style.bottom = '25px'; nodeOne.style.right = '50%'; nodeOne.style.left = '0'; nodeOne.style.height = '279px'; nodeOne.style.maxHeight = '279px'; nodeOne.style.backgroundColor = '#000000'; nodeOne.style.color = '#FFFFFF'; nodeOne.style.lineHeight = '25px'; nodeOne.style.textAlign = 'center'; nodeOne.style.cursor = 'pointer'; nodeOne.style.zIndex = '10000'; nodeOne.style.border = '1px solid #000000'; nodeOne.style.overflowY = 'scroll'; nodeOne.classList.add('travian-kingdoms-tools-table-one'); const nodeTwo = document.createElement('div'); nodeTwo.style.display = 'none'; nodeTwo.style.float = 'right'; nodeTwo.style.width = '50%'; nodeTwo.style.position = 'fixed'; nodeTwo.style.bottom = '25px'; nodeTwo.style.right = '0'; nodeTwo.style.left = '50%'; nodeTwo.style.height = '279px'; nodeTwo.style.maxHeight = '279px'; nodeTwo.style.backgroundColor = '#000000'; nodeTwo.style.color = '#FFFFFF'; nodeTwo.style.lineHeight = '25px'; nodeTwo.style.textAlign = 'center'; nodeTwo.style.cursor = 'pointer'; nodeTwo.style.zIndex = '10000'; nodeTwo.style.border = '1px solid #000000'; nodeTwo.style.overflowY = 'scroll'; nodeTwo.classList.add('travian-kingdoms-tools-table-two'); node.appendChild(text); body.appendChild(node); body.appendChild(nodeOne); body.appendChild(nodeTwo); return { text, node, nodeOne, nodeTwo }; }; const createTables = () => { const valleyTable = document.createElement('table'); valleyTable.style.color = '#000000'; valleyTable.style.border = '0'; valleyTable.style.cursor = 'default'; const oasisTable = document.createElement('table'); oasisTable.style.color = '#000000'; oasisTable.style.border = '0'; oasisTable.style.cursor = 'default'; return { valleyTable, oasisTable }; }; const createRow = (table, contentOne, contentTwo) => { const row = table.insertRow(); const cellOne = row.insertCell(); cellOne.style.width = '25px'; cellOne.style.borderRight = '1px solid #000000'; cellOne.style.borderBottom = '1px solid #000000'; cellOne.style.textAlign = 'center'; cellOne.innerHTML = contentOne.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' '); const cellTwo = row.insertCell(); cellTwo.style.height = '25px'; cellTwo.style.borderRight = '1px solid #000000'; cellTwo.style.borderBottom = '1px solid #000000'; cellTwo.innerHTML = contentTwo; }; const createValleyContent = ({ externalId, type, oases, bonus, distance, occupied }) => { const mapper = { 41: { name: '50%', position: '-286px -376px', opacity: '1.0' }, 40: { name: '25%', position: '-286px -376px', opacity: '0.5' }, 31: { name: '25% + 25%', position: '-132px -376px', opacity: '1.0' }, 30: { name: '25%', position: '-132px -376px', opacity: '0.5' }, 21: { name: '25% + 25%', position: '-242px -376px', opacity: '1.0' }, 20: { name: '25%', position: '-242px -376px', opacity: '0.5' }, 11: { name: '25% + 25%', position: '-198px -376px', opacity: '1.0' }, 10: { name: '25%', position: '-198px -376px', opacity: '0.5' }, }; const content = oases.map(oasis => { const { name, position, opacity } = mapper[oasis.type]; return `<div title='${name}' style='display: inline-block; width: 22px; height: 22px; margin: 0 auto; vertical-align: -4px; background-image: url("./layout/images/sprites/general.png"); background-position: ${position}; opacity: ${opacity}'></div>`; }).join(' '); return `<a href="https://${window.location.hostname}/#/page:map/window:mapCellDetails/cellId:${externalId}/centerId:${externalId}" style="color: ${occupied ? '#FF0000' : '#008800'}">The ${type.toString().padStart(5, '0')} valley</a> is ${distance.toString().padStart(3, '0')} fields away with ${bonus.toString().padStart(3, '0')}% ${content}`; }; const createOasisContent = oasis => { const { externalId, distance } = oasis; const content = [ { key: 'elephant', value: 'Elephant', position: '-40px -80px' }, { key: 'tiger', value: 'Tiger', position: '-20px -100px' }, { key: 'crocodile', value: 'Crocodile', position: '0 -100px' }, { key: 'bear', value: 'Bear', position: '-100px -80px' }, { key: 'wolf', value: 'Wolf', position: '-100px -60px' }, { key: 'boar', value: 'Boar', position: '-100px -40px' }, { key: 'bat', value: 'Bat', position: '0 0' }, { key: 'snake', value: 'Snake', position: '-100px 0' }, { key: 'spider', value: 'Spider', position: '-80px -80px' }, { key: 'rat', value: 'Rat', position: '-60px -80px' }, ].map(({ key, value, position }) => oasis[key] ? `${oasis[key].toString().padStart(3, '0')} <div title='${value}' style='display: inline-block; width: 20px; height: 20px; margin: 0 auto; vertical-align: -4px; background-image: url("./layout/images/sprites/unit/small/unit/small.png"); background-position: ${position};'></div>` : undefined, ).filter(content => content !== undefined).join(' '); return `<a href="https://${window.location.hostname}/#/page:map/window:mapCellDetails/cellId:${externalId}/centerId:${externalId}" style="color: ${content ? '#008800' : '#FF0000'}">The oasis</a> is ${distance.toString().padStart(3, '0')} fields away ${content ? `with ${content}` : ''}`; };