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