Created
September 7, 2024 10:25
-
-
Save devrsantos/f7978d293a8fbd1ad219321869ddfeec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var scriptData = { | |
name: 'Barbs Finder', | |
version: 'v1.4.0', | |
author: 'RedAlert', | |
authorUrl: 'https://twscripts.ga/', | |
helpLink: 'https://forum.tribalwars.net/index.php?threads/barb-finder-with-filtering.285289/', | |
}; | |
// User Input | |
if (typeof DEBUG !== 'boolean') DEBUG = false; | |
// CONSTANTS | |
var VILLAGE_TIME = 'mapVillageTime'; // localStorage key name | |
var VILLAGES_LIST = 'mapVillagesList'; // localStorage key name | |
var TIME_INTERVAL = 60 * 60 * 1000; // fetch data every hour | |
// Globals | |
var villages = []; | |
var barbarians = []; | |
// Translations | |
var translations = { | |
pt_BR: { | |
'Barbs Finder': 'Buscador de aldeias bárbaras', | |
'Min Points:': 'Pontos mÃnimos:', | |
'Max Points:': 'Pontos máximos:', | |
'Radius:': 'Campo:', | |
'Barbs found:': 'Bárbaras encontradas:', | |
'Coordinates:': 'Coordenadas:', | |
'Error while fetching "village.txt"!': 'Erro ao procurar "village.txt"!', | |
Coords: 'Coords', | |
Points: 'Pontos', | |
'Dist.': 'Dist.', | |
Attack: 'Attack', | |
Filter: 'Filtro', | |
Reset: 'Reset', | |
'No barbarian villages found!': 'Não foram encontradas aldeias bárbaras!', | |
'Current Village:': 'Aldeia Atual:', | |
'Sequential Scout Script:': 'Coordenadas para uso em script:', | |
Help: 'Ajuda', | |
}, | |
}; | |
// Init Debug | |
initDebug(); | |
// Init Translations Notice | |
initTranslationsNotice(); | |
// Auto-update localStorage villages list | |
if (localStorage.getItem(TIME_INTERVAL) != null) { | |
var mapVillageTime = parseInt(localStorage.getItem(VILLAGE_TIME)); | |
if (Date.parse(new Date()) >= mapVillageTime + TIME_INTERVAL) { | |
// hour has passed, refetch village.txt | |
fetchVillagesData(); | |
} else { | |
// hour has not passed, work with village list from localStorage | |
var data = localStorage.getItem(VILLAGES_LIST); | |
villages = CSVToArray(data); | |
init(); | |
} | |
} else { | |
// Fetch village.txt | |
fetchVillagesData(); | |
} | |
// Fetch 'village.txt' file | |
function fetchVillagesData() { | |
$.get('map/village.txt', function (data) { | |
villages = CSVToArray(data); | |
localStorage.setItem(VILLAGE_TIME, Date.parse(new Date())); | |
localStorage.setItem(VILLAGES_LIST, data); | |
}) | |
.done(function () { | |
init(); | |
}) | |
.fail(function (error) { | |
console.error(`${scriptInfo()} Error:`, error); | |
UI.ErrorMessage(`${tt('Error while fetching "village.txt"!')}`, 4000); | |
}); | |
} | |
// Initialize Script | |
function init() { | |
// Filter out only Barbarian villages | |
findBarbarianVillages(); | |
// Show popup | |
var content = ` | |
<div class="ra-mb15"> | |
<strong>${tt('Current Village:')}</strong> | |
<a href="/game.php?screen=info_village&id=${game_data.village.id}" target="_blank" rel="noopener noreferrer"> | |
${game_data.village.name} | |
</a> | |
</div> | |
<div class="ra-flex ra-mb10"> | |
<div class="ra-flex-4"> | |
<label for="minPoints" class="ra-fw600"">${tt('Min Points:')}</label> | |
<input type="text" id="minPoints" class="ra-form-control" value="26"> | |
</div> | |
<div class="ra-flex-4"> | |
<label for="maxPoints" class="ra-fw600"">${tt('Max Points:')}</label> | |
<input type="text" id="maxPoints" class="ra-form-control" value="12154"> | |
</div> | |
<div class="ra-flex-4"> | |
<label for="radius" class="ra-fw600">${tt('Radius:')}</label> | |
<select id="radius_choser" class="ra-form-control"> | |
<option value="10">10</option> | |
<option value="20">20</option> | |
<option value="30">30</option> | |
<option value="40">40</option> | |
<option value="50" selected>50</option> | |
<option value="60">60</option> | |
<option value="70">70</option> | |
<option value="80">80</option> | |
<option value="90">90</option> | |
<option value="100">100</option> | |
<option value="110">110</option> | |
<option value="120">120</option> | |
<option value="130">130</option> | |
<option value="140">140</option> | |
<option value="150">150</option> | |
</select> | |
</div> | |
</div> | |
<a href="javascript:void(0);" onClick="filterBarbs();" class="btn btn-confirm-yes"> | |
${tt('Filter')} | |
</a> | |
<a href="javascript:void(0);" onClick="resetFilters();" class="btn btn-confirm-no"> | |
${tt('Reset')} | |
</a> | |
<p class="ra-fs12"> | |
<strong>${tt('Barbs found:')}</strong> | |
<span id="barbsCount">0</span> | |
</p> | |
<div class="ra-mb10"> | |
<label class="ra-fw600" for="barbCoordsList">${tt('Coordinates:')}</label> | |
<textarea id="barbCoordsList" class="ra-textarea" readonly></textarea> | |
</div> | |
<div class="ra-mb10"> | |
<label class="ra-fw600" for="barbScoutScript">${tt('Sequential Scout Script:')}</label> | |
<textarea id="barbScoutScript" class="ra-textarea" readonly></textarea> | |
</div> | |
<div id="barbariansTable" style="display:none;max-height:240px;overflow-y:auto;margin-bottom:8px;"></div> | |
<div id="noBarbariansFound" style="display:none;"> | |
<p><strong>${tt('No barbarian villages found!')}</strong> | |
</div> | |
`; | |
const popupContent = preparePopupContent(content); | |
Dialog.show('content', popupContent); | |
} | |
// Populate villages list | |
function findBarbarianVillages() { | |
villages.forEach((village) => { | |
if (village[4] == '0') { | |
barbarians.push(village); | |
} | |
}); | |
if (DEBUG) { | |
console.debug(`${scriptInfo()} Barbarian Villages:`, barbarians); | |
} | |
} | |
// Filter Barbarians | |
function filterBarbs() { | |
var minPoints = parseInt($('#minPoints').val().trim()); | |
var maxPoints = parseInt($('#maxPoints').val().trim()); | |
var radius = parseInt($('#radius_choser').val()); | |
if (DEBUG) { | |
console.debug(`${scriptInfo()} Minimum Points:`, minPoints); | |
console.debug(`${scriptInfo()} Maximum Points:`, maxPoints); | |
} | |
// Filter by min and max points | |
const filteredBarbs = barbarians.filter((barbarian) => { | |
return barbarian[5] >= minPoints && barbarian[5] <= maxPoints; | |
}); | |
// Filter by radius | |
const filteredByRadiusBarbs = filteredBarbs.filter((barbarian) => { | |
var distance = calcDistanceFromCurrentVillage(barbarian); | |
if (distance <= radius) { | |
return barbarian; | |
} | |
}); | |
updateUI(filteredByRadiusBarbs); | |
} | |
// Reset Filters | |
function resetFilters() { | |
$('#minPoints').val(26); | |
$('#maxPoints').val(12154); | |
$('#radius_choser').val('20'); | |
$('#barbsCount').text('0'); | |
$('#barbCoordsList').text(''); | |
$('#barbScoutScript').val(''); | |
$('#barbariansTable').hide(); | |
$('#barbariansTable').html(''); | |
} | |
// Update UI | |
function updateUI(barbs) { | |
if (barbs.length > 0) { | |
var barbariansCoordsArray = getVillageCoord(barbs); | |
var barbariansCount = barbariansCoordsArray.length; | |
var barbariansCoordsList = barbariansCoordsArray.join(' '); | |
var scoutScript = generateScoutScript(barbariansCoordsList); | |
var tableContent = generateBarbariansTable(barbs); | |
$('#barbsCount').text(barbariansCount); | |
$('#barbCoordsList').text(barbariansCoordsList); | |
$('#barbScoutScript').val(scoutScript); | |
$('#barbariansTable').show(); | |
$('#barbariansTable').html(tableContent); | |
} else { | |
resetFilters(); | |
$('#noBarbariansFound').fadeIn(200); | |
setTimeout(function () { | |
$('#noBarbariansFound').fadeOut(200); | |
}, 4000); | |
} | |
} | |
// Generate Table | |
function generateBarbariansTable(barbs) { | |
if (barbs.length < 1) return; | |
var barbariansWithDistance = []; | |
barbs.forEach((barb) => { | |
var distance = calcDistanceFromCurrentVillage(barb); | |
barbariansWithDistance.push([...barb, distance]); | |
}); | |
barbariansWithDistance.sort((a, b) => { | |
return a[7] - b[7]; | |
}); | |
var tableRows = generateTableRows(barbariansWithDistance); | |
var tableContent = ` | |
<table class="vis overview_table ra-table" width="100%"> | |
<thead> | |
<tr> | |
<th> | |
# | |
</th> | |
<th> | |
K | |
</th> | |
<th> | |
${tt('Coords')} | |
</th> | |
<th> | |
${tt('Points')} | |
</td> | |
<th> | |
${tt('Dist.')} | |
</th> | |
<th> | |
${tt('Attack')} | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
${tableRows} | |
</tbody> | |
</table> | |
`; | |
return tableContent; | |
} | |
// Generate Table Rows | |
function generateTableRows(barbs) { | |
var renderTableRows = ''; | |
barbs.forEach((barb, index) => { | |
index++; | |
var continent = barb[3].charAt(0) + barb[2].charAt(0); | |
renderTableRows += ` | |
<tr> | |
<td class="ra-tac"> | |
${index} | |
</td> | |
<td class="ra-tac"> | |
${continent} | |
</td> | |
<td class="ra-tac"> | |
<a href="game.php?screen=info_village&id=${barb[0]}" target="_blank" rel="noopener noreferrer"> | |
${barb[2]}|${barb[3]} | |
</a> | |
</td> | |
<td>${formatAsNumber(barb[5])}</td> | |
<td class="ra-tac">${barb[7]}</td> | |
<td class="ra-tac"> | |
<a href="/game.php?screen=place&target=${ | |
barb[0] | |
}&light=1" onClick="highlightOpenedCommands(this);" target="_blank" rel="noopener noreferrer" class="btn"> | |
${tt('Attack')} | |
</a> | |
</td> | |
</tr> | |
`; | |
}); | |
return renderTableRows; | |
} | |
// Highlight Opened Commands | |
function highlightOpenedCommands(element) { | |
element.classList.add('btn-confirm-yes'); | |
element.classList.add('btn-already-sent'); | |
element.parentElement.parentElement.classList.add('already-sent-command'); | |
} | |
// Helper: Scout Script Generator | |
function generateScoutScript(barbsList) { | |
return `javascript:coords='${barbsList}';var doc=document;if(window.frames.length>0 && window.main!=null)doc=window.main.document;url=doc.URL;if(url.indexOf('screen=place')==-1)alert('Use the script in the rally point page!');coords=coords.split(' ');index=0;farmcookie=document.cookie.match('(^|;) ?farm=([^;]*)(;|$)');if(farmcookie!=null)index=parseInt(farmcookie[2]);if(index>=coords.length)alert('All villages were extracted, now start from the first!');if(index>=coords.length)index=0;coords=coords[index];coords=coords.split('|');index=index+1;cookie_date=new Date(2030,1,1);document.cookie ='farm='+index+';expires='+cookie_date.toGMTString();doc.forms[0].x.value=coords[0];doc.forms[0].y.value=coords[1];$('#place_target').find('input').val(coords[0]+'|'+coords[1]);doc.forms[0].light.value=1;`; | |
} | |
// Helper: Calculate distance between current and a given village | |
function calcDistanceFromCurrentVillage(village) { | |
var x1 = game_data.village.x, | |
y1 = game_data.village.y, | |
x2 = village[2], | |
y2 = village[3]; | |
//calculate distance from current village | |
var a = x1 - x2; | |
var b = y1 - y2; | |
var distance = Math.round(Math.hypot(a, b)); | |
return distance; | |
} | |
// Helper: Get Villages Coords Array | |
function getVillageCoord(villages) { | |
var villageCoords = []; | |
villages.forEach((village) => { | |
villageCoords.push(village[2] + '|' + village[3]); | |
}); | |
return villageCoords; | |
} | |
// Helper: Format as number | |
function formatAsNumber(number) { | |
return parseInt(number).toLocaleString('de'); | |
} | |
//Helper: Convert CSV data into Array | |
function CSVToArray(strData, strDelimiter) { | |
strDelimiter = strDelimiter || ','; | |
var objPattern = new RegExp( | |
'(\\' + strDelimiter + '|\\r?\\n|\\r|^)' + '(?:"([^"]*(?:""[^"]*)*)"|' + '([^"\\' + strDelimiter + '\\r\\n]*))', | |
'gi' | |
); | |
var arrData = [[]]; | |
var arrMatches = null; | |
while ((arrMatches = objPattern.exec(strData))) { | |
var strMatchedDelimiter = arrMatches[1]; | |
if (strMatchedDelimiter.length && strMatchedDelimiter !== strDelimiter) { | |
arrData.push([]); | |
} | |
var strMatchedValue; | |
if (arrMatches[2]) { | |
strMatchedValue = arrMatches[2].replace(new RegExp('""', 'g'), '"'); | |
} else { | |
strMatchedValue = arrMatches[3]; | |
} | |
arrData[arrData.length - 1].push(strMatchedValue); | |
} | |
return arrData; | |
} | |
// Helper: Generates script info | |
function scriptInfo() { | |
return `[${scriptData.name} ${scriptData.version}]`; | |
} | |
// Helper: Prepare Popup Content | |
function preparePopupContent(popupBody, minWidth = '340px', maxWidth = '360px') { | |
const popupHeader = ` | |
<h3 class="ra-fs18 ra-fw600"> | |
${tt(scriptData.name)} | |
</h3> | |
<div class="ra-body">`; | |
const popupFooter = `</div><small><strong>${tt(scriptData.name)} ${scriptData.version}</strong> - <a href="${ | |
scriptData.authorUrl | |
}" target="_blank" rel="noreferrer noopener">${scriptData.author}</a> - <a href="${ | |
scriptData.helpLink | |
}" target="_blank" rel="noreferrer noopener">${tt('Help')}</a></small>`; | |
const popupStyle = ` | |
<style> | |
.popup_box_content { overflow-y: hidden; } | |
.ra-body { width: 100%; min-width: ${minWidth}; max-width: ${maxWidth}; box-sizing: border-box; } | |
.ra-fs12 { font-size: 12px; } | |
.ra-fs16 { font-size: 16px; } | |
.ra-fs18 { font-size: 18px; } | |
.ra-fw600 { font-weight: 600; } | |
.ra-mb10 { margin-bottom: 10px; } | |
.ra-mb15 { margin-bottom: 15px; } | |
.ra-tac { text-align: center; } | |
.ra-textarea { width: 100%; height: 80px; box-sizing: border-box; padding: 5px; resize: none; } | |
.ra-textarea:focus { box-shadow: none; outline: none; border: 1px solid #000; background-color: #eee; } | |
.ra-table { border-spacing: 2px; border-collapse: separate; margin-bottom: 5px; border: 2px solid #f0e2be; } | |
.ra-table th { text-align: center; } | |
.ra-table td { padding: 1px 2px; } | |
.ra-table td a { word-break: break-all; } | |
.ra-table tr:nth-of-type(2n) td { background-color: #f0e2be } | |
.ra-table tr:nth-of-type(2n+1) td { background-color: #fff5da; } | |
.ra-form-control { font-size: 12px; padding: 4px; width: 100%; box-sizing: border-box; } | |
.ra-flex { display: flex; flex-flow: row wrap; justify-content: space-between; } | |
.ra-flex-6 { flex: 0 0 48%; } | |
.ra-flex-4 { flex: 0 0 30.5%; } | |
.btn-already-sent { padding: 3px; } | |
.already-sent-command { opacity: 0.6; } | |
</style> | |
`; | |
let popupContent = ` | |
${popupHeader} | |
${popupBody} | |
${popupFooter} | |
${popupStyle} | |
`; | |
return popupContent; | |
} | |
// Helper: Prints universal debug information | |
function initDebug() { | |
console.debug(`${scriptInfo()} It works 🚀!`); | |
console.debug(`${scriptInfo()} HELP:`, scriptData.helpLink); | |
if (DEBUG) { | |
console.debug(`${scriptInfo()} Market:`, game_data.market); | |
console.debug(`${scriptInfo()} World:`, game_data.world); | |
console.debug(`${scriptInfo()} Screen:`, game_data.screen); | |
console.debug(`${scriptInfo()} Game Version:`, game_data.majorVersion); | |
console.debug(`${scriptInfo()} Game Build:`, game_data.version); | |
console.debug(`${scriptInfo()} Locale:`, game_data.locale); | |
console.debug(`${scriptInfo()} Premium:`, game_data.features.Premium.active); | |
} | |
} | |
// Helper: Text Translator | |
function tt(string) { | |
const gameLocale = game_data.locale; | |
if (translations[gameLocale] !== undefined) { | |
return translations[gameLocale][string]; | |
} else { | |
return translations['en_DK'][string]; | |
} | |
} | |
// Helper: Translations Notice | |
function initTranslationsNotice() { | |
const gameLocale = game_data.locale; | |
if (translations[gameLocale] === undefined) { | |
UI.ErrorMessage( | |
`No translation found for <b>${gameLocale}</b>. <a href="${scriptData.helpLink}" class="btn" target="_blank" rel="noreferrer noopener">Add Yours</a> by replying to the thread.`, | |
4000 | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment