// ==UserScript== // @name IK tools // @namespace http://jira.zby.cz // @include *.gameforge.com/* // @grant none // @description on insert key: opens commands for Ikariam. Enter 'list' to list all possible commands // ==/UserScript== //LISTENER window.onkeyup = function(event) { //INSERT if(event.keyCode === 45) { let input = prompt('Jaký příkaz chcete provést?\n Příkaz \'list\' vypíše všechny dostupné příkazy.'); if(input) {input = input.trim();} else {return;} let cmd = input.match(/^[^\s]+/); cmd = cmd ? cmd[0] : ''; cmd = cmd.toLowerCase(); let arg = input.match(/\s.*$/); arg = arg ? arg[0].trim() : ''; if(!cmd) {return;} if(commands.hasOwnProperty(cmd)){ commands[cmd].f(arg); } else { alert('Příkaz nenalezen!'); } } //SAVE CURRENT MESSAGE let textInput = geto('js_msgTextConfirm'); if(textInput && textInput.value) { localStorage.setItem('message', textInput.value); } }; //Generic function as a shortcut to DOM const geto = id => document.getElementById(id); //unwanted HTML elements to be hidden let unwanted = [ geto('cityFlyingShopContainer'), geto('mapControls'), document.getElementsByClassName('footerleft')[0], document.getElementsByClassName('footerright')[0], geto('js_toggleControlsOn') ]; const toggleUnwanted = function() { let current = localStorage.getItem('hideUnwanted'); for(let item of unwanted) { if(item) {item.style.display = (current === 'true') ? 'block' : 'none';} } }; toggleUnwanted(); //Open a new window and write text const IKwrite = function(text) { let popup = window.open(); popup.document.write(text); popup.document.close(); }; //CURRENT STATE let state = {}; //COMMAND BINDING let commands = { list: { description: 'Vypíše všechny dostupné příkazy.', f: function(arg) { let text = '

Seznam příkazů

'; for(let i in commands) { if(commands.hasOwnProperty(i)) { text += '

' + i + ':

' + commands[i].description; } } IKwrite(text); } }, show: { description: 'Permanentně skryje balón na ambrozie a celou spodní lištu, což jsou prvky, které hodně překáží.
Program je bude automaticky schovávat, další použití příkazu je opět odkryje.', f: function(arg) { let current = localStorage.getItem('hideUnwanted'); if(!current) {localStorage.setItem('hideUnwanted', 'true');} else {localStorage.setItem('hideUnwanted', (current === 'true') ? 'false' : 'true');} toggleUnwanted(); } }, message: { description: `Tento příkaz nahraje poslední zprávu, co jste měli rozepsanou (i když už je odeslaná) a může tak obnovit text, který by byl jinak ztracen! Nebo třeba můžete snadno rozesílat stejnou zprávu více lidem.
Pokud máte zrovna otevřenou zprávu, tak ji rovnou vypíše do ní.`, f: function(arg) { let textInput = geto('js_msgTextConfirm'); let message = localStorage.getItem('message'); if(!message) { alert('Nenalezena žádná zpráva.'); } if(textInput) { textInput.value += message; } else{ IKwrite(message); } } }, pirates: { description: `Získá za vás informace o hráčích v žebříčku pevností (jméno, aliance, souřadnice), takže to nemusíte sami rozklikávat, což ušetří fakt hodně času.
Funguje jen když máte otevřenou pirátskou pevnost. Funkci nechte chvíli pracovat, narozdíl od ostatních tato chvíli trvá.`, f: function(arg) { const time = function(i) { return i ? Math.floor(i*1000 + Math.random()*500 - 250) : 0; }; state.windows = []; let ul = document.getElementById('pirateHighscore'); if(!ul) { alert('Nenalazen žebříček pirátů - otevřete si pirátskou pevnost.');return; } state.position = document.getElementsByClassName('place')[4].innerHTML .replace(/\s*\./,''); state.pts = document.getElementsByClassName('pirateBooty')[4].innerHTML .replace(',','') .match(/[\d]+/)[0]; let innerHTML = ul.innerHTML; let match = innerHTML.match(/view=island&cityId=\d*/g) .map(item => item.replace('amp;','')); let openPirate = function(address) { let a = location.href.search(/index\.php.*$/); let domain = location.href.slice(0, a); state.windows.push(window.open(domain + 'index.php?' + address)); }; for(let i = 0; i < match.length; i++) { window.setTimeout(openPirate, time(i) , match[i]); } window.setTimeout(function() { commands.pirates2.f(); }, (1000*match.length + 1000)); } }, pop: { description: 'Vypočte datum, kdy bude dosažena plná populace ve městě, abyste věděli, kdy utlumit alkoholické radovánky.
Funguje jen když máte otevřenou radnici.', f: function(arg) { let name, pop, pop2, sp, spTotal, result; name = geto('js_cityBread'); pop = geto('js_TownHallOccupiedSpace'); pop2 = geto('js_TownHallMaxInhabitants'); sp = geto('js_TownHallHappinessLargeValue'); let dgts = function(number) { number = String(number); if(number.length === 1) { number = '0' + number; } return number; }; if(!name || !pop || !pop2 || !sp) { alert('Nenalezeno okno radnice - otevřete si radnici.');return; } name = name.innerHTML; pop = parseInt(pop.innerHTML); pop2 = parseInt(pop2.innerHTML); sp = parseInt(sp.innerHTML); spTotal = sp + pop; if(Math.sign(sp)*pop2 >= Math.sign(sp)*spTotal){ alert(`Populace nebude nikdy dosažena. Bude se pouze neustále blížit k hodnotě ${spTotal}.`);return; } result = 50*Math.log((sp)/(spTotal - pop2)); if(result <= 0){ alert('Této populace už bylo dosaženo v minulosti nebo právě teď...');return; } result = new Date(Date.now() + result*3600*1000); result = result.getDate() + '.' + (result.getMonth()+1) + '.' + result.getFullYear() + ', ' + dgts(result.getHours()) + ':' + dgts(result.getMinutes()); IKwrite(`Dosažení limitu populace ve městě ${name}: ${result}`); } }, faith: { description: 'Zjistí existenci tajných parazitů na ostrově. Nezjistí, kdo to je, pouze že nějací existují. Viníky pak odhalte sami.
Funguje jen když máte otevřený ostrovní zázrak.', f: function(arg) { let table = geto('wonderOtherPlayers'); if(!table) { alert('Tabulka hráčů nenalezena - otevřete si OSTROVNÍ zázrak.');return; } let tds = table.innerHTML.match(/.*?<\/td>/gm) .map(item => item.replace(/<.*?>|%/g,'')); let players = [], monks = 0, faithFull = 0, faith = 0; for(let i = 0; i < tds.length; i += 3) { players.push({ monks: parseInt(tds[i]), faithPlayer: parseFloat(tds[i+1])/100, faithShare: parseFloat(tds[i+2])/100 }); } for(let item of players) { if(item.faithPlayer > 0) { item.faithShareFull = item.faithShare / item.faithPlayer; } else { item.faithShareFull = 0; } monks += item.monks; faithFull += item.faithShareFull; faith += item.faithShare; } let info = function(monks, faith) { return '

Celková konečná víra je: ' + Math.round(faith*10000)/100 + '%.
' + 'Chybějící kněží na 100% víry: ' + Math.ceil(monks*(1/faith - 1)); }; if(faithFull === 0) { alert('

Nelze určit!

Nikdo na ostrově nemá víru - nelze stanovit, zda-li má někdo chrám.'); } else if(faithFull < 0.99) { IKwrite( '

Byli zjištěni paraziti!

Někdo z těch, kdo mají nulu, má chrám.' + info(monks, faith) ); } else { IKwrite( '

Vše je v pořádku.

Z těch, kdo mají nulu, nikdo nemá chrám, takže nejsou žádní paraziti.' + info(monks, faith) ); } } }, dranc: { description:`První půlka funkce na snadný výpočet lodí potřebných na kompletní vydrancování cílového města. Otevřete špehovací zprávu a spustťe tuto funkci, následně otevřete cílové město a spusťte funkci 'dranc2'.
Otevřete právě tu špehovací zprávu, která se cílového města týká! (když jich otevřete více, vezme se ta poslední)`, f: function(arg) { let reports = document.getElementsByClassName('report'); if(!reports || reports.length === 0) { alert('Musí být otevřena zpráva o špionáži surovin.');return; } let newArr = []; for(let i = 0; i < reports.length; i++) { if(reports[i].tagName.toLowerCase() === 'tr') {newArr.push(reports[i]);} } for(let i = 0; i < newArr.length; i++) { if(newArr[i].className.indexOf('invisible') === -1) { let sur = newArr[i].innerHTML.match(/[\d\,]+<\/td>/g); if(sur && sur.length === 5) { sur = sur.map(item => parseInt(item.match(/[\d\,]+/)[0].replace(',',''))); localStorage.setItem('dranc', JSON.stringify(sur)); alert('OK, nyní otevřete cílové město a spusťte funkci \'dranc2\'.');return; } } } alert('Něco je špatně. Ujistěte se, že máte otevřenou POUZE zprávu o špionáži surovin!'); } }, dranc2: { description: 'Tato funkce slouží k dokončení funkce \'dranc\', spusťte ji v obrazovce cílového města.
Pokud ji spustíte v jiném městě, výsledkem bude nesmysl.', f: function(arg) { try {IKwrite(F.dranc2());} catch (err) {alert(err);} } }, walls: { description: `Spočte potřebné množství obléhacích zbraní ke zboření zdi za jedno kolo (hodí se k drancování) a též kolik zdí strhne plně obsazené bojiště za jedno kolo (hodí se k boji).
Spusťte v obrazovce města, do kterého chcete vtrhnout.
Funguje v jakémkoliv městě, takže můžete také zjistit, jak odolná je vaše zeď.
Pokud zadáte argument, bude to úroveň Hefaistovy kovárny, např. 'walls 5'`, f: function(arg) { try {IKwrite(F.walls(arg));} catch (err) {alert(err);} } }, dranc3: { description: `Současné spuštění 'dranc2' a 'walls' pro ušetření času ;-)`, f: function(arg) { let txt = ''; try {txt += F.dranc2();} catch (err) {alert(err);return;} txt += '

'; try {txt += F.walls(0);} catch (err) {alert(err);return;} IKwrite(txt); } }, train: { description: 'Vyžaduje číselný argument, např. \'train 3\'
Vyplní formulář na trénování lodí či jednotek tak, aby to vyšlo na počet kol, který zadáte v argumentu.
Funguje jen když máte otevřenou kasárnu nebo loděnici.', f: function(arg) { let ul = geto('units'); let rounds = parseFloat(arg); if(!ul) { alert('Okno kasárny či loděnice nenalezeno.');return; } else if(isNaN(rounds)) { alert('Zadaný počet kol není číslo! Zkuste znovu...');return; } if(document.getElementsByClassName('unit ship_steamboat').length > 0) { let pb = geto('js_barracksBuyTextfield2'), lb = geto('js_barracksBuyTextfield3'), rl = geto('js_barracksBuyTextfield7'), nb = geto('js_barracksBuyTextfield10'); pb.value = Math.round(15*rounds); lb.value = Math.round(18*rounds); rl.value = Math.round(4*rounds); nb.value = Math.round(2*rounds); pb.click();lb.click();rl.click();nb.click(); } else if(document.getElementsByClassName('unit phalanx').length > 0) { let hop = geto('js_barracksBuyTextfield1'), ser = geto('js_barracksBuyTextfield4'), del = geto('js_barracksBuyTextfield10'), bal = geto('js_barracksBuyTextfield12'); hop.value = Math.round(156*rounds); ser.value = Math.round(120*rounds); del.value = Math.round(8*rounds); bal.value = Math.round(22*rounds); hop.click();ser.click();del.click();bal.click(); } } }, black: { description: `Vyžaduje argument o třech číselch, např. 'black 6 5 20'
Pouze informační funkce na ulehčení prodeje armády.
Zeptá se vás na aktuální cenu dřeva, síry, marži v procentech (první, druhý a třetí argument respektive) a vypočte ceny jednotek.
Pokud to nevyplníte nebo vyplníte špatně, vezme hodnoty 6, 6 a 20%.`, f: function(arg) { const round = function(num) { return Math.log10(num) > 3 ? Math.round(num/100)*100 : Math.round(num/10)*10; }; let units = [ {name:'Parní beranidlo', res: [400, 800], type: 'ship'}, {name:'Lehké beranidlo', res: [250, 0], type: 'ship'}, {name:'Raketová loď', res: [200, 1200], type: 'ship'}, {name:'Nosič balónů', res: [700, 700], type: 'ship'}, {name:'Dělová loď', res: [220, 900], type: 'ship'}, {name:'Rychlý člun', res: [40, 280], type: 'ship'}, {name:'Hoplit', res: [40, 30], type: 'unit'}, {name:'Šermíř', res: [30, 30], type: 'unit'}, {name:'Parní obr', res: [130, 180], type: 'unit'}, {name:'Dělo', res: [300, 1250], type: 'unit'}, {name:'Balón', res: [40, 250], type: 'unit'}, {name:'Střelec', res: [50, 150], type: 'unit'}, {name:'Gyrokoptéra', res: [25, 100], type: 'unit'} ]; let correctly = true; arg = arg.split(' ') .map(item => parseInt(item)) .filter(item => !isNaN(item)); if(arg.length !== 3) { arg = [6,6,20]; correctly = false; } units.forEach(function(item) { let W = item.res[0]*arg[0]*(1 + arg[2]/100); let S = item.res[1]*arg[1]*(1 + arg[2]/100); item.prices = { full: round(W + S), carpenter: round(W*0.68 + S), firework: round(0.68*(W + S)) }; }); let txt = (correctly ? '' : 'Hodnoty nebyly zadány správně
'); txt += `Cena dřeva: ${arg[0]}
Cena síry: ${arg[1]}
Marže: ${arg[2]}%
První je vždy cena bez truhlárny a bez zkušebny (plná), pak cena s truhlárnou (redukovaná) a pak s truhlárnou i zkušebnou (minimální)

Ceny jednotek

Ceny lodí

'; IKwrite(txt); } }, pirates2: { description: `Nepotřebné, toto je již obsaženo v 'pirates'
Získá za vás informace o hráčích v žebříčku pevností, takže to nemusíte sami rozklikávat.
Část druhá - získání informací a zavření oken.
Funguje jen po úspěšném provedení příkazu 'pirates'`, f: function(arg) { if(state.windows.length > 0) { let message = 'Měl jsem ' + state.pts + ' bodů (' + state.position + '.):

'; let count = 0; for(let i in state.windows) { if(state.windows.hasOwnProperty(i)){ count ++; let w = state.windows[i]; w.close(); let p = [ w.document.getElementById('js_selectedCityOwnerName'), w.document.getElementById('js_selectedCityAllyName'), w.document.getElementById('js_islandBreadCoords') ]; for(let item of p) { if(!item) {alert('Data jsou z nějakého důvodu neplatná. Funkce se ukončuje.');return;} } p = p.map(item => item.innerHTML); p[1] = (p[1] === '-') ? ' ' : (' (' + p[1] + ') '); message += p[0] + p[1] + p[2] + '
'; if(count === 4){ message += 'já
'; } } } message += '
' + state.windows.length + ' hráčů'; IKwrite(message); } else { alert('Potřebná okna nenalazena. Spusťte nejprve příkaz pirates.'); } } } }; //FUNCTION TOOLBOX let F = { dranc2: function() { let local = localStorage.getItem('dranc'); let name = geto('js_cityBread'); if(!local) { throw 'Nejprve je třeba spustit funkci \'dranc \' u otevřené zprávy o špionáži surovin.'; } local = JSON.parse(local); if(!geto('js_CityPosition0Link') || !name) { throw 'Musí být otevřena obrazovka cílového města (toho, kterého se týkala zpráva o špionáži).'; } name = name.innerHTML; let warehouseTotal = 0; let warehouseElems = document.getElementsByClassName('warehouse'); for(let i = 0; i < warehouseElems.length; i++) { let m = warehouseElems[i].className.match(/level\d{1,2}/); if(m) { warehouseTotal += parseInt(m[0].match(/\d{1,2}/)); } } let safe = warehouseTotal*480+100; let safeI = warehouseTotal*80+17; let count = function(safe, sur) { let free = 0; for(let s of sur) { if(s > safe) {free += s - safe;} } return Math.ceil(free/500); }; return `Cílové město ${name}:
Počet lodí na drancování, pokud je hráč:
NEAKTIVNÍ: ${count(safeI,local)}
AKTIVNÍ: ${count(safe,local)}`; }, walls: function(arg) { let wall = geto('js_CityPosition14Link'); let hall = geto('js_CityPosition0Link'); let name = geto('js_cityBread'); if(!wall || !wall.title || !hall || !hall.title || !name) { throw 'Město nenalezeno - otevřete město, které chcete dobývat.'; } hall = hall.title.match(/\((\d+)\)/); wall = wall.title.match(/\((\d+)\)/); name = name.innerHTML; if(!wall) { return 'Město nemá žádnou zeď. Není potřeba žádných obléhacích zbraní.'; } wall = parseInt(wall[1]); hall = parseInt(hall[1]); let hefEffect = 0; if(arg) { let hef = parseInt(arg); if(isNaN(hef)) { throw 'Zadaná úroveň Hefaistovy kovárny není číslo.'; } else if(hef >= 5){hefEffect = 0.2;} else if(hef >= 3){hefEffect = 0.15;} else if(hef >= 1){hefEffect = 0.1;} else{hefEffect = 0;} } let max = 30; if(isNaN(hall)){max = 30;} else if(hall >= 25){max = 30;} else if(hall >= 17){max = 24;} else if(hall >= 10){max = 18;} else if(hall >= 5){max = 12;} else{max = 6;} let txt = `Cílové město ${name}:
Bojiště pojme ${max} obléhacích zbraní

`; let weapons = [ {name: 'beranidlo', dmg: 80}, {name: 'katapult', dmg: 133}, {name: 'dělo', dmg: 270} ]; for(let w of weapons) { txt += ``; let hp = 100 + wall*50; let dmg = w.dmg*(1+hefEffect) - wall*4; let res1 = hp/dmg; let res2 = max*dmg/hp; res2 = res2 > 0 ? res2 : 0; if(res1 < 0 || !isFinite(res1)) { txt += ``; } else if(res1 > max) { txt += ``; } else{ txt += ``; } txt += ``; txt += ''; } return txt; } }; console.log('TPM script executed');
ZbraňZbraní na jednu zeďPlné bojiště strhne zdí
${w.name}NELZE. Zbraň vůbec nedokáže zeď poškodit!NELZE. Zbraní by bylo potřeba ${Math.ceil(res1)}.${Math.ceil(res1)}${res2.toFixed(1)}