// ==UserScript==
// @name Torn - Display Stats on Attack Screen
// @namespace https://www.torn.com/profiles.php?XID=2834135#/
// @version 1.0
// @description Get TornStats spies or personal stats information on the attack page.
// @author echotte [2834135]
// @match https://www.torn.com/loader.php?sid=attack*
// @grant GM_xmlhttpRequest
// @connect www.tornstats.com
// @license MIT
// ==/UserScript==
(function attack() {
'use strict';
let api = "d73NG0Ochg45Iy9V";
let url = window.location.href;
if(url.includes("sid=attack"))
{
url = new URL(url);
let attackId = url.searchParams.get("user2ID");
var colorgreen = "#98FB98";
var colorred = "#EE4B2B";
let bsCache = localStorage["finally.torn.bs"] !== undefined ? JSONparse(localStorage["finally.torn.bs"]) : {};
let stats = [0, 0, 0, 0, 0];
let units = ["K", "M", "B", "T", "Q"];
let enemystats = {};
var enemydata, companydata, spydata, owndata;
var jobstr, statstr, footerstr, ff;
Promise.all([
fetch(`https://api.torn.com/user/${attackId}?selections=profile,personalstats&key=${api}&comment=attack_stats`),
fetch(`https://api.torn.com/user/?selections=battlestats,profile,personalstats&key=${api}&comment=attack_stats`)
]).then(responses => {
return Promise.all(responses.map(response => {
return response.json();
}));
}).then(data => {
enemydata = data[0];
owndata = data[1];
if (attackId in bsCache) {
stats[0] = bsCache[attackId].total;
stats[1] = bsCache[attackId].strength;
stats[2] = bsCache[attackId].defense;
stats[3] = bsCache[attackId].speed;
stats[4] = bsCache[attackId].dexterity;
for (let i = 0; i < stats.length; i++) {
let stat = Number.parseInt(stats[i]);
if (Number.isNaN(stat) || stat == 0) continue;
let originalStat = stat;
for (let j = 0; j < units.length; j++) {
stat = stat / 1000;
if (stat > 1000) continue;
stat = stat.toFixed(i == 0 ? (stat >= 100 ? 0 : 1) : 2);
stats[i] = `${stat}${units[j]}`;
break;
}
}
ff = "ff" in bsCache[attackId] ? ff = bsCache[attackId].ff : 0;
// cache found, display stats from cache
statstr = (`<br />TOTAL STATS: <strong><font color='${bsCache[attackId].total > owndata.total ? colorred : colorgreen}'>${stats[0]}</font></strong><br />`) +
(bsCache[attackId].strength>0 ? `STR: <font color='${bsCache[attackId].strength > owndata.strength ? colorred : colorgreen}'>${stats[1]}</font><br />` : "") +
(bsCache[attackId].defense>0 ? `DEF: <font color='${bsCache[attackId].defense > owndata.defense ? colorred : colorgreen}'>${stats[2]}</font><br />` : "") +
(bsCache[attackId].speed>0 ? `SPD: <font color='${bsCache[attackId].speed > owndata.speed ? colorred : colorgreen}'>${stats[3]}</font><br />` : "") +
(bsCache[attackId].dexterity>0 ? `DEX: <font color='${bsCache[attackId].dexterity > owndata.dexterity ? colorred : colorgreen}'>${stats[4]}</font><br />` : "") +
`<br />Fair Fight: <strong><font color='${ff<2 ? colorred : colorgreen}'>${ff.toFixed(2)}</font></strong><br />`;
} else {
// no cache found, display personalstats
statstr = `<br />Xan: <b><font color='${owndata.personalstats.xantaken<enemydata.personalstats.xantaken ? colorred : colorgreen}'>${parseInt(enemydata.personalstats.xantaken) || 0}</font></b>
Refills: <strong><font color='${owndata.personalstats.refills<enemydata.personalstats.refills ? colorred : colorgreen}'>${parseInt(enemydata.personalstats.refills) || 0}</font></strong>
<br />Cans: <strong><font color='${owndata.personalstats.energydrinkused<enemydata.personalstats.energydrinkused ? colorred : colorgreen}'>${parseInt(enemydata.personalstats.energydrinkused) || 0}</font></strong>
SE: <strong><font color='${owndata.personalstats.statenhancersused<enemydata.personalstats.statenhancersused ? colorred : colorgreen}'>${parseInt(enemydata.personalstats.statenhancersused) || 0}</font></strong>
<br />`;
}
// assemble job and current status
jobstr = enemydata.job.company_type==0 ? enemydata.job.job : companies[enemydata.job.company_type];
footerstr = `<br />Last action: <strong>${enemydata.last_action.relative}</strong>
<br />
<br />Faction: <strong>${enemydata.faction.faction_name}</strong>
<br />Job: <strong>${jobstr}</strong>`;
addButton(statstr + footerstr);
// get stars of the company job
if (enemydata.job.company_type!=0) {
fetch(`https://api.torn.com/company/${enemydata.job.company_id}?selections=profile&key=${api}`)
.then(function (response) {
// Get a JSON object from the response
return response.json();
}).then(function (data) {
// Cache the data to a variable
companydata = data;
jobstr = `${companydata.company.rating}* ${companies[enemydata.job.company_type]}`;
footerstr = `<br />Last action: <strong>${enemydata.last_action.relative}</strong>
<br />
<br />Faction: <strong>${enemydata.faction.faction_name}</strong>
<br />Job: <strong>${jobstr}</strong>`;
updateText(statstr + footerstr);
});
}
// Call TornStats, update localSystem cache if results found
GM_xmlhttpRequest({
method: "GET",
url: `https://www.tornstats.com/api/v1/${api}/spy/${attackId}`,
onload: (r) => {
spydata = JSON.parse(r.responseText);
if (spydata.spy.status == true) {
enemystats = {
total : spydata.spy.total,
strength : spydata.spy.strength,
defense : spydata.spy.defense,
speed : spydata.spy.speed,
dexterity : spydata.spy.dexterity,
ff : spydata.spy.fair_fight_bonus,
timestamp : spydata.spy.timestamp,
};
bsCache[attackId] = enemystats;
stats[0] = spydata.spy.total;
stats[1] = spydata.spy.strength;
stats[2] = spydata.spy.defense;
stats[3] = spydata.spy.speed;
stats[4] = spydata.spy.dexterity;
for (let i = 0; i < stats.length; i++) {
let stat = Number.parseInt(stats[i]);
if (Number.isNaN(stat) || stat == 0) continue;
let originalStat = stat;
for (let j = 0; j < units.length; j++) {
stat = stat / 1000;
if (stat > 1000) continue;
stat = stat.toFixed(i == 0 ? (stat >= 100 ? 0 : 1) : 2);
stats[i] = `${stat}${units[j]}`;
break;
}
}
// new and updated battlestats
statstr = `<br /> TOTAL STATS: <strong><font color='${spydata.spy.deltaTotal<0 ? colorred : colorgreen}'>${stats[0]}</font></strong><br />` +
(stats[1]!="N/A" ? `STR: <font color='${spydata.spy.deltaStrength<0 ? colorred : colorgreen}'>${stats[1]}</font><br />` : "") +
(stats[2]!="N/A" ? `DEF: <font color='${spydata.spy.deltaDefense<0 ? colorred : colorgreen}'>${stats[2]}</font><br />` : "") +
(stats[3]!="N/A" ? `SPD: <font color='${spydata.spy.deltaSpeed<0 ? colorred : colorgreen}'>${stats[3]}</font><br />` : "") +
(stats[4]!="N/A" ? `DEX: <font color='${spydata.spy.deltaDexterity<0 ? colorred : colorgreen}'>${stats[4]}</font><br />` : "") +
`<br />Fair Fight: <strong><font color='${spydata.spy.fair_fight_bonus<2 ? colorred : colorgreen}'>${spydata.spy.fair_fight_bonus.toFixed(2)}</font></strong><br />`;
updateText(statstr + footerstr);
localStorage.setItem("finally.torn.bs", JSON.stringify(bsCache));
}
}
});
}).catch(function (error) {
// if there's an error, log it
alert(error);
console.log(error);
});
}
})();
function addButton(newmsg) {
let joinBtn = $("button:contains(\"Start fight\"), button:contains(\"Join fight\")").closest("button");
if($(joinBtn).length) {
$(joinBtn).after(`<div id='attackInfo'> ` + newmsg + `</div>`);
}
}
function updateText(newmsg) {
$("#attackInfo").html(newmsg);
}
function JSONparse(str) {
try {
return JSON.parse(str);
} catch (e) { }
return null;
}
const companies = {
1: "Hair Salon",
2: "Law Firm",
3: "Flower Shop",
4: "Car Dealership",
5: "Clothing Store",
6: "Gun Shop",
7: "Game Shop",
8: "Candle Shop",
9: "Toy Shop",
10: "Adult Novelties",
11: "Cyber Cafe",
12: "Grocery Store",
13: "Theater",
14: "Sweet Shop",
15: "Cruise Line",
16: "Television Network",
17: "",
18: "Zoo",
19: "Firework Stand",
20: "Property Broker",
21: "Furniture Store",
22: "Gas Station",
23: "Music Store",
24: "Nightclub",
25: "Pub",
26: "Gents Strip Club",
27: "Restaurant",
28: "Oil Rig",
29: "Fitness Center",
30: "Mechanic Shop",
31: "Amusement Park",
32: "Lingerie Store",
33: "Meat Warehouse",
34: "Farm",
35: "Software Corporation",
36: "Ladies Strip Club",
37: "Private Security Firm",
38: "Mining Corporation",
39: "Detective Agency",
40: "Logistics Management",
};