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