· 8 years ago · Dec 22, 2017, 03:58 AM
1var twf = {
2 init: function() {
3 twf.helper.init();
4 twf.data.init();
5 twf.reports.init();
6 twf.attacks.init();
7 twf.helper.log("Todos os sistemas estão prontos.", twf.helper.MESSAGE_SUCCES);
8 $('#buttons').children().prop('disabled', false).removeClass('btn-disabled');
9 if (twf.data.settings.reportFarmer) {
10 twf.attacks.attackButton.prop('disabled', true).addClass('btn-disabled');
11 twf.helper.log("Você habilitou o Report Farmer. Pressione o botão \"Leia relatórios\" - para continuar ou desative o Report Farmer.", twf.helper.MESSAGE_WARNING);
12 }
13 },
14 /*
15 Temos dois ataques diferentes: baseado em modelo geral e baseado em relatórios
16
17 Para o relatório baseado, fazemos o seguinte: raspe todos os relatórios (<X horas atrás) @ init
18 Para cada relatório, mantenha todas as informações em algum objeto (ordená-lo pela distância para a atual lavagem, no init, e apenas comece a atacar, se possÃvel)
19 LocalStorage armazene-os com seus coords como id: salve o seguinte: wall, resource poits, storage, oculting, lastReportAttack / ScoutTime, barb
20 (De esconder e armazenar você pode calcular o máximo de coisas inesquecÃveis)
21
22 Sempre que um ataque atacar, atualizamos as mensagens e carregamos a próxima.
23 Se não houver escoteiros disponÃveis para o ataque, assumimos 0 res esquerda (talvez altere isso para algum heurÃstico (por exemplo) esperado gs - haul?)
24
25 (talvez acompanhe quais aldeias já estamos atacando, pois quando usamos várias fazendas ou quando reorganizamos suas coisas com base em
26 alguma heurÃstica)
27 Em seguida, ordenamos as aldeias em termos de eficiência, levando em consideração a função prevista (atual, minas, armazenamento, timeSinceScout, tempo de viagem)
28 e eficiência de tempo (por exemplo, res / hour)
29 nós descontamos com o passar do tempo desde o escuteiro, então desperdiçamos pequenas tropas por lançamentos sem sucesso. Nós também enviamos uma quantidade mÃnima de luzes, com base na parede
30 Isso também deve ser levado em conta, porque desperdiçar muitas luzes na pequena fazenda, mas a parede alta é uma merda
31
32
33
34 reports: {
35 "123|654": {
36 buildings: {
37 wall: 0,
38 iron: 5,
39 clay: 5,
40 wood: 5,
41 warehouse: 10,
42 hiding: 3,
43 },
44 isBarb: true,
45 lastAttack: Date(xx),
46 lastScout: Date(xx),
47 lostTroops: true,
48 currentlyUnderAttack: false (//update in attack module, then set false when receiving report),
49 resLeft: 0
50 }
51 }
52 functions
53
54 loadOldReportData()
55 readReport()
56 saveReport()
57 expectedRes(resLastReport, MineLevels, StorageLevel, HidingLevel, timeSinceLastReport, travelTime/coords)
58 calcDistance()
59 orderVillages(someHeuristic, where currentlyUnderAttack discounts very hard e.g. /1000)
60 onFrameLoaded();
61 getReportsOnPage();
62
63 */
64
65 // só começamos a leitura automática desde o momento em que o startattack é executado
66 // se não analisamos os relatórios de antemão, com o erro e pedimos análises
67 reports: {
68 firstRunFinished: false,
69 currentlyReading: false,// flag para verificar se está lendo agora.
70 allowAutomaticReading: false,
71 reportFrame: null, // contém o quadro
72 lastRun: null, // hora de inÃcio da corrida anterior
73 thisRun: null,// hora de inÃcio da corrida atual
74 currentPage: 1, // página atual do reportList (1-indexed)
75 data: {},
76 reportsToRead: [],
77 init: function() {
78 $("#start_reports").click(twf.reports.startReading);
79 $("#stop_reports").click(twf.reports.stopReading);
80 console.log(this.lastRun);
81
82 twf.reports.loadOldReportData();
83
84 twf.reports.reportParserUrl = "/game.php?village=" + game_data.village.id + "&screen=report&mode=attack&group_id=-1&view=";
85 twf.reports.reportListUrl = "/game.php?village=" + game_data.village.id + "&screen=report&mode=attack&group_id=-1&from=";
86
87 twf.reports.reportFrame = twf.helper.createHiddenFrame(twf.reports.reportListUrl + "0", twf.reports.onFrameLoaded, "report_parser_hidden_frame");
88
89 this.lastRun = new Date(twf.data.loadWorldLevel('reports_lastRun'));
90 twf.helper.log("Informe o módulo do Farm pronto.", twf.helper.MESSAGE_SUCCES);
91 },
92 startReading: function() {
93 // não faça nada se estamos ocupados
94 if (twf.reports.currentlyReading) {
95 twf.helper.log("Já está lendo. Não começando de novo.", twf.helper.MESSAGE_WARNING);
96 return;
97 }
98 // esta é uma substituição manual, então remova o temporizador
99 if (twf.data.timers.reportPollTimer) {
100 clearTimeout(twf.data.timers.reportPollTimer);
101 twf.data.timers.reportPollTimer = null;
102 console.debug("Removido reportPollTimer desde o inÃcioReading");
103 }
104
105 // handle buttons
106 $("#start_reports").hide();
107 $("#stop_reports").show();
108
109 // reset vars
110 twf.reports.currentlyReading = true;
111 twf.reports.currentPage = 1;
112 twf.reportsToRead = [];
113
114 twf.reports.thisRun = twf.helper.getServerTime(twf.reports.reportFrame);
115
116 // recarregue a página de modo que ligue o fogo
117 twf.helper.spinner.show();
118 twf.reports.reportFrame.attr('src', twf.reports.reportListUrl + "0");
119 },
120 stopReading: function() {
121 // lidar com botões
122 $("#start_reports").show();
123 $("#stop_reports").hide();
124
125 // desligue a leitura atual
126 twf.reports.currentlyReading = false;
127
128 // verifique se devemos habilitar os ataques
129 if (twf.reports.firstRunFinished) {
130 twf.attacks.attackButton.removeClass("btn-disabled").prop("disabled", false);
131 }
132
133 // atualize lastRun e guarde-o se terminarmos a execução
134 if (twf.reports.thisRun && twf.reports.firstRunFinished) {
135 twf.reports.lastRun = twf.reports.thisRun;
136 twf.data.storeWorldLevel('reports_lastRun', twf.reports.lastRun);
137 }
138
139 twf.reports.updateReportStats(); // configure-o para "ConcluÃdo."
140 },
141 onFrameLoaded: function() {
142 try {
143 twf.helper.spinner.fadeOut();
144 twf.helper.checkBotProtection();
145 twf.reports.updateReportStats(false, false);
146
147 if (twf.reports.currentlyReading) {
148 if (twf.reports.reportFrame[0].contentWindow.location.search.indexOf('view') == -1) {
149 // estamos na lista
150 let maxPage = twf.reports.getMaxPage();
151 if (twf.reports.currentPage <= maxPage) {
152 // páginas restantes, atualizar estatÃsticas e interpretar
153 twf.reports.updateReportStats(true, maxPage);
154 twf.reports.handleList();
155 } else if (twf.reports.reportsToRead.length > 0) {
156 // Estamos na última página e temos relatórios para ler!
157 twf.helper.spinner.show();
158 twf.helper.log("Relatórios legÃveis de carregamento concluÃdo...", twf.helper.MESSAGE_SUCCES);
159 console.debug("Última página da lista de relatórios -> indo para ver um relatório!");
160 twf.reports.reportFrame.attr('src', twf.reports.reportParserUrl + twf.reports.reportsToRead.shift());
161 } else {
162 // última página e nenhum relatório encontrado! (também pode não haver nenhum relatório)
163 twf.helper.log("Nenhum relatório útil encontrado. Suspeito...", twf.helper.MESSAGE_WARNING);
164 twf.reports.firstRunFinished = true;
165 twf.reports.stopReading();
166 }
167
168 } else {
169 // estamos em um relatório
170 twf.reports.handleReport();
171 }
172 }
173 } catch (error) {
174 twf.helper.stopEverything();
175 console.error(error);
176 alert("BOT PROTECTION? " + error);
177 }
178
179 },
180 parseAndStore: function() {
181 // TODO CHECK SE ESTÃ EM ARMAZENAMENTO
182 let coords = twf.reports.reportFrame.contents().find('span.quickedit-label').text().trim().match(/\d{1,3}\|\d{1,3}/g);
183 coords = coords[coords.length - 1];
184
185 // Tempo de chegada
186
187 let arrival = twf.reports.reportFrame.contents().find('.small.grey').parent().text().trim().split(" ");
188 arrival[0] = arrival[0].split(".").reverse().join("-"); //corrige a data
189 arrival[1] = arrival[1].replace(/:([^:]*)$/, "." + '$1'); //corrige o tempo (substitua o último: com .)
190 arrival = new Date("20" + arrival[0] + "T" + arrival[1] + "Z"); // define a data
191
192 if (twf.reports.data[coords] && twf.reports.data[coords].scoutBuilding && arrival <= twf.reports.data[coords].scoutBuilding) {
193 // Se já examinamos os edifÃcios aqui e esse relatório é mais antigo do que os nossos relatórios de construção mais recentes
194 // não temos nada para ganhar com isso, para que possamos ignorá-lo
195 console.debug("Ignorar" + coords + " porque temos dados de construção mais frescos. Atual:" + arrival.toUTCString() + ", em db:" + twf.reports.data[coords].scoutBuilding.toUTCString());
196 return;
197 }
198
199 // sorte
200
201 let luck = parseFloat(twf.reports.reportFrame.contents().find('.nobg b').text().trim().replace("%", ""));
202
203 // enviar de
204 let sentFromId = parseInt(twf.reports.reportFrame.contents().find('[data-id].village_anchor').eq(0).attr('data-id'));
205 let sentFromCoords = twf.reports.reportFrame.contents().find('[data-id].village_anchor').eq(0).text().trim().match(/\d{1,3}\|\d{1,3}/g);
206 sentFromCoords = sentFromCoords[sentFromCoords.length - 1];
207
208 // unidades enviadas e perdidas
209 let unitsSent = {};
210 let unitsDied = {};
211 for (let i in twf.data.unitTypes) { // eu sou uma string, wtf
212 unitsSent[twf.data.unitTypes[i]] = parseInt(twf.reports.reportFrame.contents().find('#attack_info_att_units .unit-item').eq(i).text().trim());
213 unitsDied[twf.data.unitTypes[i]] = parseInt(twf.reports.reportFrame.contents().find('#attack_info_att_units .unit-item').eq(twf.data.unitTypes.length + parseInt(i)).text().trim());
214 }
215
216 // é jogador
217 let isPlayer = twf.reports.reportFrame.contents().find('#attack_info_def th').eq(1).text().trim() != "---";
218
219 let enemyHome = null;
220 let enemyDied = null;
221
222 // se pelo menos na tropa sobreviveram, temos dados sobre suas tropas
223 for (let i in unitsSent) {
224 if (unitsSent[i] != unitsDied[i]) {
225 // temos pelo menos uma tropa sobrevivente
226 // instancia objects
227 enemyHome = {};
228 enemyDied = {};
229
230 // analisa suas unidades
231 // isso ignora a milÃcia
232 for (let i in twf.data.unitTypes) { // eu sou uma string, wtf
233 enemyHome[twf.data.unitTypes[i]] = parseInt(twf.reports.reportFrame.contents().find('#attack_info_def_units .unit-item').eq(i).text().trim());
234 // note o comprimento + 1 (porque ignoramos a multidão)
235 enemyDied[twf.data.unitTypes[i]] = parseInt(twf.reports.reportFrame.contents().find('#attack_info_def_units .unit-item').eq(twf.data.unitTypes.length + 1 + parseInt(i)).text().trim());
236 }
237 break;
238 }
239 }
240
241 // resultados
242
243 let resTaken = twf.reports.reportFrame.contents().find('#attack_results tr td').eq(1).text().trim().split("/");
244 let maxResTaken = parseInt(resTaken[1]);
245 resTaken = parseInt(resTaken[0]);
246 // acompanhe o nÃvel que exploramos
247 let scoutLevel = 0;
248 // res left (if no data == null)
249 let resLeft = null;
250 // edifÃcios scouted (no data -> null)
251 let buildingData = null;
252 // tropas fora da aldeia
253 let enemyAway = null;
254 // informação do spy
255 if (twf.reports.reportFrame.contents().find("#attack_spy_resources").length > 0) {
256 // nós exploramos recursos
257 scoutLevel = 1;
258 let resArray = twf.reports.reportFrame.contents().find('#attack_spy_resources .nowrap').text().trim().split(" ");
259 for (let i in resArray) {
260 let p = parseInt(resArray[i]);
261 if (!isNaN(p)) {
262 resLeft += p;
263 }
264 }
265 } else if (resTaken < maxResTaken) {
266 // se não tivéssemos transporte completo, podemos assumir que não resta mais restos
267 resLeft = 0;
268 } else {
269 // se tivéssemos um curso completo, não temos informações, então, signifique com -1
270 resLeft = -1;
271 }
272 if (twf.reports.reportFrame.contents().find('#attack_spy_building_data').length > 0) {
273 // nós exploramos edifÃcios
274 scoutLevel = 2;
275 // Init BuildingData
276 buildingData = {};
277 // analise-os para mostrar
278 let parsedData = JSON.parse(twf.reports.reportFrame.contents().find('#attack_spy_building_data').val());
279 // analise-os em uma nova matriz
280 for (let i in parsedData) {
281 // para todos os dados que temos, guarde-os em buildingdata como edifÃcio: nÃvel
282 buildingData[parsedData[i].id] = parseInt(parsedData[i].level);
283 }
284 // então corre sobre todos os edifÃcios e ajuste-os para 0 se eles ainda não existem
285 for (let i in twf.data.buildingTypes) {
286 if (buildingData[twf.data.buildingTypes[i]] == undefined) {
287 buildingData[twf.data.buildingTypes[i]] = 0;
288 }
289 }
290 }
291 if (twf.reports.reportFrame.contents().find('#attack_spy_away').length > 0) {
292 // temos dados sobre tropas fora
293 scoutLevel = 3;
294 // Init inimigo
295 enemyAway = {};
296 for (let i in twf.data.unitTypes) {// eu sou uma string, wtf
297 enemyAway[twf.data.unitTypes[i]] = parseInt(twf.reports.reportFrame.contents().find('#attack_spy_away .unit-item').eq(i).text().trim());
298 }
299 }
300 // interpretamos tudo o que existe,
301 // agora armazene-o
302
303 // primeiro recupere o relatório antigo (para substituÃ-lo)
304 let report = {};
305 if (twf.reports.data[coords]) {
306 report = twf.reports.data[coords];
307 }
308 report['lastAttack'] = arrival;
309 report['luck'] = luck;
310 report['sentFromId'] = sentFromId;
311 report['sentFromCoords'] = sentFromCoords;
312 report['isPlayer'] = isPlayer;
313 report['unitsSent'] = unitsSent;
314 report['unitsDied'] = unitsDied;
315 if (enemyHome) {
316 report['enemyHome'] = enemyHome;
317 report['enemyDied'] = enemyDied;
318 }
319 // não economize resTaken e maxResTaken porque podemos inferir tudo o que precisamos saber de resLeft
320 // relatório ['resTaken'] = resTaken;
321 // relatório ['maxResTaken'] = maxResTaken;
322 report['resLeft'] = resLeft; //-1 não significa dados
323 if (buildingData) {
324 report['buildingData'] = buildingData;
325 }
326 if (enemyAway) {
327 report['enemyAway'] = enemyAway;
328 }
329
330 // armazenar quando o último batedor foi
331 if (scoutLevel) {
332 if (scoutLevel >= 1) {
333 report['scoutRes'] = arrival;
334 }
335 if (scoutLevel >= 2) {
336 report['scoutBuilding'] = arrival;
337 }
338 if (scoutLevel >= 3) {
339 report['scoutAway'] = arrival;
340 }
341 }
342
343 // armazenar no objeto de dados
344 twf.reports.data[coords] = report;
345
346 // store in localstorage
347 twf.data.storeWorldLevel('reports', twf.reports.data);
348 console.debug(report);
349
350 // done
351 },
352 handleReport: function() {
353 twf.helper.spinner.fadeOut();
354
355 if (twf.reports.reportsToRead.length > 0) {
356 twf.reports.parseAndStore();
357 twf.helper.spinner.show();
358 twf.reports.reportFrame.attr('src', twf.reports.reportParserUrl + twf.reports.reportsToRead.shift());
359 } else {
360 twf.helper.log("Feito relatórios de leitura!", twf.helper.MESSAGE_SUCCES);
361 twf.reports.firstRunFinished = true;
362 twf.reports.stopReading()
363 }
364
365 },
366 getMaxPage: function() {
367 let t = twf.reports.reportFrame.contents().find('.paged-nav-item:last');
368 if (t) {
369 // found atleast one page
370 t = t.text().trim(); //yields "[xx]"
371 t = parseInt(t.substring(1, t.length - 1));
372 } else {
373 // no extra pages found
374 t = 1;
375 }
376 return t;
377 },
378 // primeiro argumento -> verdadeiro se ainda estiver na lista, o maxPage é a página máxima
379 updateReportStats: function(pages, maxPage) {
380 if (!twf.reports.currentlyReading && twf.reports.firstRunFinished) {
381 //não lendo e terminou
382 $("#reports_left").html("<b>Feito!</b>");
383 } else if (!twf.reports.currentlyReading) {
384 $("#reports_left").text("Waiting...");
385 } else if (pages) {
386 // carregando relatórios
387 $("#reports_left").text("page " + twf.reports.currentPage + "/" + maxPage);
388 } else {
389 // lendo relatórios
390 $("#reports_left").text(twf.reports.reportsToRead.length + " reports left");
391 }
392 },
393 handleList: function() {
394 let finished = false;
395 twf.reports.reportFrame.contents().find('#report_list tr td:nth-of-type(3)').each(function(i, e) {
396 // recuperar alguma informação
397 let coords = $(e).siblings().find('.quickedit-label').text().match(/\d{1,3}\|\d{1,3}/g);
398 coords = coords[coords.length - 1];
399
400 let id = $(e).siblings().find("[data-id]").attr('data-id');
401
402 let receivedAt = twf.reports.getReportTimeInList($(e).text());
403 let now = twf.helper.getServerTime(twf.reports.reportFrame);
404 // agora> recebido, porque o recebimento foi reduzido a minutos completos (e.g. 18:26:12.123 -> 18:26)
405
406 // Lemos um relatório se tiver menos de 24 horas E não temos um escoteiro mais recente
407 // porque se tivermos um scout mais recente não há nada a saber
408 // no entanto, se tivermos um ataque mais recente, mas não scout, ainda podemos receber informações de construção
409 if (now - receivedAt >= 1000 * 60 * 60 * twf.data.settings.reportMaxReadAge) {
410 // mais de 24 -> podemos parar imediatamente
411 console.debug("Ignorar " + coords + " recebido em " + receivedAt.toUTCString() + " Porque > " + twf.data.settings.reportMaxReadAge + " hours.");
412 finished = true;
413 return false; //break out of each-loop
414 } else if (twf.reports.lastRun && twf.reports.lastRun - receivedAt >= 1000 * 60) {
415 // the report was already caught in the last run
416 console.debug("ignorar " + coords + " recebido em " + receivedAt.toUTCString() + " porque a capturamos pela última vez. (InÃcio da última execução:" + twf.reports.lastRun.toUTCString() + ")");
417 finished = true;
418 return false;
419 } else if (twf.reports.data[coords] && twf.reports.data[coords].scoutBuilding && receivedAt /*+ 60 * 1000*/ <= twf.reports.data[coords].scoutBuilding) {
420 // do jeito que é agora: ignoramos se dois ataques chegaram no mesmo minuto, o que nunca deve acontecer.
421 // o caminho abaixo adiciona muitos relatórios que são inúteis
422
423 // o relatório foi recebido em algum lugar no minuto em que o relatório de escuta anterior foi recebido
424 // tornamos um minuto mais fresco, porque o recebimento é arredondado para os minutos completos, enquanto o outro é exato
425 // não que isso importe tanto
426 console.debug("ignorar " + coords + " recebido em " + receivedAt.toUTCString() + " O último explorador de construção é mais recente (" + twf.reports.data[coords].scoutBuilding.toUTCString() + ")");
427 } else {
428 console.debug("Última_corrida: " + (twf.reports.lastRun ? twf.reports.lastRun.toUTCString() : "--nenhuma corrida--") + ". Recebido: " + receivedAt.toUTCString());
429 //console.debug("twf.reports.lastRun ("+ twf.reports.lastRun +") && twf.reports.lastRun> = receivedAt + 1000 * 5 == "+ (twf.reports.lastRun> = receivedAt + 1000 * 5) + "(receivedAt + 1000 * 5:" + (receivedAt.getTime () + 1000 * 5));
430 //console.debug("Adding "+ coords +" recebido em "+ receivedAt.toUTCString () +". Diferença de tempo (h): "+ ((now-receivedAt) / 1000/60/60));
431 // temos um relatório válido, então adicione-o
432 twf.reports.reportsToRead.push(id);
433 }
434
435 });
436 // adicionamos todos os relatórios e talvez tenhamos terminado porque não restavam relatórios recentes
437 // se acabamos, começamos a interpretá-los
438 if (finished) {
439 twf.helper.spinner.show();
440 twf.helper.log("Relatórios legÃveis de carregamento concluÃdo...", twf.helper.MESSAGE_SUCCES);
441 twf.reports.reportFrame.attr('src', twf.reports.reportParserUrl + twf.reports.reportsToRead.shift());
442 } else { // Relatórios legÃveis de carregamento concluÃdo
443 twf.helper.spinner.show();
444 twf.reports.currentPage = twf.reports.currentPage + 1;
445 console.debug("Passando para a próxima página: " + twf.reports.currentPage);
446 twf.reports.reportFrame.attr('src', twf.reports.reportListUrl + (twf.reports.currentPage - 1) * 12); //12 relatórios por página 1-indexed
447 }
448 },
449 getReportTimeInList(text) {
450 let dateTime = text.split(" ");
451 let dateString = "20" + dateTime[0].split(".").reverse().join("-"); //Deve render algo como 2018-05-06
452 return new Date(dateString + "T" + dateTime[1] + "Z");
453 },
454 loadOldReportData: function() {
455 let tempReports = twf.data.loadWorldLevel('reports');
456 if (tempReports) {
457 // analisar datas como objects
458 for (let i in tempReports) {
459 if (tempReports[i].scoutRes) {
460 tempReports[i].scoutRes = new Date(tempReports[i].scoutRes);
461 }
462 if (tempReports[i].scoutBuilding) {
463 tempReports[i].scoutBuilding = new Date(tempReports[i].scoutBuilding);
464 }
465 if (tempReports[i].scoutAway) {
466 tempReports[i].scoutAway = new Date(tempReports[i].scoutAway);
467 }
468 if (tempReports[i].lastAttack) {
469 tempReports[i].lastAttack = new Date(tempReports[i].lastAttack);
470 }
471
472 }
473 twf.helper.log("Relatórios carregados com sucesso.", twf.helper.MESSAGE_SUCCES);
474 twf.reports.data = tempReports;
475 } else {
476 twf.helper.log("Falha ao carregar relatórios. Talvez ainda não?", twf.helper.MESSAGE_WARNING);
477 }
478 },
479 // assumeRes = valor de res esperamos que haja. Isso sobrescreve o que está no relatório
480 // assumeBuildings = nÃvel de armazenamento, esconderijo, madeira, argila e ferro que esperamos. Isso substitui o relatório se ele existir
481 // se você não quiser assumir nada, verifique se está no relatório e passa falso (NÃO falso-y, falso real)
482 expectedRes: function(coords, unit, serverTime, assumeRes, assumeBuildings) {
483 let lastReport = twf.reports.data[coords];
484
485 let distance = twf.helper.getDistance(game_data.village.coord, coords);
486 let travelTimeInHours = twf.helper.getTravelTimeInMinutes(distance, unit) / 60;
487 let timePassedInHours = (serverTime.getTime() - lastReport.lastAttack.getTime()) / (1000 * 60 * 60);
488
489 //console.debug(coords + ". Distância:" + distância + ". Tempo de viagem:" + travelTimeInHours + ". Tempo desde ataque:" + timePassedInHours ");
490 let maxStorage = null;
491 let maxHide = null
492 let resOver = null;
493 let resPerHour = null;
494
495 if (assumeBuildings !== false) {
496 console.debug("Assumindo edifÃcios!");
497 maxStorage = twf.helper.getMaxStorage(assumeBuildings.storage);
498 maxHide = twf.helper.getMaxHiding(assumeBuildings.hide);
499 resPerHour = twf.helper.getResPerHour(assumeBuildings.wood) + twf.helper.getResPerHour(assumeBuildings.clay) + twf.helper.getResPerHour(assumeBuildings.iron);
500 } else {
501 maxStorage = twf.helper.getMaxStorage(lastReport.buildingData.storage);
502 maxHide = twf.helper.getMaxHiding(lastReport.buildingData.hide);
503 resPerHour = twf.helper.getResPerHour(lastReport.buildingData.wood) + twf.helper.getResPerHour(lastReport.buildingData.clay) + twf.helper.getResPerHour(lastReport.buildingData.iron);
504 }
505
506 //console.debug("maxStorage: " + maxStorage + ". maxHide: " + maxHide + ". resPerHour: " + resPerHour);
507 if (assumeRes !== false) {
508 console.debug("Supondo que res left")
509 resOver = assumeRes;
510 } else {
511 resOver = lastReport.resLeft;
512 }
513
514
515 let maxLoot = maxStorage - maxHide;
516 let tempRes = Math.min(resOver + (timePassedInHours + travelTimeInHours) * resPerHour, twf.data.settings.reportMaxUseAge * resPerHour);
517
518 let expectedRes = Math.min(maxLoot, tempRes)
519
520 //console.debug("Expected res for " + coords + "@" + unit + ": " + expectedRes);
521
522 if (twf.data.settings.discountTime && twf.data.settings.discountFactor != 1) {
523 expectedRes = expectedRes / (Math.pow(twf.data.settings.discountFactor, timePassedInHours + travelTimeInHours));
524 }
525
526 //console.debug("Expected discounted res for " + coords + "@" + unit + ": " + expectedRes);
527
528 return expectedRes;
529
530 },
531 lostAllTroops: function(coords) {
532 // note that spies do not die (usually)
533 // if they have not died, we have all information
534 // if they have, we know to send just one
535 if (twf.reports.data[coords]) {
536 for (let troop in twf.reports.data[coords].unitsSent) {
537 if (twf.reports.data[coords].unitsSent[troop] != twf.reports.data[coords].unitsDied[troop]) {
538 return false;
539 }
540 }
541 return true;
542 } else {
543 return null;
544 }
545 }
546 },
547 data: {
548 settings: {
549 minPollAttack: 0,
550 minPollReport: 0,
551 //minPoll: 300, // minimum polling time -> prevent detecetion
552 //temp
553 reportMaxUseAge: 6,
554 reportMaxReadAge: 1,
555 // end of temp
556 resetToFirst: true,
557 waitForTroops: true,
558 attackPlayers: false,
559 pollLate: true,
560 pollLateSeconds: 60,
561 autoStop: true,
562 autoStopMinutes: 120,
563 autoStopTimer: 0,
564 discountTime: false,
565 discountFactor: 1.07, //per hour
566 reportFarmer: false
567 },
568 timers: {
569 autoStopTimer: null,
570 attackPollTimer: null,
571 reportPollTimer: null,
572 },
573 worldSettings: {
574 speed: 1,
575 unitSpeed: 1,
576 knight: true,
577 archer: true,
578 },
579 travelTime: {
580 spear: 18,
581 sword: 22,
582 axe: 18,
583 archer: 18,
584 spy: 9,
585 light: 10,
586 marcher: 10,
587 heavy: 11,
588 ram: 30,
589 catapult: 30,
590 snob: 35,
591 knight: 10
592 },
593 carryCapacity: {
594 spear: 25,
595 sword: 15,
596 axe: 10,
597 archer: 10,
598 spy: 0,
599 light: 80,
600 marcher: 50,
601 heavy: 50,
602 ram: 0,
603 catapult: 0,
604 snob: 0,
605 knight: 100
606 },
607 unitTypes: ["spear", "sword", "axe", "archer", "spy", "light", "marcher", "heavy", "ram", "catapult", "snob", "knight", ],
608 buildingTypes: ["main", "hide", "market", "storage", "stable", "smith", "barracks", "place", "wall", "iron", "clay", "wood", "farm", "church", "watchtower", "statue", "garage", "snob"],
609 minLightPerWall: [1, 4, 32, 87, 170, 281],
610 init: function() {
611 this.getAndSaveWorldSettings();
612 this.loadSettings();
613
614 if (this.settings.autoStop) {
615 twf.data.timers.autoStopTimer = window.setTimeout(twf.attacks.stopAttack, twf.data.settings.autoStopMinutes * 60 * 1000);
616 twf.helper.log("Autostop comprometido. Parando em " + twf.data.settings.autoStopMinutes + " minutos!", twf.helper.MESSAGE_DEFAULT);
617 }
618
619 if (!this.settings.reportFarmer) {
620 $('td.reports_left').hide();
621 }
622
623 twf.helper.log("Módulo de dados pronto.", twf.helper.MESSAGE_SUCCES);
624 },
625 saveSettings: function() {
626 //também valida
627 twf.data.settings.resetToFirst = $('#reset_to_first').is(':verificado');
628 twf.data.settings.waitForTroops = $('#wait_for_troops').is(':verificado');
629 twf.data.settings.attackPlayers = $('#attack_players').is(':verificado');
630 twf.data.settings.autoStop = $('#autostop').is(':verificado');
631 twf.data.settings.pollLate = $('#poll_late').is(':verificado');
632 twf.data.settings.discountTime = $('#discount_time').is(':verificado');
633 twf.data.settings.reportFarmer = $('#report_farmer').is(':verificado');
634
635
636 //values (take first number as value)
637 twf.data.settings.autoStopMinutes = parseInt($('#autostop_minutes').val().match(/\d+/)[0]);
638 twf.data.settings.pollLateSeconds = parseInt($('#poll_late_seconds').val().match(/\d+/)[0]);
639 twf.data.settings.discountFactor = parseFloat($('#discount_factor').val().match(/^[12](\.\d{1,2})*/)[0]); // matches 1, 1.1,1.2, etc
640 twf.data.settings.reportMaxReadAge = parseInt($('#report_max_read_age').val().match(/\d+/)[0]);
641 twf.data.settings.reportMaxUseAge = parseInt($('#report_max_use_age').val().match(/\d+/)[0]);
642
643 // TEMP -> TODO ADD TO CONFIG
644 twf.data.settings.minPollAttack = twf.data.settings.minPollAttack;
645 twf.data.settings.minPollReport = twf.data.settings.minPollReport;
646 // END TEMP
647
648 twf.data.storeGlobal('settings', twf.data.settings);
649 twf.helper.log("Configurações salvas!", twf.helper.MESSAGE_SUCCES);
650
651 // rerun autoStop
652 if (twf.data.settings.autoStop) {
653 window.clearTimeout(twf.data.timers.autoStopTimer);
654 twf.data.timers.autoStopTimer = window.setTimeout(twf.attacks.stopAttack, twf.data.settings.autoStopMinutes * 60 * 1000);
655 twf.helper.log("Autostop comprometido. Parando em " + twf.data.settings.autoStopMinutes + " minutos!", twf.helper.MESSAGE_DEFAULT);
656 }
657
658 // show reports progress
659 if (twf.data.settings.reportFarmer) {
660 twf.reports.loadOldReportData();
661 $('td.reports_left').show();
662 if (twf.reports.firstRunFinished) {
663 twf.attacks.attackButton.removeClass('btn-disabled').prop('disabled', false);
664 } else {
665 twf.attacks.attackButton.addClass('btn-disabled').prop('disabled', true);
666 twf.helper.log("Você habilitou o Report Farmer. Pressione o botão \"Ler relatórios\" - para continuar ou desative o Report Farmer. ", twf.helper.MESSAGE_WARNING);
667 }
668 } else {
669 twf.attacks.attackButton.removeClass('btn-disabled').prop('disabled', false);
670 $('td.reports_left').hide();
671 }
672
673
674 $('#settings_popup').hide();
675 },
676 loadSettings: function() {
677 let tempSettings = twf.data.loadGlobal('settings');
678 // only load if global settings are saved
679 if (tempSettings) {
680 twf.data.settings = tempSettings;
681 twf.helper.log("Configurações de bot obtidas com sucesso.", twf.helper.MESSAGE_SUCCES);
682 } else {
683 twf.helper.log("Falha ao recuperar as configurações do bot. Usando padrões.", twf.helper.MESSAGE_ERROR);
684 }
685
686 },
687 getAndSaveWorldSettings: function() {
688 let tempWorldSettings = this.loadWorldLevel("worldSettings");
689 if (!tempWorldSettings) {
690 let configUrl = '/interface.php?func=get_config';
691 //no world settings -> load and save them
692 $.ajax({
693 url: configUrl,
694 }).fail(function() {
695 twf.helper.log("Falha ao recuperar configurações mundiais! Usando padrões.", twf.helper.MESSAGE_ERROR);
696 }).done(function(result) {
697 twf.data.worldSettings.speed = parseFloat($(result).find('speed').text());
698 twf.data.worldSettings.unitSpeed = parseFloat($(result).find('unit_speed').text());
699 twf.data.worldSettings.archer = $(result).find('archer').text() ? true : false;
700 twf.data.worldSettings.knight = $(result).find('knight').text() ? true : false;
701 twf.data.storeWorldLevel("worldSettings", twf.data.worldSettings);
702 twf.helper.log("Configurações do mundo obtidas com sucesso remotamente.", twf.helper.MESSAGE_SUCCES);
703 })
704 } else {
705 twf.helper.log("Configurações mundiais bem sucedidas.", twf.helper.MESSAGE_SUCCES);
706 twf.data.worldSettings = tempWorldSettings;
707 }
708
709 },
710 // store and load localstorage data at various levels
711 storeTownLevel: function(key, value) {
712 localStorage.setItem("twf_" + game_data.world + "_" + game_data.village.id + "_" + key, JSON.stringify(value));
713 },
714 loadTownLevel: function(key) {
715 return JSON.parse(localStorage.getItem("twf_" + game_data.world + "_" + game_data.village.id + "_" + key));
716 },
717 storeWorldLevel: function(key, value) {
718 localStorage.setItem("twf_" + game_data.world + "_" + key, JSON.stringify(value));
719 },
720 loadWorldLevel: function(key) {
721 return JSON.parse(localStorage.getItem("twf_" + game_data.world + "_" + key));
722 },
723 storeGlobal: function(key, value) {
724 localStorage.setItem("twf_" + key, JSON.stringify(value));
725 },
726 loadGlobal: function(key) {
727 return JSON.parse(localStorage.getItem("twf_" + key));
728 }
729 },
730 attacks: {
731 attacking: false,
732 continueAttack: true,
733 attackTemplates: {},
734 currentAttackTemplateTimestamp: null,
735 unitsPerAttack: {},
736 villageString: "",
737 villageArray: [],
738 currentVillage: null,
739 hiddenFrame: null, //todo init
740 hiddenFrameUrl: null,
741 init: function() {
742 this.hiddenFrameUrl = '/game.php?village=' + game_data.village.id + '&screen=place';
743 this.hiddenFrame = twf.helper.createHiddenFrame(this.hiddenFrameUrl, this.onFrameLoaded, "attack_hidden_frame");
744
745 this.attackButton = $('#attackButton').click(this.attack); // this one is disabled in general init if reportFarmer is true
746 this.sAttackButton = $('#sAttackButton').click(this.stopAttack).hide();
747 this.rAttackButton = $('#resetAttack').click(this.resetAttack);
748 this.cAttackButton = $('#cAttackButton').click(function() {
749 twf.helper.showAttackTemplate();
750 });
751
752 this.loadAttackTemplates();
753 this.loadAttack();
754
755 twf.helper.log("Módulo de ataque pronto.", twf.helper.MESSAGE_SUCCES);
756 },
757 reportSendUnits: function(coords) {
758 console.debug("Relatório de relatórioSendUnits for " + coords);
759 console.debug("Actual coords: " + twf.attacks.villageArray[twf.attacks.attackTemplates[twf.attacks.currentAttackTemplateTimestamp].position]);
760 let frame = twf.attacks.hiddenFrame;
761 // reportfarming
762 let serverTime = twf.helper.getServerTime(frame);
763
764 // we have data and it isn't too old
765 // console.log("servertime:" + serverTime.toUTCString() + " --- lastAttack: " + twf.reports.data[coords].lastAttack.toUTCString() );
766 // rewrite this because it prevents using available wall levels
767 // basically only check use this for expectedResources. We can fake it by setting that value to the exact amount that we to farm (e.g. maxUnits = minUnits)
768 if (twf.reports.data[coords]) {
769 if (twf.reports.lostAllTroops(coords)) {
770 //todo handle this
771 // e.g. some algorithm to guess the wall level?
772 }
773 // we have report
774 let slowestUnit = null;
775 // find slowest unit
776 for (let unit in twf.attacks.unitsPerAttack) {
777 if (twf.attacks.unitsPerAttack[unit] > 0 && (twf.data.travelTime[unit] > twf.data.travelTime[slowestUnit] || slowestUnit == null)) {
778 slowestUnit = unit;
779 }
780 }
781
782 let expectedRes = null;
783 let minLight = null;
784 if (twf.reports.data[coords].scoutBuilding) {
785 expectedRes = twf.reports.expectedRes(coords, slowestUnit, serverTime, false, false);
786 minLight = twf.data.minLightPerWall[twf.reports.data[coords].buildingData.wall];
787
788 } else if (twf.reports.data[coords].resLeft != -1) {
789 // we have resources scouted
790 // just a guess
791 let buildings = {
792 wall: 1,
793 storage: 3,
794 hide: 3,
795 iron: 3,
796 clay: 3,
797 wood: 3
798 }
799 // if we assumed level 1 or 0 and we lost all troops, our guess was incorrect (or it was spiked or something)
800 // so adjust the wall level
801 if (twf.reports.lostAllTroops(coords)) {
802 buildings.wall = buildings.wall + 1;
803 }
804 expectedRes = twf.reports.expectedRes(coords, slowestUnit, serverTime, false, buildings);
805 minLight = twf.data.minLightPerWall[buildings.wall];
806 } else if (twf.reports.data[coords].resLeft == -1) {
807 // we had a full haul
808 let buildings = {
809 wall: 1,
810 storage: 3,
811 hide: 3,
812 iron: 3,
813 clay: 3,
814 wood: 3
815 }
816 // if we assumed level 1 or 0 and we lost all troops, our guess was incorrect (or it was spiked or something)
817 if (twf.reports.lostAllTroops(coords)) {
818 buildings.wall = buildings.wall + 1;
819 }
820 let assumedRes = 50;
821 expectedRes = twf.reports.expectedRes(coords, slowestUnit, serverTime, assumedRes, buildings);
822 minLight = twf.data.minLightPerWall[buildings.wall];
823 }
824 // if we cannot base expected res on how long ago the attack was sent (because it is longer than x ago)
825 // just set expectesRes = 0, so we send the standard amount but based on the wall as well
826 // we do not have to do this for wall, since that is already used if it is available and otherwise it's assumed
827 if (serverTime - twf.reports.data[coords].lastAttack > 1000 * 60 * 60 * twf.data.settings.reportMaxUseAge) {
828 expectedRes = 0;
829 console.debug("Relatorio mais antigo do que " + twf.data.settings.reportMaxUseAge + "h, então esperado = 0.");
830 }
831 for (let unitType in twf.attacks.unitsPerAttack) {
832 if (twf.attacks.continueAttack) {
833 // skip if not in list to send
834 if (twf.attacks.unitsPerAttack[unitType] == 0) {
835 continue;
836 }
837 let unitsLeft = frame.contents().find('#units_entry_all_' + unitType).html();
838 unitsLeft = parseInt(unitsLeft.substring(1, unitsLeft.length - 1));
839
840 let fullHaulUnits = Math.ceil(expectedRes / twf.data.carryCapacity[unitType]);
841 //console.debug("Expected: " + expectedRes + ". Carry (" + unitType + "): " + twf.data.carryCapacity[unitType] + ". fullHaul: " + fullHaulUnits);
842 let minUnits = twf.attacks.unitsPerAttack[unitType];
843
844 if (unitType == "light") {
845 // if light, adjust for wall
846 minUnits = Math.max(minUnits, minLight);
847 }
848 if (unitType == "spy") {
849 // if spy, just send the minimum (i.e. overwrite whatever we calculated)
850 fullHaulUnits = twf.attacks.unitsPerAttack[unitType];
851 }
852
853
854 // dont waste an extra attack on an attack
855 if (minUnits > fullHaulUnits + 5 && minUnits > 10) {
856 twf.helper.log("Não enviando ataque para " + coords + ". Min " + unitType + " = " + minUnits + " enquanto nós precisamos apenas " + fullHaulUnits + " para um transporte completo.", twf.helper.MESSAGE_WARNING);
857 //twf.attacks.continueAttack = false; // to prevent trying to send
858 // does not work
859 twf.attacks.ignoreVillage();
860 console.debug("Iniciou ignoreVillage de dentro do reportSendUnits");
861 return false;
862 }
863 // not enough units
864 else if (minUnits > unitsLeft) {
865 if (unitType == "spy" && twf.attacks.attackTemplates[twf.attacks.currentAttackTemplateTimestamp].ignoreScouts) {
866 //console.debug("No spies. Trying to send...");
867 twf.attacks.continueAttack = true;
868 continue;
869 } else if (twf.data.settings.waitForTroops) {
870 twf.helper.log("Não é suficiente" + unitType + ". Esperando tropas", twf.helper.MESSAGE_DEFAULT);
871 } else {
872 twf.helper.log('Não há unidades suficientes de tipo: ' + unitType, twf.helper.MESSAGE_ERROR);
873 twf.helper.stopEverything();
874 }
875 twf.attacks.continueAttack = false;
876 return true; // we did not skip a village
877 }
878 // we have as many as we want,
879 else if (fullHaulUnits <= unitsLeft) {
880 console.debug("Set " + unitType + " para " + Math.max(minUnits, fullHaulUnits) + " (disponÃvel " + unitsLeft + ", minimum: " + minUnits + ")");
881 frame.contents().find('#unit_input_' + unitType).val(Math.max(minUnits, fullHaulUnits));
882 twf.attacks.continueAttack = true;
883 } else {
884 console.debug("Configuração " + unitType + " to " + unitsLeft + " (Preferido mas incapaz: " + fullHaulUnits + ")");
885 frame.contents().find('#unit_input_' + unitType).val(unitsLeft);
886 twf.attacks.continueAttack = true;
887 }
888 }
889 }
890 console.debug("Unidades de entrada feitas. twf.attacks.continueAttack = " + twf.attacks.continueAttack);
891 return true; // we did not skip a village
892 } else {
893 // no report
894 // todo check wall level and all units lost, else send just a scout?
895
896 // we have no report data or it is too old -> normal attacks
897 // maybe change to scout and settings and stuff
898 console.debug("Sem dados, mudando para a Farm manual.");
899 twf.attacks.normalSendUnits(coords);
900 return true;
901 // no report, force scout? skip?
902 // degrade to standard farming
903 }
904
905 },
906 // handles inputting and checking of available units
907 normalSendUnits: function(coord) {
908 for (let unitType in twf.attacks.unitsPerAttack) {
909 if (twf.attacks.continueAttack) {
910 twf.attacks.continueAttack = twf.attacks.sendUnit(unitType, coord);
911 }
912 }
913 },
914 sendUnit: function(unitType, coords) {
915 let unitsToSend = this.unitsPerAttack;;
916 let frame = this.hiddenFrame;
917 if (unitsToSend[unitType] == 0) {
918 return true;
919 }
920
921 let unitsLeft = frame.contents().find('#units_entry_all_' + unitType).html();
922 unitsLeft = parseInt(unitsLeft.substring(1, unitsLeft.length - 1));
923 // can also use [data-all-count]
924
925 // normal farming
926 if (unitsLeft >= unitsToSend[unitType]) {
927 frame.contents().find('#unit_input_' + unitType).val(unitsToSend[unitType]);
928 return true
929 // if we are allowed to skip spies, skip them!
930 } else if (unitType == "spy" && twf.attacks.attackTemplates[twf.attacks.currentAttackTemplateTimestamp].ignoreScouts) {
931 twf.helper.log("Não há espiões suficientes. Tentando enviar sem espiões.", twf.helper.MESSAGE_DEFAULT);
932 return true;
933 } else {
934 if (twf.data.settings.waitForTroops) {
935 twf.helper.log('Não há unidades suficientes de tipo: ' + unitType + ', esperando que alguns voltem!', twf.helper.MESSAGE_DEFAULT);
936 } else {
937 twf.helper.log('Não há unidades suficientes de tipo: ' + unitType, twf.helper.MESSAGE_ERROR);
938 this.stopAttack();
939 }
940 return false
941 }
942 },
943 attack: function() {
944 console.debug("Ataques iniciados. Ataque");
945 twf.attacks.attackButton.hide();
946 twf.attacks.sAttackButton.show();
947
948 let coord = twf.attacks.villageArray[twf.attacks.getPosition()];
949 twf.attacks.continueAttack = true;
950 let noSkippedVil = true;
951 // fill in units if available
952 if (!twf.data.settings.reportFarmer) {
953 twf.attacks.normalSendUnits(coord);
954 } else {
955 // this needs all checks
956 // e.g. wall level, did all troops die, etc, etc
957 if (twf.reports.data[coord] && twf.reports.data[coord].buildingData && twf.reports.data[coord].buildingData.wall >= twf.data.minLightPerWall.length) {
958 console.debug("Ignore " + coords + " because wall too high.");
959 return twf.attacks.ignoreVillage();
960 // does this work?
961 }
962 // allow report reading if we are botting
963 if (twf.data.settings.waitForTroops) {
964 twf.reports.allowAutomaticReading = true;
965 }
966
967 noSkippedVil = twf.attacks.reportSendUnits(coord);
968 }
969 if (twf.attacks.continueAttack && noSkippedVil) {
970 console.debug("Em ataques. Ataque, antes de pressionar #target_attack");
971 twf.attacks.hiddenFrame.contents().find('.target-input-field.target-input-autocomplete.ui-autocomplete-input').val(coord);
972 twf.attacks.hiddenFrame.contents().find('#target_attack').click();
973 twf.attacks.attacking = true;
974 twf.helper.spinner.show();
975 twf.helper.log("Atacando [" + coord + "]!", twf.helper.MESSAGE_SUCCES);
976 return true;
977 // not enough units but botting, so wait
978 } else if (twf.data.settings.waitForTroops && noSkippedVil) {
979 if (!twf.data.timers.attackPollTimer) {
980 // if we haven't got a timer, run it
981
982 // this finds the time of the first returning attacks
983 let rows = twf.attacks.hiddenFrame.contents().find('[data-command-type="return"], [data-command-type="cancel"]').parents('.command-row');
984 if (rows.length > 0) {
985 let time = rows.eq(rows.length - 1).find('[data-endtime]').html().split(":");
986 time[0] = twf.helper.leadingZero(time[0]);
987
988 let secondsToArrival = 3600 * parseInt(time[0]) + 60 * parseInt(time[1]) + parseInt(time[2]) + 1;
989
990 // create a variance variable (triangular?) distributed [-earlyTime, lateTime],
991 let pollVariance = 0;
992 if (twf.data.settings.pollLate) {
993 pollVariance += twf.helper.getRandomSecondsBetween(twf.data.settings.minPollAttack, twf.data.settings.minPollAttack + twf.data.settings.pollLateSeconds);
994 }
995
996 let timeToCheck = Math.floor((secondsToArrival + pollVariance + 1));
997 let s = timeToCheck % 60;
998 let m = Math.floor((timeToCheck / 60) % 60);
999 let h = Math.floor((timeToCheck / 3600));
1000
1001 twf.data.timers.attackPollTimer = window.setTimeout(twf.attacks.poll, timeToCheck * 1000);
1002 twf.helper.log("Arrival in: " + time.join(":") + ". Checking in: " + h + ":" + twf.helper.leadingZero(m) + ":" + twf.helper.leadingZero(s), twf.helper.MESSAGE_DEFAULT);
1003 } else {
1004 var waitTime = 60;
1005 twf.data.timers.attackPollTimer = window.setTimeout(twf.attacks.poll, waitTime * 1000);
1006 twf.helper.log("Não há chegadas :(. Verificar " + waitTime + " segundos.");
1007 }
1008 }
1009 }
1010 },
1011 poll: function() {
1012 twf.data.timers.attackPollTimer = null;
1013 twf.attacks.continueAttack = true;
1014 twf.attacks.attacking = true;
1015 twf.attacks.hiddenFrame.attr('src', twf.attacks.hiddenFrame.attr('src'));
1016 },
1017 // also stops timer
1018 stopAttack: function() {
1019 twf.attacks.attackButton.show();
1020 twf.attacks.sAttackButton.hide();
1021 twf.attacks.attacking = false;
1022 twf.attacks.continueAttack = false;
1023 if (twf.data.timers.attackPollTimer) { //remove polling timer
1024 window.clearTimeout(twf.data.timers.attackPollTimer);
1025 twf.data.timers.attackPollTimer = null;
1026 }
1027 if (twf.attacks.getPosition() >= twf.attacks.villageArray.length) {
1028 twf.helper.log("Ciclo finalizado. Trocar para a primeira aldeia.", twf.helper.MESSAGE_DEFAULT);
1029 twf.attacks.resetAttack(true);
1030 }
1031 if (twf.data.timers.reportPollTimer) {
1032 window.clearTimeout(twf.data.timers.reportPollTimer);
1033 twf.data.timers.reportPollTimer = null;
1034 }
1035 twf.reports.allowAutomaticReading = false;
1036 twf.helper.log("O Bot foi interrompido.", twf.helper.MESSAGE_DEFAULT);
1037 },
1038 resetAttack: function(skipLog) {
1039 if (!skipLog) {
1040 twf.helper.log("Trocar para a primeira aldeia.", twf.helper.MESSAGE_DEFAULT);
1041 }
1042 twf.attacks.attackTemplates[twf.attacks.currentAttackTemplateTimestamp].position = 0;
1043 $('#attacked_villages').text(twf.attacks.getPosition() + 1 + "/" + twf.attacks.villageArray.length);
1044 twf.data.storeTownLevel('attackTemplates', twf.attacks.attackTemplates, true)
1045 },
1046 onFrameLoaded: function() {
1047 console.debug("Entered attacks.onFrameLoaded");
1048 try {
1049 twf.helper.spinner.fadeOut();
1050 twf.helper.checkBotProtection(this.hiddenFrame);
1051
1052 // check on which screen we are and do something accordingly
1053 // confirm attack screen
1054 let confirmAttack = twf.attacks.hiddenFrame.contents().find("#troop_confirm_go");
1055 let error = twf.attacks.hiddenFrame.contents().find('.error_box');
1056 // we are attacking a player
1057 let isPlayerEn = twf.attacks.hiddenFrame.contents().find('table.vis td:contains("Player:")');
1058 let isPlayerNl = twf.attacks.hiddenFrame.contents().find('table.vis td:contains("Speler:")');
1059 if (error && error.length > 0) {
1060 let coords = twf.attacks.villageArray[twf.attacks.getPosition()];
1061 twf.helper.log("Error: " + error.html() + " --- Continuar para proxima aldeias (ignorado [" + coords + "])", twf.helper.MESSAGE_ERROR);
1062 return twf.attacks.ignoreVillage();
1063 }
1064 if ((isPlayerEn.length > 0 || isPlayerNl.length > 0) && !twf.data.settings.attackPlayers) {
1065 let coords = twf.attacks.villageArray[twf.attacks.getPosition()];
1066 twf.helper.log("O proprietário é um jogador! Continuar para proxima aldeias (ignorado [" + coords + "])", twf.helper.MESSAGE_ERROR);
1067 return twf.attacks.ignoreVillage();
1068 }
1069 // select troop screen
1070 if (confirmAttack.size() == 0) {
1071
1072 //============= BEGIN REPORT READING
1073 // basically, if we are in place, also extract next arriving attack and set a timer
1074 // TODO MAYBE CHANGE THIS TO JUST DO IT AT THE SAME TIME AS SENDING ATTACKS
1075 if (twf.data.settings.reportFarmer && twf.reports.allowAutomaticReading && twf.data.timers.reportPollTimer == null && !twf.reports.currentlyReading) {
1076 // automatically read report
1077 if (twf.attacks.hiddenFrame.contents().find('[data-command-type="attack"]').length > 0) {
1078 let rows = twf.attacks.hiddenFrame.contents().find('[data-command-type="attack"]').parents('.command-row');
1079 let time = rows.eq(rows.length - 1).find('[data-endtime]').html().split(":");
1080 time[0] = twf.helper.leadingZero(time[0]);
1081
1082 let secondsToArrival = 3600 * parseInt(time[0]) + 60 * parseInt(time[1]) + parseInt(time[2]) + 1;
1083
1084 // create a variance variable (triangular?) distributed [-earlyTime, lateTime],
1085 let pollVariance = 0;
1086
1087 if (twf.data.settings.pollLate) {
1088 pollVariance += twf.helper.getRandomSecondsBetween(twf.data.settings.minPollReport, twf.data.settings.minPollReport + twf.data.settings.pollLateSeconds * 5);
1089 }
1090
1091 let timeToCheck = Math.floor((secondsToArrival + pollVariance + 1));
1092 let s = timeToCheck % 60;
1093 let m = Math.floor((timeToCheck / 60) % 60);
1094 let h = Math.floor((timeToCheck / 3600));
1095
1096 twf.data.timers.reportPollTimer = window.setTimeout(function() {
1097 twf.data.timers.reportPollTimer = null;
1098 twf.reports.startReading();
1099 }, timeToCheck * 1000);
1100 twf.helper.log("Ataque chegando em: " + time.join(":") + ". Relatórios de leitura: " + h + ":" + twf.helper.leadingZero(m) + ":" + twf.helper.leadingZero(s), twf.helper.MESSAGE_DEFAULT);
1101 }
1102 }
1103 // ========== END REPORT READING
1104
1105 twf.attacks.loadAttack(twf.attacks.currentAttackTemplateTimestamp);
1106 twf.attacks.showAttack();
1107 if (twf.attacks.attacking && twf.attacks.continueAttack) {
1108 twf.attacks.attack();
1109 }
1110 } else {
1111 console.debug("Confirmar ataque para " + twf.attacks.villageArray[twf.attacks.getPosition()]);
1112 //update attack information and click confirm
1113 twf.attacks.attackTemplates[twf.attacks.currentAttackTemplateTimestamp].position = twf.attacks.getPosition() + 1;
1114 if (twf.attacks.getPosition() >= twf.attacks.villageArray.length) {
1115 if (twf.data.settings.resetToFirst) {
1116 twf.attacks.resetAttack()
1117 } else {
1118 twf.attacks.stopAttack()
1119 }
1120 }
1121 twf.data.storeTownLevel('attackTemplates', twf.attacks.attackTemplates);
1122 twf.helper.spinner.show();
1123 confirmAttack.click();
1124 }
1125 } catch (error) {
1126 //console.error(error);
1127 //twf.helper.stopEverything();
1128 console.error(error);
1129 alert("BOT PROTECTION?\n" + new Date() + "\n" + error);
1130 }
1131 },
1132 // shows the currently loaded attack in the panel and bind clicking on it
1133 showAttack: function() {
1134 $("#attackUnits").html("");
1135 for (let i in this.unitsPerAttack) {
1136 if (this.unitsPerAttack[i] > 0) {
1137 var unitsAvailable = this.hiddenFrame.contents().find('#units_entry_all_' + i).html() || "???";
1138 var unitsText = i + ': ' + this.unitsPerAttack[i] + ' (' + unitsAvailable.trim().substring(1, unitsAvailable.length - 1) + ')';
1139 $('<img />').attr('src', 'https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_' + i + '.png').attr('title', unitsText).attr('alt', unitsAvailable).appendTo($('#attackUnits')).click(function(event) {
1140 twf.helper.showAttackTemplate(twf.attacks.currentAttackTemplateTimestamp);
1141 $('#template_popup #unit_input_' + i).focus().select();
1142 });
1143 $('<span />').html('(' + this.unitsPerAttack[i] + ') ').css({
1144 'color': '#000'
1145 }).appendTo($("#attackUnits"));
1146 }
1147 }
1148 },
1149 // loads an attack from a template to prepare it for take0off
1150 loadAttack: function(timestamp) {
1151 // no templates -> error
1152 if (!this.attackTemplates) {
1153 //twf.helper.log("No attack templates available!", twf.helper.MESSAGE_WARNING);
1154 return;
1155 }
1156 // no ts -> load first one
1157 if (!timestamp) {
1158 timestamp = Object.keys(this.attackTemplates)[0];
1159 }
1160 // else load the one given
1161 this.currentAttackTemplateTimestamp = timestamp;
1162 let attack = this.attackTemplates[timestamp];
1163 $("#attackName").html(attack.name);
1164 for (let i in twf.data.unitTypes) {
1165
1166 this.unitsPerAttack[twf.data.unitTypes[i]] = attack.units[twf.data.unitTypes[i]];
1167 //console.log(this.unitsPerAttack)
1168 }
1169 this.villageString = attack.coords.join(" ");
1170 this.villageArray = attack.coords;
1171 this.villageArray = twf.helper.sortByDistance(this.villageArray, game_data.village.coord);
1172 this.showAttack();
1173 $('#attacked_villages').text(this.getPosition() + 1 + "/" + this.villageArray.length);
1174 return attack;
1175 },
1176 // fills the list of attack templates
1177 populateTemplateList: function() {
1178 $("#attackList").children().remove();
1179 for (let timestamp in this.attackTemplates) {
1180 let row = $('<tr/>').appendTo($("#attackList"));
1181 $('<td title="Carregue este ataque" />').html('L').bind('click', {
1182 attack: timestamp
1183 }, function(event) {
1184 twf.attacks.loadAttack(event.data.attack);
1185 }).css({
1186 'border': '1px solid #0F0',
1187 'width': '10px',
1188 'cursor': 'pointer',
1189 'color': '#0F0',
1190 'background-color': '#000'
1191 }).appendTo(row);
1192 $('<td>' + this.attackTemplates[timestamp].name + '</td>').appendTo(row);
1193 $('<td title="Remova esse ataque (NÃO PODE SER DESTACADO)" />').html('X').bind('click', {
1194 attack: timestamp
1195 }, function(event) {
1196 if (confirm("Você tem certeza que deseja remover'" + twf.attacks.attackTemplates[event.data.attack].name + "'?")) {
1197 twf.attacks.removeAttackTemplate(event.data.attack);
1198 }
1199 }).css({
1200 'border': '1px solid #f00',
1201 'width': '10px',
1202 'cursor': 'pointer',
1203 'color': '#f00',
1204 'background-color': '#000'
1205 }).appendTo(row);
1206 }
1207 },
1208 // saves an attack template and reloads the templatelist
1209 saveAttackTemplate: function() {
1210 // check if data is valid
1211 if (!$("#unit_input_name").val() || $("#unit_input_coords").val().match(/[\d{3}\|\d{3}\s]+/) === null) {
1212 $(".quest-summary").css("background-color", "red");
1213 $(".quest-summary")[0].innerHTML = "Certifique-se de preencher os caracteres e nomear corretamente e tente novamente!";
1214 return false;
1215 }
1216 // fill table of units
1217 let unitsToSend = {};
1218 for (let i in twf.data.unitTypes) {
1219 unitsToSend[twf.data.unitTypes[i]] = parseInt($("#unit_input_" + twf.data.unitTypes[i]).val()) || 0;
1220 }
1221 //clear coords from empty values
1222 let tempCoords1 = $("#unit_input_coords").val().trim().split(" ");
1223 let tempCoords2 = [];
1224 for (let i in tempCoords1) {
1225 if (tempCoords1[i]) {
1226 tempCoords2.push(tempCoords1[i]);
1227 }
1228 }
1229 // fill template
1230 let template = {
1231 name: $("#unit_input_name").val(),
1232 units: unitsToSend,
1233 coords: tempCoords2,
1234 position: $("#unit_input_position").val(),
1235 ignoreScouts: $('#ignore_scouts').is(":checked"),
1236 }
1237 // save
1238 if (!twf.attacks.attackTemplates) { // no attacktemplates so it is reset to null when loading
1239 // so we have to instantiate it now
1240 twf.attacks.attackTemplates = {};
1241 }
1242 twf.attacks.attackTemplates[$("#unit_input_timestamp").val()] = template;
1243 twf.data.storeTownLevel("attackTemplates", this.attackTemplates);
1244 twf.helper.log("Salvo o novo modelo de ataque: " + template.name, twf.helper.MESSAGE_SUCCES);
1245 this.populateTemplateList();
1246 this.loadAttack($("#unit_input_timestamp").val());
1247 $("#template_popup").hide();
1248 },
1249 // removes an attack template from storage and reloads the templatelist
1250 removeAttackTemplate: function(timestamp) {
1251 delete this.attackTemplates[timestamp]
1252 if (this.currentAttackTemplateTimestamp == timestamp) {
1253 this.loadAttack();
1254 }
1255 twf.data.storeTownLevel("attackTemplates", this.attackTemplates);
1256 this.populateTemplateList();
1257 },
1258 // loads all attack templates from storage
1259 loadAttackTemplates: function() {
1260 this.attackTemplates = twf.data.loadTownLevel("attackTemplates");
1261 this.populateTemplateList();
1262 if (this.attackTemplates) {
1263 let l = Object.keys(this.attackTemplates).length;
1264 twf.helper.log("Loaded " + l + " attack template" + ((l > 1) ? "s." : "."), twf.helper.MESSAGE_SUCCES);
1265 } else {
1266 twf.helper.log("Nenhum modelo de ataque a ser carregado.<b> Create one first! </b>", twf.helper.MESSAGE_WARNING);
1267 }
1268 },
1269 // returns the current village to attack (persistent throughout runs)
1270 getPosition: function() {
1271 return parseInt(this.attackTemplates[this.currentAttackTemplateTimestamp].position);
1272 },
1273 ignoreVillage: function() {
1274 console.group("ignoreVillage");
1275 // before update
1276 console.log("BEFORE UPDATE");
1277 console.log("attackTemplates: ", this.attackTemplates);
1278 console.log("url: ", this.hiddenFrame[0].src);
1279 console.log("villageArray: ", this.villageArray);
1280 console.log("attTemplate timestamp: ", this.currentAttackTemplateTimestamp);
1281 console.log("attTemplate[timestamp].pos: ", this.attackTemplates[this.currentAttackTemplateTimestamp].position);
1282 console.log("getPosition: ", this.getPosition());
1283 console.log("currentCoords: ", this.villageArray[this.getPosition()]);
1284
1285 this.attackTemplates[this.currentAttackTemplateTimestamp].position = this.getPosition() + 1;
1286
1287 console.log("===========================");
1288 console.log("AFTER UPDATE");
1289 console.log("attackTemplates: ", this.attackTemplates);
1290 console.log("url: ", this.hiddenFrame[0].src);
1291 console.log("villageArray: ", this.villageArray);
1292 console.log("attTemplate timestamp: ", this.currentAttackTemplateTimestamp);
1293 console.log("attTemplate[timestamp].pos: ", this.attackTemplates[this.currentAttackTemplateTimestamp].position);
1294 console.log("getPosition", this.getPosition());
1295 console.log("currentCoords: ", this.villageArray[this.getPosition()]);
1296 //console.error("IGNORED A VILLAGE, BUT STILL PRESS OKAY");
1297 //console.error("Village to be ignored: " + this.villageArray[this.attackTemplates[this.currentAttackTemplateTimestamp].position - 1] + ". Village to continue with: " + this.villageArray[this.attackTemplates[this.currentAttackTemplateTimestamp].position]);
1298 if (this.getPosition() >= this.villageArray.length) {
1299 if (twf.data.settings.resetToFirst) {
1300 this.resetAttack();
1301 } else {
1302 this.stopAttack();
1303 }
1304 }
1305
1306 twf.data.storeTownLevel('attackTemplates', twf.attacks.attackTemplates);
1307 console.log("===========================");
1308 console.log("BEFORE REFRESH");
1309 console.log("url: ", this.hiddenFrame[0].src);
1310 console.log("url New : ", this.hiddenFrameUrl);
1311 console.groupEnd();
1312 this.hiddenFrame.attr('src', this.hiddenFrameUrl);
1313 console.log("Returning true in ignoreVillage()");
1314 return true;
1315 }
1316 },
1317 helper: {
1318 MESSAGE_ERROR: 0,
1319 MESSAGE_SUCCES: 1,
1320 MESSAGE_DEFAULT: 2,
1321 MESSAGE_WARNING: 3,
1322 splash: null,
1323 stickyPanel: false,
1324 panelInTransit: false,
1325 panelOut: false,
1326 init: function() {
1327 //append all html snippets and set their click events
1328 $("head").append(twf.html.css);
1329 $(twf.html.templatePopup).appendTo('body').hide();
1330 $(twf.html.settingsPopup).appendTo('body').hide();
1331 this.panel = $(twf.html.panel).appendTo('body').bind("mouseenter", twf.helper.panelMouseIn).bind("mouseleave", twf.helper.panelMouseOut);
1332
1333 this.messages = $('#messages');
1334 this.spinner = $('#loading');
1335
1336 $('#save_attack_template').click(function() {
1337 twf.attacks.saveAttackTemplate();
1338 });
1339 $('.close_template_button').click(function() {
1340 $('#template_popup').hide();
1341 })
1342 $('.close_settings_button').click(function() {
1343 $('#settings_popup').hide();
1344 })
1345 $('#save_settings').click(function() {
1346 twf.data.saveSettings();
1347 });
1348 $('#show_settings').click(function() {
1349 twf.helper.showSettings();
1350 });
1351 $("#wallbreaker").click(function() {
1352 twf.helper.log("Este módulo ainda não está terminado.", twf.helper.MESSAGE_ERROR);
1353 })
1354
1355 $('#tack').click(this.toggleSticky).find('.on').hide();
1356
1357 $('#attackUnits').attr('title', 'Clique nas imagens para editar o modelo');
1358
1359 twf.helper.log("Módulo auxiliar pronto.", twf.helper.MESSAGE_SUCCES);
1360 },
1361 createHiddenFrame: function(url, onload, frameId) {
1362 return $('<iframe id="' + frameId + '" src="' + url + '" />').load(onload).css({
1363 width: '100px',
1364 height: '100px',
1365 position: 'absolute',
1366 left: '-1000px'
1367 }).appendTo('body').hide();
1368 },
1369 showSettings: function() {
1370 // checkboxes
1371 $('#reset_to_first').prop('checked', twf.data.settings.resetToFirst);
1372 $('#wait_for_troops').prop('checked', twf.data.settings.waitForTroops);
1373 $('#attack_players').prop('checked', twf.data.settings.resetToFirst);
1374 $('#autostop').prop('checked', twf.data.settings.autoStop);
1375 $('#poll_late').prop('checked', twf.data.settings.pollLate);
1376 $('#discount_time').prop('checked', twf.data.settings.discountTime);
1377 $('#report_farmer').prop('checked', twf.data.settings.reportFarmer);
1378
1379 //values
1380 $('#autostop_minutes').val(twf.data.settings.autoStopMinutes);
1381 $('#poll_late_seconds').val(twf.data.settings.pollLateSeconds);
1382 $('#discount_factor').val(twf.data.settings.discountFactor == 1 ? twf.data.settings.discountFactor + ".00" : twf.data.settings.discountFactor);
1383 $("#report_max_read_age").val(twf.data.settings.reportMaxReadAge);
1384 $("#report_max_use_age").val(twf.data.settings.reportMaxUseAge);
1385
1386 //show
1387 $('#settings_popup').show();
1388 },
1389 showAttackTemplate: function(timestamp) {
1390 // if timestamp, load old if exists
1391 if (timestamp && twf.attacks.attackTemplates.hasOwnProperty(timestamp)) {
1392 $("#template_popup #unit_input_timestamp").val(timestamp);
1393 $("#template_popup #unit_input_position").val(twf.attacks.attackTemplates[timestamp].position);
1394 $("#template_popup #unit_input_coords").val(twf.attacks.attackTemplates[timestamp].coords.join(" ").trim());
1395 $("#template_popup #unit_input_name").val(twf.attacks.attackTemplates[timestamp].name);
1396 for (let i in twf.data.unitTypes) {
1397 $("#template_popup #unit_input_" + twf.data.unitTypes[i]).val(twf.attacks.attackTemplates[timestamp].units[twf.data.unitTypes[i]])
1398 }
1399 $('#ignore_scouts').prop('checked', twf.attacks.attackTemplates[timestamp].ignoreScouts);
1400 } else {
1401 $("#template_popup #unit_input_timestamp").val(+new Date());
1402 $("#template_popup #unit_input_position").val('0');
1403 $("#template_popup #unit_input_coords").val('');
1404 $("#template_popup #unit_input_name").val('');
1405 for (let i in twf.data.unitTypes) {
1406 $("#template_popup #unit_input_" + twf.data.unitTypes[i]).val('')
1407 }
1408 $('#ignore_scouts').prop('checked', false);
1409 }
1410 $("#template_popup").show();
1411 },
1412 panelMouseIn: function() {
1413 if (!twf.helper.stickyPanel && !twf.helper.panelInTransit && !twf.helper.panelOut) {
1414 twf.helper.panelInTransit = true;
1415 twf.helper.panel.animate({
1416 "right": "+=314px"
1417 }, "slow", function() {
1418 twf.helper.panelInTransit = false;
1419 twf.helper.panelOut = true;
1420 })
1421 }
1422 },
1423 panelMouseOut: function() {
1424 if (!twf.helper.stickyPanel && !twf.helper.panelInTransit && twf.helper.panelOut) {
1425 twf.helper.panelInTransit = true;
1426 twf.helper.panel.animate({
1427 "right": "-=314px"
1428 }, "slow", function() {
1429 twf.helper.panelInTransit = false;
1430 twf.helper.panelOut = false;
1431 })
1432 }
1433 },
1434 toggleSticky: function() {
1435 twf.helper.stickyPanel = !twf.helper.stickyPanel;
1436 $('#tack').find('.on').toggle();
1437 $('#tack').find('.off').toggle();
1438 },
1439 sortByDistance: function(arr, me) {
1440 return arr.sort(function(a, b) {
1441 a = a.split("|");
1442 b = b.split("|");
1443 let from = me.split("|");
1444 let d1 = (a[0] - from[0]) * (a[0] - from[0]) + (a[1] - from[1]) * (a[1] - from[1]);
1445 let d2 = (b[0] - from[0]) * (b[0] - from[0]) + (b[1] - from[1]) * (b[1] - from[1]);
1446 return d1 - d2;
1447 });
1448
1449 },
1450 getServerTime: function(frame) {
1451 let serverTime = frame.contents().find('#serverTime').text().split(":"); //[7; 14; 05] for example
1452 serverTime[0] = twf.helper.leadingZero(parseInt(serverTime[0])) // add leading zero to hour
1453 let serverDate = frame.contents().find('#serverDate').text().split("/").reverse().join("-");
1454 //console.debug(serverDate);
1455 // assume everything in UTC, but it does not matter since their timezone is always the same.
1456 return new Date(serverDate + "T" + serverTime.join(":") + "Z");
1457 },
1458 getResPerHour: function(level) {
1459 if (level == 0) {
1460 return 5;
1461 }
1462 return 30 * Math.pow(1.163118, level - 1);
1463 },
1464 getTravelTimeInMinutes: function(distance, unit) {
1465 return distance * twf.data.travelTime[unit] / twf.data.worldSettings.speed / twf.data.worldSettings.unitSpeed;
1466 },
1467 getDistance: function(opp, me) {
1468 let a = opp.split("|");
1469 let b = me.split("|");
1470 return Math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
1471 },
1472 getMaxStorage: function(level) {
1473 return Math.round(1000 * Math.pow(1.2294934, (level - 1)));
1474 },
1475 getMaxHiding: function(level) {
1476 return Math.round(150 * Math.pow((4 / 3), (level - 1)));
1477 },
1478 leadingZero: function(a) {
1479 return (a < 10) ? '0' + a : a;
1480 },
1481 getRandomSecondsBetween: function(low, high) {
1482 if (low >= high) {
1483 console.log("Valor errado para baixo, alto:" + [low, high]);
1484 return null;
1485 }
1486 return Math.random() * (high - low) + low;
1487 },
1488 checkBotProtection: function(frame) {
1489 if (!frame) return true;
1490
1491 if (frame.contents().find(".g-recaptcha, #bot_check").length > 0 || $(".g-recaptcha, #bot_check").length > 0 || frame.contents().find("body").data("bot-protect")) {
1492 console.warn("BOT PROTECTION DETECTED!");
1493 this.stopEverything();
1494 alert("BOT PROTECTION DETECTED! --- " + new Date());
1495 }
1496 },
1497 stopEverything: function() {
1498 twf.attacks.stopAttack();
1499 twf.reports.stopReading();
1500 if (twf.data.timers.reportPollTimer) {
1501 clearTimeout(twf.data.timers.reportPollTimer);
1502 console.debug("Stopped reportPollTimer.");
1503 }
1504 },
1505 log: function(text, messageType) {
1506 let date = new Date();
1507 let message = '<i>' + twf.helper.leadingZero(date.getHours()) + ':' + twf.helper.leadingZero(date.getMinutes()) + ':' + twf.helper.leadingZero(date.getSeconds()) + ': </i>';
1508 switch (messageType) {
1509 case this.MESSAGE_ERROR:
1510 message += '<span style="color: #F00;">' + text + '</span>';
1511 break;
1512 case this.MESSAGE_SUCCES:
1513 message += '<span style="color: #0F0;">' + text + '</span>';
1514 break;
1515 case this.MESSAGE_WARNING:
1516 message += '<span style="color:#FFA500;">' + text + '</span>';
1517 break;
1518 default:
1519 message += '<span style="color: #FFF;">' + text + '</span>';
1520 break;
1521 }
1522 twf.helper.messages.append('<li>' + message + '</li>');
1523 twf.helper.messages.scrollTop(twf.helper.messages[0].scrollHeight);
1524 },
1525 },
1526 html: {
1527 templatePopup: '<div id="template_popup" class="popup_box_container"> <div class="popup_box show" id="popup_box_quest" style="width: 700px;"> <div class="popup_box_content"> <a class="popup_box_close close_template_button" href="#"> </a> <div style="width: 700px"> <div style="background: no-repeat url(' + " '/graphic/paladin_new.png' " + ');"> <h3 style="margin: 0 3px 5px 120px;">Crie um modelo.</h3> <table align="right" style="margin-bottom: 5px;"> <tbody> <tr> <td class="quest-summary" style="width: 583px"> Crie um modelo aqui para usar no Farm automático. Se você ativou <em>Report Farming</ em> em <em>Configurações</ em>, note que os valores aqui são valores mÃnimos. Por Fim Lembra-se que na configuração deve ser inserido o nome do modelo a seu gosto, e deve ser posta as coordenadas quantas desejar.</td> </tr> </tbody> </table> <div class="quest-goal"> <table style="border:none;"> <tbody> <tr> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <th>Infantry</th> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="spear"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_spear.png" title="Spear" alt="" class=""></a> <input id="unit_input_spear" name="spear" type="text" style="width: 40px" tabindex="1" value="" class="unitsInput" data-all-count="39"></td> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="sword"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_sword.png" title="Sword" alt="" class=""></a> <input id="unit_input_sword" name="sword" type="text" style="width: 40px" tabindex="2" value="" class="unitsInput" data-all-count="20"> </td> </tr> <tr> <td class="nowrap"> <a href="#" class="unit_link" data-unit="axe"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_axe.png" title="Axe" alt="" class=""></a> <input id="unit_input_axe" name="axe" type="text" style="width: 40px" tabindex="3" value="" class="unitsInput" data-all-count="0"></td> </tr> <tr> <td class="nowrap"> <a href="#" class="unit_link" data-unit="archer"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_archer.png" title="Archer" alt="" class=""></a> <input id="unit_input_archer" name="archer" type="text" style="width: 40px" tabindex="4" value="" class="unitsInput" data-all-count="0"> </td> </tr> </tbody> </table> </td> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <th>Cavalry</th> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="spy"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_spy.png" title="Spy" alt="" class=""></a> <input id="unit_input_spy" name="spy" type="text" style="width: 40px" tabindex="5" value="" class="unitsInput" data-all-count="4"></td> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="light"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_light.png" title="LC" alt="" class=""></a> <input id="unit_input_light" name="light" type="text" style="width: 40px" tabindex="6" value="" class="unitsInput" data-all-count="13"> </td> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="marcher"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_marcher.png" title="Marcher" alt="" class=""></a> <input id="unit_input_marcher" name="marcher" type="text" style="width: 40px" tabindex="7" value="" class="unitsInput" data-all-count="0"></td> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="heavy"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_heavy.png" title="Heavy" alt="" class=""></a> <input id="unit_input_heavy" name="heavy" type="text" style="width: 40px" tabindex="8" value="" class="unitsInput" data-all-count="0"> </td> </tr> </tbody> </table> </td> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <th>Cerco</th> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="ram"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_ram.png" title="Ram" alt="" class=""></a> <input id="unit_input_ram" name="ram" type="text" style="width: 40px" tabindex="9" value="" class="unitsInput" data-all-count="0"> </td> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="catapult"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_catapult.png" title="Catapult" alt="" class=""></a> <input id="unit_input_catapult" name="catapult" type="text" style="width: 40px" tabindex="10" value="" class="unitsInput" data-all-count="0"></td> </tr> </tbody> </table> </td> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <th>Outras</th> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="knight"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_knight.png" title="Knight" alt="" class=""></a> <input id="unit_input_knight" name="knight" type="text" style="width: 40px" tabindex="11" value="" class="unitsInput" data-all-count="0"> </td> </tr> <tr> <td class="nowrap "> <a href="#" class="unit_link" data-unit="snob"><img src="https://dsnl.innogamescdn.com/8.67/31807/graphic/unit/unit_snob.png" title="Snob" alt="" class=""></a> <input id="unit_input_snob" name="snob" type="text" style="width: 40px" tabindex="12" value="" class="unitsInput" data-all-count="0"> </td> </tr> </tbody> </table> </td> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <th>Configurações</th> </tr> <tr> <td class="nowrap "> <input id="unit_input_name" name="name" type="text" style="width: 100%;" tabindex="13" value="" class="unitsInput" data-all-count="0" placeholder="Name"> </td> </tr> <tr> <td class="nowrap "> <input id="unit_input_coords" name="coords" type="text" style="width: 100%;" tabindex="13" value="" class="unitsInput" data-all-count="0"> </td> </tr> <tr> <td title="Envie ataques independentemente da quantidade de spys disponÃveis." class="nowrap"> <input id="ignore_scouts" name="ignore_scouts" type="checkbox" /> <label for="ignore_scouts">Ignorar poucos spys</label> </td> </tr> </tbody> </table> <input type="hidden" id="unit_input_timestamp" value="" /> <input type="hidden" id="unit_input_position" value="" /> </td> </tr> </tbody> </table> </div> </div> <div align="center" style="padding: 10px;"> <a class="btn close_template_button" href="#">Fechar</a> <a class="btn" id="save_attack_template">Salvar e fechar</a> </div> </div> </div> </div> <div class="fader"></div> </div>',
1528 css: '<style type="text/css">#settings_table td{ padding: 0 5px 0 5px; }#panel { background-color: #000000; border: 0 none; box-shadow: 5px 5px 10px #999999; border-bottom-left-radius: 15px; border-top-left-radius: 15px; -webkit-border-bottom-left-radius: 15px; -moz-border-radius-bottomleft: 15px; -webkit-border-top-left-radius: 15px; -moz-border-radius-topleft: 15px; float: right; color: #ddd; font-size: 10px; line-height: 1.5em; margin-right: 0%; opacity: 0.95; padding: 15px; padding-top: 1px; position: fixed; top: 60px; right: -315px; text-align: left; width: 300px; z-index: 12000; } #attackName { margin: 0 } #buttons {} #buttons button { width: 144px; margin: 0 2px; text-align: center;} #buttons input[type "checkbox"] { margin: 5px 2px 0 0; } #buttons p { width: 145px } #buttons label { width: 129px; display: inline-block } #unitTable { background: #000; width: 300px; } #unitTable.vis td { background: #000; } #attackListWrapper { height: 90px; width: 310px; overflow-y: auto; } #attackList { width: 300px; margin-top: 10px; } #attackList tr { height: 10px; } #attackList tr: nth-child(odd) { background-color: #c0c0c0; color: #0c0c0c; } #attackUnits { cursor: pointer; } #rAttackListWrapper { /*height: 80px;*/ width: 310px; overflow-y: auto; } #rAttackList { width: 300px; margin-top: 10px; } #rAttackList tr { height: 10px; color: #f00; font-wheight: bold; } #rAttackList tr.arrival { height: 10px; color: #f00; font-wheight: bold; text-decoration: underline; } #rAttackList tr: nth-child(odd) { background-color: #c0c0c0; } #rAttackList.timer { width: 50px; } #tack { margin: 0; cursor: pointer; } #loading { position: absolute; right: 0; bottom: 0; } #messages { list-style: none; width: 310px; height: 200px; overflow: auto; padding: 0 } #messages.note {} #messages.nor { color: #0f0; } #messages.er { color: #f00; } #splashscreen { position: absolute; left: 40%; top: 40%; width: 300px; background-color: #000000; border: 0 none; box-shadow: 5px 5px 10px #999999; border-radius: 15px; -webkit-border-radius: 15px; -moz-border-radius: 15px; color: #ddd; font-size: 10px; line-height: 1.5em; opacity: 0.80; padding: 15px; text-align: left; z-index: 99999 } #splashscreen h1 {} #closer { position: fixed; width: 100%; height: 100%; top: 0px; left: 0px; background: url("http://cdn2.tribalwars.net/graphic/index/grey-fade.png?01a9d"); z-index: 12000; } #captchaframe { position: absolute; left: 30%; top: 20%; width: 600px; background-color: #000000; border: 0 none; box-shadow: 5px 5px 10px #999999; border-radius: 15px; -webkit-border-radius: 15px; -moz-border-radius: 15px; color: #ddd; font-size: 10px; line-height: 1.5em; opacity: 0.80; padding: 15px; text-align: left; z-index: 99999 } #captchacloser { position: fixed; width: 100%; height: 100%; top: 0px; left: 0px; background: url("http://cdn2.tribalwars.net/graphic/index/grey-fade.png?01a9d"); z-index: 12000; } .timer {} .tooltip { display: none; position: absolute; left: -10px; background-color: #fff; color: #000; }</style>',
1529 panel: '<div id="panel"> <span id="tack"><img style="" src="https://openclipart.org/image/20px/svg_to_png/89059/394580430943859083405.png&disposition=attachment" class="off" height="20" /><img src="https://openclipart.org/image/20px/svg_to_png/33601/thumb%20tack%202%20plain.png&disposition=attachment" class="on" height="20" />Maker by Jari - by Carigan by Marcos v.s Marques</span> <div id="newContent"> <div id="loading"><img src="graphic/throbber.gif" title="Carregando algo, aguarde..." alt="Carregando algo, aguarde.." /></div> <ul id="messages"> <li>Layout inicializado</li> <li>Carregando as tropas disponÃveis</li> </ul> <div id="attackListWrapper"> <table id="attackList"></table> </div> <div id="rAttackListWrapper"> <table id="rAttackList"></table> </div> <h3 id="attackName"></h3> <table id="unitTable"> <tbody> <tr> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <td id="attackUnits" class="nowrap"><img src="http://cdn2.tribalwars.net/graphic/command/attack.png?0019c" title="Attacked villages" alt="Attacked villages" class="" /><input id="attackedVillages" name="attackedVillages" type="text" style="width: 40px" tabindex="10" value="" class="unitsInput" /><i style="color: #000;" id="amount_of_attackedVillages">Procurando...</i> </td> </tr> </tbody> </table> </td> </tr> <tr> <td valign="top"> <table class="vis" width="100%"> <tbody> <tr> <td style="color: black; width: 50%;" class="nowrap attacked_villages">Aldeias: <span id="attacked_villages">Procurando.</span></td> <td style="color: black; width: 50%;" class="nowrap reports_left">Relatórios: <span id="reports_left">Esperando dados.</span></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <div id="buttons"> <button class="btn btn-attack btn-disabled" id="attackButton" style="width: 296px; padding-right: 25px; padding-left: 25px;" disabled>Ataque</button> <button class="btn btn-cancel btn-disabled" id="sAttackButton" style="display:none; width: 296px; padding-right: 25px; padding-left: 25px;" disabled>Cancelar ataque</button> <button class="btn btn-recruit btn-disabled" id="cAttackButton" disabled>Novo ataque</button> <button class="btn btn-disabled" id="resetAttack" title="Redefinir o ataque para a primeira vila" disabled>Reiniciar contador</button> <button class="btn btn-research btn-disabled" id="show_settings" disabled>Configurações</button> <button class="btn btn-disabled" id="start_reports" disabled>Leia relatórios</button> <button class="btn btn-cancel btn-disabled" id="stop_reports" style="display:none;" disabled>Pare de ler</button> <button class="btn btn-pp btn-disabled" id="donate">Doação!</button> <button class="btn btn-disabled" id="wallbreaker">Muralha Ariet</button> </div> </div> </div> ',
1530 settingsPopup: '<div id="settings_popup" class="popup_box_container"> <div class="popup_box show" id="popup_box_quest" style="width: 700px;"> <div class="popup_box_content"> <a class="popup_box_close close_settings_button" href="#"> </a> <div style="width: 700px"> <div style="background: no-repeat url(' + " '/graphic/paladin_new.png' " + ');"> <h3 style="margin: 0 3px 5px 120px;">Configurações globais</h3> <table align="right" style="margin-bottom: 5px;"> <tbody> <tr> <td class="quest-summary" style="width: 583px"> <p> Modifique várias configurações especÃficas do modelo aqui! Observe que usar este bot é compatÃvel e nenhuma combinação de configurações é permitida. Dito isto, a combinação que é menos provável suscitar suspeitas consiste em não verificar nenhuma das caixas (talvez exceto <em> Atacar jogadores </em>) na coluna Configurações gerais. Especialmente <em> Redefinir para primeiro </em> e <em> Aguarde tropas </em> são muito similares a bot. </P> <p> Com a configuração <em> Poll Late </em>, você pode atrasar a atualização da página depois que as tropas chegaram por um valor aleatório. Isto é para fazer parecer menos bot-like. </p> <p><strong>Relatorio farmer</strong></p> <p>Isso tenta imitar a funcionalidade do assistente de fazenda, mas melhor! Selecione se deseja reduzir o tempo e por qual fator. Isso significa que, se for longo o último ataque, assumimos que há menos recursos do que no caso perfeito. Por exemplo, com um fator de desconto de 1,07, assumimos que, após 11 horas, apenas aproximadamente metade dos recursos esperados estará realmente lá.</p> </td> </tr> </tbody> </table> <div class="quest-goal"> <table style="border:none;" id="settings_table"> <tbody> <thead style="font-weight:bold;"> <tr> <td colspan="2">Geral</td> <td colspan="2">Bot</td> <td colspan="2">Relatorios</td> </tr> </thead> <tr> <td colspan="1" title="Repor para a primeira vila quando fora das aldeias alvo"><label for="reset_to_first">Redefinir primeiro</label></td> <td colspan="1" title="Repor para a primeira vila quando fora das aldeias alvo"><input type="checkbox" id="reset_to_first" name="reset_to_first" /></td> <td colspan="1" title="Segundos para atualizar tarde"><label for="poll_late_seconds">Max segundos</label></td> <td colspan="1" title="Segundos para atualizar tarde"><input style="width: 20px;" maxlength="3" type="text" id="poll_late_seconds" name="poll_late_seconds"/></td> <td colspan="1" title="Habilite o localizador de relatórios!"><label for="report_farmer">Relatorio Finder</label></td> <td colspan="1" title="Habilite o localizador de relatórios!"><input type="checkbox" id="report_farmer" name="report_farmer" /></td> </tr> <tr> <td colspan="1" title="Aguarde tropas se não estiverem disponÃveis"><label for="wait_for_troops">Aguarde tropas</label></td> <td colspan="1" title="Aguarde tropas se não estiverem disponÃveis"><input type="checkbox" id="wait_for_troops" name="wait_for_troops" /></td> <td colspan="1" title="Ao esperar por tropas, atualize-se tarde demais à s vezes"><label for="poll_late">Poll late</label></td> <td colspan="1" title="Ao esperar por tropas, atualize-se tarde demais à s vezes"><input type="checkbox" id="poll_late" name="poll_late" /></td> <td colspan="1" title="Ao calcular os recursos esperados, o desconto para o tempo até o transporte"><label for="discount_time">Tempo de desconto</label></td> <td colspan="1" title="Ao calcular os recursos esperados, o desconto para o tempo até o transporte"><input type="checkbox" id="discount_time" name="discount_time" /></td> </tr> <tr> <td colspan="1" title="Atacar aldeias de jogadores quando cultivar"><label for="attack_players">Atacar players</label></td> <td colspan="1" title="Atacar aldeias de jogadores quando cultivar"><input type="checkbox" id="attack_players" name="attack_players" /></td> <td></td> <td></td> <td colspan="1" title="Fator de desconto (entre 1 e 2). Formato:1.07"><label for="discount_factor">Fator de desconto</label></td> <td colspan="1" title="Fator de desconto (entre 1 e 2). Formato: 1.07"><input style="width: 20px;" maxlength="4" type="text" id="discount_factor" name="discount_factor"/></td> </tr> <tr> <td colspan="1" title="Pare automaticamente depois de alguns minutos"><label for="autostop">Parar Automatico</label></td> <td colspan="1" title="Automatically stop after some minutes"><input type="checkbox" id="autostop" name="autostop" /></td> <td></td> <td></td> <td colspan="1" title="Tempo máxima de um relatório para leitura (h)"><label for="report_max_read_age">Tempo máxima de leitura</label></td> <td colspan="1" title="Tempo máxima de um relatório para leitura(h)"><input style="width: 20px;" maxlength="3" type="text" id="report_max_read_age" name="report_max_read_age"/></td> </tr> <tr> <td colspan="1" title="Minutos para exec antes de parar"><label for="autostop_minutes">Minutos para exec</label></td> <td colspan="1" title="Minutos para exec antes de parar"><input style="width: 20px;" maxlength="3" type="text" id="autostop_minutes" name="autostop_minutes"/></td> <td></td> <td></td> <td colspan="1" title="Tempo máxima de um relatório para cálculos (h)"><label for="report_max_use_age">Máxima de Tempo</label></td> <td colspan="1" title="Tempo máxima de um relatório para cálculos (h)"><input style="width: 20px;" maxlength="3" type="text" id="report_max_use_age" name="report_max_read_age"/></td> <tr> </tr> </tbody> </table> </div> </div> <div align="center" style="padding: 10px;"> <a class="btn close_settings_button" href="#">Fechar</a> <a class="btn" id="save_settings">Salvar e fechar</a> </div> </div> </div> </div> <div class="fader"></div> </div>'
1531 }
1532};
1533
1534twf.init();