· 8 years ago · Mar 23, 2018, 11:42 AM
1// ==UserScript==
2// @name Talibri - Pirion's Combat Stat Tracker
3// @namespace http://talibri.pirion.net/
4// @version 0.2
5// @description This is used to get a list of inventory items.
6// @author Kaine "Pirion" Adams (0.1) & Eph
7// @match https://talibri.com/*
8// @include https://talibri.com/*
9// @grant none
10// ==/UserScript==
11
12var combat = {
13 data: null,
14 is_stopped: false,
15 actionDictionary: { skills: [
16 {name: "Stab",successful: true, text: "You lunged at the enemy stabbing them"},
17 {name: "Stab",successful: false, text: "You attempted to use Stab"},
18 {name: "Shock Strike",successful: true, text: "You channel lightning energy into your blade"},
19 {name: "Shock Strike",successful: false, text: "You attempted to use Shock Strike"},
20 {name: "Lightning Defense",successful: true, text: "Lightning courses through your body"},
21 {name: "Lightning Defense",successful: false, text: "You attempted to use Lightning Defense"},
22 {name: "Dash",successful: true, text: "You dash into the enemy's defenses"},
23 {name: "Bash",successful: true, text: "You bash the enemy"},
24 {name: "Blade Twist",successful: true, text: "Your blade penetrates the enemy"},
25 {name: "Blade Twist",successful: false, text: "You attempted to use Blade Twist but failed"},
26 {name: "Bash",successful: false, text: "You attempted to use Bash"},
27 {name: "ShieldBash",successful: true, text: "You bashed the enemy with your shield"},
28 {name: "Shout",successful: true, text: "You shout at the enemy building your adrenaline"},
29 {name: "Wound",successful: true, text: "You strike at the enemy in an attempt to wound them"},
30 {name: "Wound",successful: false, text: "You attempted to use Wound but failed"},
31 {name: "Fiery Strike",successful: true, text: "You cover your weapon in oil and light it ablaze before striking the enemy"},
32 {name: "Fiery Strike",successful: false, text: "You attempted to use Fiery Strike"},
33 {name: "Aimed Shot",successful: true, text: "You line up the shot and let your arrow fly"},
34 {name: "Aimed Shot",successful: false, text: "You attempted to use Aimed Shot"},
35 {name: "Rapid Shot",successful: true, text: "One of your arrows launched in quick succession"},
36 {name: "Rapid Shot",successful: false, text: "You attempted to use Rapid Shot"},
37 {name: "Wing Clip",successful: true, text: "Your shot pinned the enemy"},
38 {name: "Wing Clip",successful: false, text: "You attempted to use Wing Clip"},
39 {name: "Poison Shot",successful: true, text: "Your poison tipped arrow"},
40 {name: "Ignite",successful: true, text: "You ignite your enemy"},
41 {name: "Ignite",successful: false, text: "You attempted to use Ignite"},
42 {name: "Freeze",successful: true, text: "You freeze your enemy"},
43 {name: "Freeze",successful: false, text: "You attempted to use Freeze"},
44 {name: "Electrify",successful: true, text: "You Electrify your enemy"},
45 {name: "Electrify",successful: false, text: "You attempted to use Electrify"},
46 {name: "Earth Eruption",successful: true, text: "The Earth Erupts under your enemy"},
47 {name: "Earth Eruption",successful: false, text: "You attempted to use Earth Eruption"},
48 {name: "Antipode Blast",successful: true, text: "The fire of your antipode"},
49 {name: "Antipode Blast",successful: false, text: "You attempted to use Antipode Blast"},
50 {name: "Item Failed",successful: false, text: "You are out of"},
51 ]},
52 cookiePath: function() {
53 return "/";
54 },
55 cookieName: function() {
56 return "/scripts/pirion/combat/data";
57 },
58 cookieExpirationDays: function() {
59 return 7;
60 },
61
62 initialize: function() {
63 //add jquery.cookie.js:
64 $('head').append('<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>');
65 $('head').append('<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>');
66 $('head').append('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" />');
67 $('head').append('<script type="text/javascript">var script = script || {};</script>');
68
69 // stop combat function
70 if (!window.stopAdventureCallbacks) {
71 window.stopAdventureCallbacks = [stopAdventure];
72 stopAdventure = function() {
73 for (let i = window.stopAdventureCallbacks.length; i--; i >= 0) {
74 window.stopAdventureCallbacks[i]();
75 }
76 }
77 }
78 let stopAdventureCallback = function() {
79 combat.data.flee += 1;
80 combat.data.rounds += 1;
81 };
82 if (window.stopAdventureCallbacks.indexOf(stopAdventureCallback) === -1) {
83 window.stopAdventureCallbacks.push(stopAdventureCallback);
84 }
85
86 window.setTimeout(combat.initalizeUserInterface, 2000);
87
88 combat.start();
89 },
90 initalizeUserInterface: function() {
91 var html = "";
92 html += '<div style="width: 80%; margin-left: auto; margin-right: auto;">';
93 html += '<div style="width: 80px; margin-left: auto; margin-right: auto;">';
94 html += '<a style="width: 80px;" onclick="javascript: script.reset=true;return false;" href="#">Reset Stats</a></div>';
95 html += '<table style="width: 80%; margin-left: auto; margin-right: auto;">';
96 html += '<tr><th>Actions</th><td id="combat_actions">0</td></tr>';
97 html += '<tr><th>Fights (W/L/F)</th><td id="fights">0</td></tr>';
98 html += '<tbody><tr><th>Accuracy</th><td id="combat_accuracy">0/0</td></tr>';
99 html += '<tr><th>Exp Gained</th><td id="combat_exp">0</td></tr>';
100 html += '<tr><th>Leol Gained</th><td id="combat_leol">0</td></tr>';
101 html += '<tr><th>Exp/Tick</th><td id="combat_exp_tick">0</td></tr>';
102 html += '<tr><th>Leol/Tick</th><td id="combat_leol_tick">0</td></tr>';
103 html += '<tr><th>Sec/Tick</th><td id="combat_second_tick">0</td></tr>';
104 // html += '<tr><th>Exp/Hour</th><td id="combat_exp_hour">0.00</td></tr>';
105 // html += '<tr><th>Leol/Hour</th><td id="combat_leol_hour">0.00</td></tr>';
106 combat.actionDictionary.skills.map(skill => skill.name).filter((v, i, a) => a.indexOf(v) === i).map(skill => {
107 html += `<tr style="display: none;"><th>${skill} Accuracy</th><td id="combat_${skill.replace(" ", "_")}_accuracy">0/0</td></tr>`;
108 });
109 html += '</tbody></table></div>';
110
111 if(!combat.data) {
112 combat.data = combat.load();
113 //if we can't check the cookie,
114 //let's skip this round so we don't overwrite it.
115 if(!combat.data) {
116 return;
117 }
118 }
119
120 var attachTracker = function() {
121 if(!$('#combatTrackerDialog').length && window.location.href.indexOf("combat_zones") > -1) {
122 $('body').append('<div id="combatTrackerDialog" title="Combat Tracking">' + html + '</div>');
123 $("#combatTrackerDialog").dialog();
124 }
125 combat.updateUI();
126 };
127 setInterval(attachTracker, 1000);
128 },
129 stop: function() {
130 combat.is_stopped = true;
131 },
132 start: function() {
133 //add a listener for any ajax page completed
134 $(document).ajaxComplete(combat.listen);
135 combat.is_stopped = false;
136 },
137 destory: function() {
138 //why would you ever want to do this?
139 combat.stop();
140 $.removeCookie(combat.cookieName(), {path: combat.cookiePath()});
141 },
142 reset: function() {
143 //overwrite data, and save
144 script.reset = false;
145 combat.data = combat.new();
146 combat.save();
147
148 combat.actionDictionary.skills.map(skill => skill.name).filter((v, i, a) => a.indexOf(v) === i).map(skill => {
149 $(`#combat_${skill.replace(" ", "_")}_accuracy`).parent().hide();
150 });
151
152 combat.updateUI();
153 },
154 new: function() {
155 //generate an empty json array
156 var empty_data = {
157 version: 2,
158 update: (new Date()),
159 since: (new Date()),
160 rounds: 0,
161 win: 0,
162 loss: 0,
163 flee: 0,
164 inCombat: false,
165 leol: 0,
166 actions: {},
167 items: {},
168 monsters: {},
169 stats: {},
170 affinities: {},
171 loot: {}
172 };
173 return empty_data;
174 },
175 load: function() {
176 //if cookie exists, load, otherwise get a new array.
177 var cookie = $.cookie(combat.cookieName());
178 if(cookie) {
179 var loaded_data = $.parseJSON(cookie);
180 return combat.migrate(loaded_data);
181 }
182 return combat.new();
183 },
184 migrate: function(migration_data) {
185 //if we load, we need to make sure to reset the cookie to a good state.
186 if(migration_data.version == 1) {
187 //bug caused leol to fail. this will set it to zero.
188 migration_data.version = 2;
189 migration_data.leol = 0;
190 }
191 //if we are in combat, lets count as a flee:
192 if(migration_data.inCombat == true) {
193 migration_data.inCombat = false;
194 migration_data.flee += 1;
195 }
196 return migration_data;
197 },
198 save: function() {
199 //create cookie that expires in 7 days:
200 $.cookie(combat.cookieName(), JSON.stringify(combat.data), { path: combat.cookiePath(), expires: combat.cookieExpirationDays() });
201 },
202 listen: function(event, xhr, settings) {
203 //if we've been asked to exit, let's unbind:
204 if(combat.is_stopped) {
205 $(e.currentTarget).unbind('ajaxComplete');
206 return;
207 }
208
209 //if ajax response comes from expected location:
210 if(settings.url.indexOf('adventure/continue') > -1) {
211 if(script && script.reset) {
212 combat.reset();
213 }
214 //if data has not been initialized, initialize it.
215 if(!combat.data) {
216 combat.data = combat.load();
217 //if we can't check the cookie,
218 //let's skip this round so we don't overwrite it.
219 if(!combat.data) {
220 return;
221 }
222 }
223
224 if (xhr.status >= 200 && xhr.status < 300) {
225 var result = combat.getResult(xhr.responseText);
226 combat.logBattleRound(result);
227 }
228 }
229 },
230 getResult: function(response) {
231 var result = {
232 player: {
233 slain: false
234 },
235 monster: {
236 name: "Unknown",
237 slain: false
238 },
239 leol: 0,
240 action: {
241 type: "Nothing",
242 name: "Unknown",
243 successful: false
244 },
245 stats: {},
246 affinities: {},
247 loot: {}
248 };
249
250 if(response.indexOf('You limped back to town on the verge of death') > -1) {
251 result.player.slain = true;
252 }
253
254 responseLines = response.split(";");
255
256 for(var i = 0; i < responseLines.length; i++) {
257 var item = responseLines[i].toLowerCase();
258 if(item.indexOf("$('button:contains(\"") > -1) {
259 var userItem = item.substr(item.indexOf("$('button:contains(\"")+20);
260 result.action.type = "Item";
261 result.action.name = userItem.substr(0,userItem.indexOf(" ("));
262 result.action.successful = true;
263
264 } else if(item.indexOf("$combat_round.append") > -1 &&
265 item.indexOf("you") > -1)
266 {
267 var combatText = item.split("\"")[1];
268
269 if(combatText.indexOf("experience.") > -1) {
270 var parts = combatText.split(" ");
271 result.affinities[parts[3]] = parseInt(parts[2]);
272 } else if(combatText.indexOf("leol.") > -1) {
273 var parts = combatText.split(" ");
274 result.leol = parseInt(parts[2]);
275 } else if(combatText.indexOf("experience,") > -1) {
276 var parts = combatText
277 .replace("you gained ","")
278 .split(", ");
279 for(var j = 0; j < parts.length; j++){
280 var experienceString = parts[j].split(" ");
281 result.stats[experienceString[1]] = parseInt(experienceString[0]);
282 }
283 result.leol = parseInt(parts[2]);
284 } else if(combatText.indexOf("you killed the ") > -1) {
285 result.monster.name = combatText.replace("you killed the ","").replace("! <br/>","");
286 result.monster.slain = true;
287 } else if(combatText.indexOf(". you now have ") > -1) {
288 var itemStringParts = combatText.substr(11, combatText.indexOf("(")-11).split(" ");
289 var itemString = "";
290 for(var k = 1; k < itemStringParts.length; k++){
291 itemString += (itemString == "" ? "" : " ") + itemStringParts[k];
292 }
293 result.loot[itemString] = parseInt(itemStringParts[0]);
294 } else {
295 if(result.action.name == "Unknown") {
296 for(var l = 0; l < combat.actionDictionary.skills.length; l++) {
297 if(combatText.startsWith(combat.actionDictionary.skills[l].text.toLowerCase())) {
298 result.action = combat.actionDictionary.skills[l];
299 result.action.type = "Skill";
300 }
301 }
302 }
303 }
304
305 }
306 }
307
308 return result;
309 },
310 logBattleRound: function(result){
311 //update the data array:
312 combat.data.update = new Date();
313 combat.data.rounds += 1;
314 combat.data.leol += result.leol;
315
316 if(result.player.slain) {
317 combat.data.loss += 1;
318 }
319
320 //create action stub if not exists:
321 if(result.action.type=="Skill") {
322 if(!combat.data.actions[result.action.name]) {
323 combat.data.actions[result.action.name] = {successful: 0, unsuccessful: 0};
324 }
325
326 //count action:
327 if(result.action.successful) {
328 combat.data.actions[result.action.name].successful += 1;
329 } else {
330 combat.data.actions[result.action.name].unsuccessful += 1;
331 }
332 } else if(result.action.type=="Item") {
333 if(!combat.data.items[result.action.name]) {
334 combat.data.items[result.action.name] = {count: 1};
335 } else {
336 combat.data.items[result.action.name].count += 1;
337 }
338 }
339 //if monster was slain, count:
340 if(result.monster.slain) {
341 if(!combat.data.monsters[result.monster.name]) {
342 combat.data.monsters[result.monster.name] = 1;
343 } else {
344 combat.data.monsters[result.monster.name] += 1;
345 }
346 combat.data.inCombat = false;
347 combat.data.win += 1;
348 } else {
349 combat.data.inCombat = true;
350 }
351
352 for(var stat in result.stats)
353 {
354 if(!combat.data.stats[stat]) {
355 combat.data.stats[stat] = result.stats[stat];
356 } else {
357 combat.data.stats[stat] += result.stats[stat];
358 }
359 }
360
361 for(var affinity in result.affinities)
362 {
363 if(!combat.data.affinities[affinity]) {
364 combat.data.affinities[affinity] = result.affinities[affinity];
365 } else {
366 combat.data.affinities[affinity] += result.affinities[affinity];
367 }
368 }
369
370 for(var item in result.loot)
371 {
372 if(!combat.data.loot[item]) {
373 combat.data.loot[item] = result.loot[item];
374 } else {
375 combat.data.loot[item] += result.loot[item];
376 }
377 }
378
379 //make changes to the cookie
380 combat.save();
381 //make an update to the visible UI
382 combat.updateUI();
383 },
384 fleeBehavior: function() {
385 combat.data.flee += 1;
386 },
387 updateUI: function() {
388 var fleeButton = $(".btn.btn-large.btn-danger").get(0);
389 if (fleeButton) {
390 fleeButton.addEventListener("click", combat.fleeBehavior, false);
391 }
392
393 var ui = $('#combatTrackerDialog')[0];
394 if(!ui) {
395 return;
396 }
397 var runtime = (new Date(combat.data.update) - new Date(combat.data.since))/(3600000);
398 var successRate = (combat.data.win) / ((combat.data.win + combat.data.loss + combat.data.flee) || 1);
399 $("#fights")[0].innerText = `${combat.data.win} | ${combat.data.loss} | ${combat.data.flee} - ${Math.round(successRate * 100)}%`;
400 var success = 0;
401 var failure = 0;
402 for(var action in combat.data.actions) {
403 var cur_success = combat.data.actions[action].successful;
404 var cur_failure = combat.data.actions[action].unsuccessful;
405 success += cur_success;
406 failure += cur_failure;
407 if (cur_success+cur_failure) {
408 $(`#combat_${action.replace(" ", "_")}_accuracy`)[0].innerText = cur_success.toString() + ' of ' + (cur_success+cur_failure).toString() + ' ('+ (Math.round(cur_success/(cur_success+cur_failure)*10000)/100).toString() + '%)';
409 $(`#combat_${action.replace(" ", "_")}_accuracy`).parent().show();
410 } else {
411 $(`#combat_${action.replace(" ", "_")}_accuracy`).parent().hide();
412 }
413 }
414 $("#combat_accuracy")[0].innerText = success.toString() + ' of ' + (success+failure).toString() + ' ('+ (Math.round(success/(success+failure)*10000)/100).toString() + '%)';
415 $("#combat_actions")[0].innerText = combat.data.rounds.toString();
416 var exp_all = 0;
417 for(var stat in combat.data.stats) {
418 exp_all += combat.data.stats[stat];
419 }
420 $("#combat_exp")[0].innerText = exp_all.toString();
421 $("#combat_leol")[0].innerText = combat.data.leol.toString();
422 $("#combat_exp_tick")[0].innerText = (Math.round(exp_all / combat.data.rounds)).toString();
423 $("#combat_leol_tick")[0].innerText = (Math.round(combat.data.leol / combat.data.rounds)).toString();
424 $("#combat_second_tick")[0].innerText = ((new Date() - new Date(combat.data.since)) / (1000 * combat.data.rounds)).toFixed(3);
425 // $("#combat_exp_hour")[0].innerText = (Math.round(exp_all/runtime*100)/100).toString();
426 // $("#combat_leol_hour")[0].innerText = (Math.round(combat.data.leol/runtime*100)/100).toString();
427 },
428 getString: function() {
429 return JSON.stringify(combat.data);
430 },
431 log: function() {
432 console.log(combat.getString());
433 }
434};
435
436//start the script:
437combat.initialize();