· 5 years ago · Feb 16, 2020, 07:30 PM
1 javascript:
2 //mass scavenging by Sophie "Shinko to Kuma"
3 //edit by To Infinity For Serenity
4
5 //relocate to mass scavenging page
6 if (window.location.href.indexOf('screen=place&mode=scavenge_mass') < 0) {
7 //relocate
8 window.location.assign(game_data.link_base_pure + "place&mode=scavenge_mass");
9 }
10 $("#massScavengeSophie").remove();
11 //set global variables
12
13 if (typeof troopTypeEnabled == 'undefined') {
14 var troopTypeEnabled = {
15 "spear": true,
16 "sword": true,
17 "axe": true,
18 "archer": true,
19 "scout": true,
20 "light": true,
21 "marcher": true,
22 "heavy": true,
23 "catapult": true,
24 "ram": true
25 };
26 }
27 var arrayWithData;
28 var enabledCategories=[];
29 var availableUnits = [];
30 var squad_requests = [];
31 var scavengeInfo;
32 var duration_factor = 0;
33 var duration_exponent = 0;
34 var duration_initial_seconds = 0;
35 var categoryNames= JSON.parse("["+$.find('script:contains("ScavengeMassScreen")')[0].innerHTML.match(/\{.*\:\{.*\:.*\}\}/g)+"]")[0];
36 //basic setting, to be safe
37 var time = 0;
38
39 //colors for UI
40 var backgroundColor = "#36393f";
41 var borderColor = "#3e4147";
42 var headerColor = "#202225";
43 var titleColor = "#ffffdf";
44
45 $.getAll = function (
46 urls, // array of URLs
47 onLoad, // called when any URL is loaded, params (index, data)
48 onDone, // called when all URLs successfully loaded, no params
49 onError // called when a URL load fails or if onLoad throws an exception, params (error)
50 ) {
51 var numDone = 0;
52 var lastRequestTime = 0;
53 var minWaitTime = 200; // ms between requests
54 loadNext();
55 function loadNext() {
56 if (numDone == urls.length) {
57 onDone();
58 return;
59 }
60
61 let now = Date.now();
62 let timeElapsed = now - lastRequestTime;
63 if (timeElapsed < minWaitTime) {
64 let timeRemaining = minWaitTime - timeElapsed;
65 setTimeout(loadNext, timeRemaining);
66 return;
67 }
68 console.log('Getting ', urls[numDone]);
69 $("#progress").css("width", `${(numDone + 1) / urls.length * 100}%`);
70 lastRequestTime = now;
71 $.get(urls[numDone])
72 .done((data) => {
73 try {
74 onLoad(numDone, data);
75 ++numDone;
76 loadNext();
77 } catch (e) {
78 onError(e);
79 }
80 })
81 .fail((xhr) => {
82 onError(xhr);
83 })
84 }
85 };
86
87 //get scavenging data that is in play for this world, every world has different exponent, factor, and initial seconds. Also getting the URLS of each mass scavenging page
88 //we can limit the amount of pages we need to call this way, since the mass scavenging pages have all the data that is necessary: troopcounts, which categories per village are unlocked, and if rally point exists.
89 function getData() {
90 $("#massScavengeSophie").remove();
91 URLs = [];
92 $.get("game.php?&screen=place&mode=scavenge_mass", function (data) {
93 for (var i = 0; i <= $(".paged-nav-item").length; i++) {
94 //push url that belongs to scavenging page i
95 URLs.push("game.php?&screen=place&mode=scavenge_mass&page=" + i);
96 //get world data
97 tempData = JSON.parse($(data).find('script:contains("ScavengeMassScreen")').html().match(/\{.*\:\{.*\:.*\}\}/g)[0]);
98 duration_exponent = tempData[1].duration_exponent;
99 duration_factor = tempData[1].duration_factor;
100 duration_initial_seconds = tempData[1].duration_initial_seconds;
101 }
102 console.log(URLs);
103
104 })
105 .done(function () {
106 //here we get all the village data and make an array with it, we won't be able to parse unless we add brackets before and after the string
107 arrayWithData = "[";
108 $.getAll(URLs,
109 (i, data) => {
110 thisPageData = $(data).find('script:contains("ScavengeMassScreen")').html().match(/\{.*\:\{.*\:.*\}\}/g)[2];
111 arrayWithData += thisPageData + ",";
112 },
113 () => {
114 //on done
115 arrayWithData = arrayWithData.substring(0, arrayWithData.length - 1);
116 //closing bracket so we can parse the data into a useable array
117 arrayWithData += "]";
118 console.log(arrayWithData);
119 scavengeInfo = JSON.parse(arrayWithData);
120 // count and calculate per village how many troops per category need to be sent.
121 // Once count is finished, make a new UI element, and group all the results per 200.
122 // According to morty, that is the limit at which the server will accept squad pushes.
123 count=0;
124 for (var i = 0; i < scavengeInfo.length; i++) {
125 calculateHaulCategories(scavengeInfo[i]);
126 count++;
127 }
128 if (count == scavengeInfo.length) {
129 //Post here
130 console.log("Done");
131 //need to split all the scavenging runs per 200, server limit according to morty
132 squads = {};
133 per200 = 0;
134 groupNumber = 0;
135 squads[groupNumber] = [];
136 for (var k = 0; k < squad_requests.length; k++) {
137 if (per200 == 200) {
138 groupNumber++;
139 squads[groupNumber] = [];
140 per200 = 0;
141 }
142 per200++;
143 squads[groupNumber].push(squad_requests[k]);
144 }
145
146 //create html send screen with button per launch
147 console.log("Creating launch options");
148 htmlWithLaunchButtons=`<div id="massScavengeFinal" class="ui-widget-content" style="position:fixed;background-color:${backgroundColor};cursor:move;z-index:50;">
149 <table id="massScavengeSophieFinalTable" class="vis" border="1" style="width: 100%;background-color:${backgroundColor};border-color:${borderColor}">
150 <tr>
151 <td colspan="10" id="massScavengeSophieTitle" style="text-align:center; width:auto; background-color:${headerColor}">
152 <h2>
153 <center style="margin:10px"><u>
154 <font color="${titleColor}">Mass scavenging: send per 50 villages</font>
155 </u>
156 </center>
157 </h2>
158 </td>
159 </tr>`;
160 for(var s=0;s<Object.keys(squads).length;s++)
161 {
162 //add row with new button
163 htmlWithLaunchButtons+=`<tr id="sendRow${s}" style="text-align:center; width:auto; background-color:${backgroundColor}"><td style="text-align:center; width:auto; background-color:${backgroundColor}"><center><input type="button" class="btn evt-confirm-btn btn-confirm-yes" id="sendMass" onclick="sendGroup(${s})" value="Launch group ${s+1}"></center></td></tr>`
164 }
165 htmlWithLaunchButtons+="</table></div>"
166 //appending to page
167 console.log("Creating launch UI");
168 $("#contentContainer").eq(0).prepend(htmlWithLaunchButtons);
169 $("#mobileContent").eq(0).prepend(htmlWithLaunchButtons);
170 $("#massScavengeFinal").draggable();
171 }
172 },
173 (error) => {
174 console.error(error);
175 });
176 }
177 )
178 }
179 //first UI, will always open as soon as you run the script.
180 html = `
181 <div id="massScavengeSophie" class="ui-widget-content" style="position:fixed;background-color:${backgroundColor};cursor:move;z-index:50;">
182 <table id="massScavengeSophieTable" class="vis" border="1" style="width: 100%;background-color:${backgroundColor};border-color:${borderColor}">
183 <tr>
184 <td colspan="10" id="massScavengeSophieTitle" style="text-align:center; width:auto; background-color:${headerColor}">
185 <h2>
186 <center style="margin:10px"><u>
187 <font color="${titleColor}">Mass scavenging</font>
188 </u>
189 </center>
190 </h2>
191 </td>
192 </tr>
193 <tr style="background-color:${backgroundColor}">
194 <td style="text-align:center;background-color:${headerColor}" colspan="15">
195 <h2>
196 <center style="margin:10px"><u>
197 <font color="${titleColor}">Select unit types to scavenge with</font>
198 </u></center>
199 </h2>
200 </td>
201 </tr>
202 <tr id="imgRow">
203 </tr>
204 <tr id="checkboxRow">
205 </tr>
206 </table>
207 <hr>
208 <table class="vis" border="1" style="width: 100%;background-color:${backgroundColor};border-color:${borderColor}">
209 <tbody>
210 <tr style="background-color:${backgroundColor}">
211 <td style="text-align:center;background-color:${headerColor}" colspan="4">
212 <h2>
213 <center style="margin:10px"><u>
214 <font color="${titleColor}">Select categories to use</font>
215 </u></center>
216 </h2>
217 </td>
218 </tr>
219 <tr id="categories" style="text-align:center; width:auto; background-color:${headerColor}">
220 <td style="text-align:center; width:auto; background-color:${headerColor};padding: 10px;"><font color="${titleColor}">${categoryNames[1].name}</font></td>
221 <td style="text-align:center; width:auto; background-color:${headerColor};padding: 10px;"><font color="${titleColor}">${categoryNames[2].name}</font></td>
222 <td style="text-align:center; width:auto; background-color:${headerColor};padding: 10px;"><font color="${titleColor}">${categoryNames[3].name}</font></td>
223 <td style="text-align:center; width:auto; background-color:${headerColor};padding: 10px;"><font color="${titleColor}">${categoryNames[4].name}</font></td>
224 </tr>
225 <tr>
226 <td style="text-align:center; width:auto; background-color:${backgroundColor}"><center><input type="checkbox" ID="category1" name="cat1" checked="checked"></center></td>
227 <td style="text-align:center; width:auto; background-color:${backgroundColor}"><center><input type="checkbox" ID="category2" name="cat2" checked="checked"></center></td>
228 <td style="text-align:center; width:auto; background-color:${backgroundColor}"><center><input type="checkbox" ID="category3" name="cat3" checked="checked"></center></td>
229 <td style="text-align:center; width:auto; background-color:${backgroundColor}"><center><input type="checkbox" ID="category4" name="cat4" checked="checked"></center></td>
230 </tr>
231 </tbody>
232 </table>
233 <hr>
234 <center>
235 <font color="${titleColor}">How long do you want to send the scavenging runs out for in HOURS? </font>
236 </center>
237 <br>
238 <center><textarea id="runTime" cols="12" style="background-color:${backgroundColor};color:${titleColor};resize:none;" placeholder="Runtime here"></textarea></center>
239 <hr>
240 <center><input type="button" class="btn evt-confirm-btn btn-confirm-yes" id="sendMass" onclick="readyToSend()" value="Calculate runtimes for each page"></center>
241 <hr>
242 /*<center><img class=" tooltip-delayed" title="Sophie -Shinko to Kuma-" src="https://dl.dropboxusercontent.com/s/0do4be4rzef4j30/sophie2.gif" style="cursor:help; position: relative"></center>*/
243 <br>
244 <center>
245 <p>
246 <font color="${titleColor}">Creator: </font><a href="https://forum.tribalwars.net/index.php?members/shinko-to-kuma.121220/" style="text-shadow:-1px -1px 0 ${titleColor},1px -1px 0 ${titleColor},-1px 1px 0 ${titleColor},1px 1px 0 ${titleColor};" title="Sophie profile" target="_blank">Sophie "Shinko to Kuma"</a>
247 </p>
248 </center>
249 </div>
250 `;
251 $("#contentContainer").eq(0).prepend(html);
252 $("#mobileContent").eq(0).prepend(html);
253 $("#massScavengeSophie").draggable();
254
255 //create checkboxes and add them to the UI
256 localUnitNames = [];
257 worldUnits = game_data.units;
258 for (var i = 0; i < worldUnits.length; i++) {
259 if (worldUnits[i] != "militia" && worldUnits[i] != "snob" && worldUnits[i] != "ram" && worldUnits[i] != "catapult" && worldUnits[i] != "spy") {
260 localUnitNames.push(worldUnits[i]);
261 }
262 }
263 for (var i = 0; i < localUnitNames.length; i++) {
264 $("#imgRow").eq(0).append(`<td style="text-align:center;background-color:${headerColor}" ><a href="#" class="unit_link" data-unit="${localUnitNames[i]}"><img src="https://dsen.innogamescdn.com/asset/cf2959e7/graphic/unit/unit_${localUnitNames[i]}.png" title="${localUnitNames[i]}" alt="" class=""></a></td>
265 `);
266 $("#checkboxRow").eq(0).append(`<td align="center" style="background-color:${backgroundColor}"><input type="checkbox" ID="${localUnitNames[i]}" name="${localUnitNames[i]}"></td>
267 `);
268 enableCorrectTroopTypes();
269 }
270
271 function readyToSend() {
272 //get trooptypes we wanna use, and runtime
273 worldUnits = game_data.units;
274 for (var i = 0; i < worldUnits.length; i++) {
275 if (worldUnits[i] != "militia" && worldUnits[i] != "snob" && worldUnits[i] != "ram" && worldUnits[i] != "catapult" && worldUnits[i] != "spy") {
276 troopTypeEnabled[worldUnits[i]] = $(`:checkbox#${worldUnits[i]}`).is(":checked");
277 }
278 }
279 enabledCategories.push($("#category1").is(":unchecked"));
280 enabledCategories.push($("#category2").is(":unchecked"));
281 enabledCategories.push($("#category3").is(":unchecked"));
282 enabledCategories.push($("#category4").is(":unchecked"));
283 time=$("#runTime")[0].value;
284 getData();
285 }
286
287 function sendGroup(groupNr)
288 {
289 //Send one group(one page worth of scavenging)
290 TribalWars.post('scavenge_api', { ajaxaction: 'send_squads' }, { "squad_requests": squads[groupNr] })
291 //once group is sent, remove the row from the table
292 $(`#sendRow${groupNr}`).remove();
293 }
294
295
296
297 function calculateHaulCategories(data) {
298 //check if village has rally point
299 if (data.has_rally_point == true) {
300 console.log("can scavenge");
301 var troopsAllowed = {};
302 for (key in troopTypeEnabled) {
303 if (troopTypeEnabled[key] == true) {
304 troopsAllowed[key] = data.unit_counts_home[key];
305 }
306 }
307
308 totalLoot = 0;
309
310 //check what the max possible loot is
311 for (key in troopsAllowed) {
312 if (key == "spear") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 25);
313 if (key == "sword") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 15);
314 if (key == "axe") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 10);
315 if (key == "archer") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 10);
316 if (key == "light") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 80);
317 if (key == "marcher") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 50);
318 if (key == "heavy") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 50);
319 if (key == "knight") totalLoot += troopsAllowed[key] * (data.unit_carry_factor * 100);
320 }
321 console.log("Loot possible from this village: " + totalLoot);
322 if (totalLoot == 0) {
323 //can't loot from here, end
324 return;
325 }
326
327 haul = parseInt(((time * 3600) / duration_factor - duration_initial_seconds) ** (1 / (duration_exponent)) / 100) ** (1 / 2);
328 haulCategoryRate = {};
329 //check which categories are enabled
330
331
332 if (data.options[1].is_locked == true) {
333 haulCategoryRate[1] = 0;
334 } else {
335 haulCategoryRate[1] = haul / 0.1;
336 }
337 if (data.options[2].is_locked == true) {
338 haulCategoryRate[2] = 0;
339 } else {
340 haulCategoryRate[2] = haul / 0.25;
341 }
342 if (data.options[3].is_locked == true) {
343 haulCategoryRate[3] = 0;
344 } else {
345 haulCategoryRate[3] = haul / 0.50;
346 }
347 if (data.options[4].is_locked == true) {
348 haulCategoryRate[4] = 0;
349 } else {
350 haulCategoryRate[4] = haul / 0.75;
351 }
352 console.log(haulCategoryRate);
353
354 for(var i=0;i<enabledCategories.length;i++)
355 {
356 if(enabledCategories[i]==false) haulCategoryRate[i+1]=0;
357 }
358
359
360 totalHaul = haulCategoryRate[1] + haulCategoryRate[2] + haulCategoryRate[3] + haulCategoryRate[4];
361
362
363 //calculate HERE :D
364 unitsReadyForSend = {};
365 unitsReadyForSend[0] = {};
366 unitsReadyForSend[1] = {};
367 unitsReadyForSend[2] = {};
368 unitsReadyForSend[3] = {};
369 if (totalLoot > totalHaul) {
370 //not enough units, just fill in everything
371 for (var j = 0; j < 4; j++) {
372 for (key in troopsAllowed) {
373 unitsReadyForSend[j][key] = Math.floor((haulCategoryRate[j+1] * (troopsAllowed[key] / totalLoot)));
374 }
375 }
376
377 }
378 else {
379 //too many units, fill till it reaches time limit predetermined
380 for (var j = 0; j < 4; j++) {
381 for (key in troopsAllowed) {
382 unitsReadyForSend[j][key] = Math.floor((totalLoot / totalHaul * haulCategoryRate[j+1]) * (troopsAllowed[key] / totalLoot));
383 }
384 }
385 }
386 for (var k = 0; k < Object.keys(unitsReadyForSend).length; k++) {
387 candidate_squad = { "unit_counts": unitsReadyForSend[k], "carry_max": 9999999999 };
388 squad_requests.push({ "village_id": data.village_id, "candidate_squad": candidate_squad, "option_id": k + 1, "use_premium": false })
389 }
390 }
391 else {
392 console.log("no rally point");
393 }
394 }
395
396 function enableCorrectTroopTypes()
397 {
398 worldUnits = game_data.units;
399 for (var i = 0; i < worldUnits.length; i++) {
400
401 if (worldUnits[i] != "militia" && worldUnits[i] != "snob" && worldUnits[i] != "ram" && worldUnits[i] != "catapult" && worldUnits[i] != "spy") {
402 if(troopTypeEnabled[worldUnits[i]]==true) $(`#${worldUnits[i]}`).prop( "checked", true );
403 }
404 }
405 }
406
407 /* This is some notes just for me so I know what I'm working with data wise
408
409 Structure of the array:
410 scavengInfo[i].
411
412 village_id
413 player_id
414 village_name
415 res :{wood,stone,iron}
416 res_rate:{wood,stone,iron}
417 storage_max
418 unit_counts_home:{spear,sword, etc}
419 unit_carry_factor
420 has_rally_point (true or false)
421
422 options[1]
423 base_id: 1
424 village_id
425 is_locked: (true or false)
426 unlock_time: null
427 scavenging_squad: null
428 */