NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Npc times
// @namespace http://tampermonkey.net/
// @version 1.7.4
// @description try to take over the world!
// @author Ahab [1735214]
// @include *www.torn.com/*
// @require https://raw.githubusercontent.com/lostandconfused/test/master/count.js
// @require https://gist.githubusercontent.com/BrockA/2625891/raw/9c97aa67ff9c5d56be34a55ad6c18a314e5eb548/waitForKeyElements.js
// @require https://raw.githubusercontent.com/craftpip/jquery-confirm/master/dist/jquery-confirm.min.js
// @grant GM_addStyle
// @grant GM.xmlHttpRequest
// @updateURL https://openuserjs.org/meta/Ahab/Npc_times.meta.js
// @license MIT
// ==/UserScript==
var data = {}
var settings = {}
if(localStorage.lootTimes === undefined){
localStorage.lootTimes = JSON.stringify({})
}
if(localStorage.lootSettings === undefined){
localStorage.lootSettings = JSON.stringify({'toLoot':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'soundAlert':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'min':2,'sec':40,'lootLevel':["4","5"],'showAll':0,'allLevels':0,'audioAlert':1,'timesAttack':1,'audio':'https://www.torn.com/casino/keno/audio/numberwin1.ogg','update':0,'lootState':1,'apiKey':"Add key"})
settings = JSON.parse(localStorage.lootSettings)
data = {}
}
else{
data = JSON.parse(localStorage.lootTimes);
settings = JSON.parse(localStorage.lootSettings)
}
var loots = ['loot_1','loot_2','loot_3','loot_4','loot_5']
var toLootback = []
var sub = 0
var audioPlayer = document.createElement('audio');
audioPlayer.src = JSON.parse(localStorage.lootSettings)['audio']
audioPlayer.preload = 'auto';
GM_addStyle(`
#npcs{
border-radius: 0 0 5px 5px;
line-height: 16px;
margin-bottom: 10px;
}
#npcs a{
color: var(--default-blue-color);
}
#npcs td{
text-align: center;
line-height: 12px;
flex: 1;
padding: 0 9px;
color: #444;
color: var(--activity-log-table-text-color);
font-size: 12px;
height: 25px;
vertical-align: middle;
border-bottom: 0px !important;
}
#npcs tr{
border-bottom: 0px !important;
}
.svg-icon{
height: 16px;
float: right;
margin: 7px;
padding-right: 8px;
}
.svg-icon path,
.svg-icon polygon,
.svg-icon rect{
fill: #ffffff;
}
#error{
border-radius: 0 0 5px 5px;
line-height: 16px;
margin-bottom: 10px;
padding: 9px;
text-align: center;
}
.vInput::placeholder {
padding: 5px;
min-width: 15ch;
max-width: 55ch;
}
.vInput[type="text"] {
box-sizing: border-box;
padding-left: 5px;
min-width: 2ch;
max-width: 55ch;
}
.vCheckbox{
margin-left:5px;
}
.vLabel{
vertical-align:text-top;
}
.vInput{
width:25%;
margin-left:5px;
border-radius:5px;
height:22px;
}
.vButton{
margin:5px;
background: transparent linear-gradient(180deg,#CCCCCC 0%,#999999 60%,#666666 100%) 0 0 no-repeat;
box-sizing: border-box;
border-radius: 5px;
font-weight: 700;
color: #333;
text-shadow: 0 1px 0 #ffffff66;
padding: 4px 8px;
font-size: 14px;
cursor: pointer;
}
.vButton:hover{
background: transparent linear-gradient(180deg,#E5E5E5 0%,#BBBBBB 60%,#999999 100%) 0 0 no-repeat;
color: #333;
}
.vCon{
padding: 5px;
}
.vSet{
background-color: #f2f2f2;
border-radius: 0 0 5px 5px;
}
body[class*=jconfirm-no-scroll-] {
overflow: hidden !important;
}
.jconfirm {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 99999999;
font-family: inherit;
overflow: hidden;
}
.jconfirm .jconfirm-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-transition: opacity .4s;
transition: opacity .4s;
}
.jconfirm .jconfirm-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-transition: opacity .4s;
transition: opacity .4s;
}
.jconfirm .jconfirm-scrollpane {
-webkit-perspective: 500px;
perspective: 500px;
-webkit-perspective-origin: center;
perspective-origin: center;
width: 100%;
height: 100%;
}
.jconfirm .jconfirm-row {
width: 100%;
}
.jconfirm .jconfirm-cell {
display: flex;
justify-content: space-around;
vertical-align: middle;
}
.jconfirm .jconfirm-holder {
max-height: 100%;
padding: 50px 0;
}
.jconfirm .jconfirm-box-container {
-webkit-transition: -webkit-transform;
transition: -webkit-transform;
transition: transform;
transition: transform, -webkit-transform;
}
.jconfirm .jconfirm-box {
background: white;
border-radius: 4px;
position: relative;
outline: none;
padding: 15px 15px 0;
overflow: hidden;
margin-left: auto;
margin-right: auto;
}
.jconfirm .jconfirm-box .jconfirm-buttons button {
display: inline-block;
padding: 6px 12px;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border-radius: 4px;
min-height: 1em;
-webkit-transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
-webkit-tap-highlight-color: transparent;
border: none;
background-image: none;
}
.jconfirm .jconfirm-box .jconfirm-buttons {
padding-bottom: 11px;
text-align: center;
}
.jconfirm .jconfirm-box .jconfirm-buttons > button {
margin-bottom: 4px;
margin-left: 2px;
margin-right: 2px;
}
.jconfirm .jconfirm-box .jconfirm-buttons button.btn-default {
background-color: #ecf0f1;
color: #000;
text-shadow: none;
-webkit-transition: background .2s;
transition: background .2s;
}
.jconfirm .jconfirm-box .jconfirm-buttons button.btn-default:hover {
background-color: #bdc3c7;
color: #000;
}
.jconfirm-content {
text-align: center;
font-size: 15px;
color: #777;
padding-bottom: 10px;
}
.jc-bs3-container{
width: max-content !important;
}
.jconfirm-bg {
background-color: #444;
opacity: .2;
}
`)
function getLoot(){
if(sub == 0){
GM.xmlHttpRequest({
method: "GET",
headers: {'Cache-Control': 'no-cache'},
url: "https://www.tornstats.com/api/v1/"+JSON.parse(localStorage.lootSettings)['apiKey']+"/loot",
onload: function(response){
if(JSON.parse(response.responseText).status == false){
$($('div[class^="content-wrapper"]')[0]).prepend('<div id="timesCont" style="width:'+$('div[class^="content-wrapper"]').css( "width" )+'; margin: 0 auto;"><div id="timesTitle" class="title-black top-round">NPC Loot Times<span id="state"><svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 20 32"><path d="M16 10l-6 6-6-6-4 4 10 10 10-10-4-4z"></path></svg></span></div><div id="error" class="cont-gray">'+JSON.parse(response.responseText).message+'</div><hr id="addedd" class="page-head-delimiter m-top10 m-bottom10"></div>')
}else{
settings['update'] = Date.now()
localStorage.lootSettings = JSON.stringify(settings)
data = JSON.parse(response.responseText);
$.each(data, function(npc){
if(!isNaN(npc)){
delete Object.assign(data[npc], {['loot_1']: data[npc]['hosp_out'] })['hosp_out'];
}
})
localStorage.lootTimes = JSON.stringify(data)
$('div[id^="timesCont"]').remove()
cd()
}
}
})
}
}
function disp(toDisp){
$.each(toDisp, function(index, npc){
var id = 0
var cdt = ""
$('table[id^="npcs"]')[0].firstChild.insertAdjacentHTML("beforeend",'<tr id="'+data[npc]['name']+'" class="cont-gray"><td class="cont-gray"><a href="https://www.torn.com/profiles.php?XID='+data[npc]['torn_id']+'">'+data[npc]['name']+'</a></td>')
$.each(loots, function(index, lootLvl){
if(JSON.parse(localStorage.lootSettings)['allLevels'] == 0){
if(JSON.parse(localStorage.lootSettings)['lootLevel'].includes(lootLvl.split('_')[1])){
id = lootLvl.split('_')[1]
cdt = new Date(data[npc][lootLvl]*1000).toLocaleString(Intl.DateTimeFormat().resolvedOptions().locale, {timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone})
$('tr[id^="'+data[npc]['name']+'"]')[0].insertAdjacentHTML("beforeend",'<td data-level="'+id+'">Loot '+lootLvl.split('_')[1]+': <div data-countdown="'+cdt+'" style="display: inline;"></div></td> ')
}
}
else if(JSON.parse(localStorage.lootSettings)['allLevels'] == 1){
id = lootLvl.split('_')[1]
cdt = new Date(data[npc][lootLvl]*1000).toLocaleString(Intl.DateTimeFormat().resolvedOptions().locale, {timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone})
$('tr[id^="'+data[npc]['name']+'"]')[0].insertAdjacentHTML("beforeend",'<td data-level="'+id+'">Loot '+lootLvl.split('_')[1]+': <div data-countdown="'+cdt+'" style="display: inline;"></div></td> ')
}
})
})
}
function cd(){
$($('div[class^="content-wrapper"]')[0]).prepend('<div id="timesCont" style="width:'+$('div[class^="content-wrapper"]').css( "width" )+'; margin: 0 auto;"><div id="timesTitle" class="title-black top-round">NPC Loot Times<span id="state"><svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 20 32"><path d="M16 10l-6 6-6-6-4 4 10 10 10-10-4-4z"></path></svg></span></div><table id="npcs" class="cont-gray" style="width:100%"><tbody></tbody></table><hr id="addedd" class="page-head-delimiter m-top10 m-bottom10"></div>')
var toDisp = []
if(window.location.href.split("/")[4] == 'mymaps'){
$('div[id*="timesCont"]').css({'float': 'right'})
}
if(window.location.href.split("/")[4] == 'mapeditor'){
$('div[id*="timesCont"]').css({'float': ''})
}
$.each(data, function(npc){
if(JSON.parse(localStorage.lootSettings)['showAll'] == 0){
if(!isNaN(npc) && JSON.parse(localStorage.lootSettings)['toLoot'].includes(data[npc]['name'])){
toDisp.push(npc)
}
}
else if(!isNaN(npc)){
toDisp.push(npc)
}
})
disp(toDisp)
$('[data-countdown]').each(function() {
var $this = $(this), finalDate = $(this).data('countdown');
$this.countd(finalDate, function(event) {
$this.html(event.strftime('%H:%M:%S'));
}).on('finish.countd', function() {
$(this)[0].parentElement.remove()
$($('table[id^="npcs"]')[0].children[0].childNodes).each(function() {
if($(this)[0].children.length == 1){
$('tr[id^="'+$(this)[0].id+'"]')[0].insertAdjacentHTML("beforeend",'<td>Loot 5 Ready</td>')
}
})
}).on('update.countd', function(event) {
if(event.offset.seconds == JSON.parse(localStorage.lootSettings)['sec'] && event.offset.minutes == JSON.parse(localStorage.lootSettings)['min'] && event.offset.hours == 0 && JSON.parse(localStorage.lootSettings)['soundAlert'].includes($(this)[0].parentElement.parentElement.id) && JSON.parse(localStorage.lootSettings)['lootLevel'].includes($(this)[0].parentNode.attributes[0].value) && $('div[class*="travel-agency-travelling"]').length == 0 && $('div[class*="travel-agency-market"]').length == 0 && sub == 0){
var name = $(this)[0].parentElement.parentElement.id
var ll = $(this)[0].parentNode.attributes[0].value
var y = $($(this)[0].parentElement.parentElement).find('a')[0].href.split('=')[1]
if(JSON.parse(localStorage.lootSettings)['audioAlert'] == 1){
audioPlayer.play();
//audioPlayer.onended = function() {if(confirm(name+' Ready to attack at level '+ll)) document.location = 'https://www.torn.com/loader.php?sid=attack&user2ID='+y;}
audioPlayer.onended = function() {$.confirm({
title: false,
autoClose: 'Ignore|20000',
content: name+' ready to attack at level '+ll,
buttons: {
Attack: {
keys: ['enter'],
action: function () {window.open('https://www.torn.com/loader.php?sid=attack&user2ID='+y) }
},
Ignore: function () {
},
}
});
}
}
else{
//if(confirm(name+' Ready to attack at level '+ll)) document.location = 'https://www.torn.com/loader.php?sid=attack&user2ID='+y;
$.confirm({
title: false,
autoClose: 'Ignore|20000',
content: name+' ready to attack at level '+ll,
buttons: {
Attack: {
keys: ['enter'],
action: function () {window.open('https://www.torn.com/loader.php?sid=attack&user2ID='+y) }
},
Ignore: function () {
},
}
});
}
}
});
});
$('tr[id^="Duke"]').find('a').attr('title','Rheinmetall MG 3 (Primary)<br>Homemade Pocket Shotgun (Secondary)<br>Madball (Melee)<br>Nail Bomb (Temporary)<br>Classic Fedora<br>Pinstripe Suit Trousers<br>Duster');
$('tr[id^="Leslie"]').find('a').attr('title','Nock Gun (Primary)<br>Beretta Pico (Secondary)<br>Riding Crop (Melee)<br>Sand (Temporary)<br>Black Oxfords<br>Sweatpants<br>String Vest');
$('tr[id^="Jimmy"]').find('a').attr('title','Bread Knife (Melee)<br>Concussion Grenade (Temporary)<br>Ripped Jeans<br>Lawyer Business Card<br>Bag of Sherbert<br>Jawbreaker<br>Pixie Sticks');
$('tr[id^="Fernando"]').find('a').attr('title','Poison Umbrella (Melee)<br>Bandit Mask<br>Feathery Hotel Coupon');
$('tr[id^="Tiny"]').find('a').attr('title','Sledgehammer (Melee)<br>Energy Cans (Munster, Red Cow, Taurine)<br>Six Pack of Energy Drink<br>Dumbbells<br>Boxing Gloves');
$('tr[id^="Scrooge"]').find('a').attr('title',"Snow Cannon (Primary)<br>Diamond Icicle (Melee)<br>Snowball (Temporary)<br>Scrooge's Boots<br>Scrooge's Gloves<br>Scrooge's Hat<br>Scrooge's Topcoat<br>Scrooge's Trousers");
$('tr[id^="Easter Bunny"]').find('a').attr('title','Egg-Propelled Launcher (Primary)<br>Frying Pan (Melee)<br>Bunny Suit<br>Ski Mask<br>Bunny Nose<br>Goodie Bag<br>Rabbit Foot');
$('div[id^="timesTitle"]').on('click', function(event) {
icon()
})
$('table[id^="npcs"]').find('tr').on('click', function(event) {
if(JSON.parse(localStorage.lootSettings)['timesAttack'] == 1){
document.location = 'https://www.torn.com/loader.php?sid=attack&user2ID='+$(this).find('a')[0].href.split('=')[1]
}else{
document.location = $(this).find('a')[0].href
}
})
if(JSON.parse(localStorage.lootSettings)['lootState'] == 0){
icon()
}
}
function icon(){
$('table[id*="npcs"]').toggle()
if($('span[id*="state"]')[0].id == 'state'){
$('span[id*="state"]')[0].innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 14 32"><path d="M4 6l-4 4 6 6-6 6 4 4 10-10L4 6z"></path></svg>'
$('span[id*="state"]')[0].id = 'state1'
settings['lootState'] = 0
localStorage.lootSettings = JSON.stringify(settings)
}
else{
$('span[id*="state"]')[0].innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 20 32"><path d="M16 10l-6 6-6-6-4 4 10 10 10-10-4-4z"></path></svg>'
$('span[id*="state"]')[0].id = 'state'
settings['lootState'] = 1
localStorage.lootSettings = JSON.stringify(settings)
}
}
function addSettings(){
$('div[class^="preferences-container"]').append("<div class='title-black top-round' id='settingsHead' style='margin-top: 5px;'>NPC times Settings</div>").append("<div id='settingsCont' class='vSet cont-gray'></div>")
$('#settingsCont').append(`
<div class="vCon"><label for="apiKey" class="vLabel">Torn Stats API key</label><input type="text" id="apiKey" class="vInput"/></input></div>
<div class="vCon"><label for="toLoot" class="vLabel"> NPC/s to display</label><input type="text" id="toLoot" class="vInput"/></input></div>
<div class="vCon"><label for="soundAlert" class="vLabel"> NPC/s to alert on</label><input type="text" id="soundAlert" class="vInput"/></input></div>
<div class="vCon"><label for="lootLevel" class="vLabel"> Levels to alert on</label><input type="text" id="lootLevel" class="vInput"/></input></div>
<div class="vCon"><label for="min" class="vLabel"> Minute to alert on</label><input type="text" id="min" class="vInput"/></input></div>
<div class="vCon"><label for="sec" class="vLabel"> Second to alert on</label><input type="text" id="sec" class="vInput"/></input></div>
<div class="vCon"><label for="audio" class="vLabel"> Audio for alert</label><input type="text" id="audio" class="vInput"/></input></div>
<div class="vCon"><label for="showAll" class="vLabel"> Show all NPCs</label><input type="checkbox" id="showAll" class="vCheckbox"></input></div>
<div class="vCon"><label for="allLevels" class="vLabel"> Show all loot levels</label><input type="checkbox" id="allLevels" class="vCheckbox"></input></div>
<div class="vCon"><label for="audioAlert" class="vLabel"> Audio alert</label><input type="checkbox" id="audioAlert" class="vCheckbox"></input></div>
<div class="vCon"><label for="timesAttack" class="vLabel"> NPC row links to attack page</label><input type="checkbox" id="timesAttack" class="vCheckbox"></input></div>
<div class="vCon"><input id="sValue" name="sLink" type="submit" class="vButton" value="Submit"></input><input id="resetSettings" name="sLink" type="submit" class="vButton" value="Reset"></input></div>
`)
$.each($('input[class*="vInput"]'), function(){
$(this).css('width',JSON.stringify(JSON.parse(localStorage.lootSettings)[$(this)[0].id]).replace(/\[/g, '').replace(/\]/g, '').replace(/"/g, '').length+2+'ch')
$(this).val(JSON.stringify(JSON.parse(localStorage.lootSettings)[$(this)[0].id]).replace(/\[/g, '').replace(/\]/g, '').replace(/"/g, ''))
})
$('input[id="min"],input[id="sec"]').keypress(function (e) {
if (String.fromCharCode(e.keyCode).match(/[^0-9]/g)) return false;
})
$('input[class*="vInput"]').on('input', function(){
this.style.width=this.value.length+2+'ch'
if(this.id == 'apiKey' || this.id == 'sec' || this.id == 'min' || this.id == 'audio'){
if(this.id == 'apiKey' || this.id == 'audio'){
settings[this.id] = this.value
localStorage.lootSettings = JSON.stringify(settings)
}
else{
settings[this.id] = parseInt(this.value)
localStorage.lootSettings = JSON.stringify(settings)
}
}
else if(this.id == 'toLoot' || this.id == 'soundAlert' || this.id == 'lootLevel'){
settings[this.id] = this.value.split(',')
localStorage.lootSettings = JSON.stringify(settings)
}
})
$.each($('div[class*="vCon"] input[type*="checkbox"]'), function(){
if(JSON.parse(localStorage.lootSettings)[$(this)[0].id] == 1){
$(this).prop('checked', true)
}
})
$('div[class*="vCon"] input[type*="checkbox"]').on('click', function(){
if($("input[id="+$(this)[0].id+"]:checked").length == 0){
settings[$(this)[0].id] = 0
localStorage.lootSettings = JSON.stringify(settings)
}else{
settings[$(this)[0].id] = 1
localStorage.lootSettings = JSON.stringify(settings)
}
})
$('input[id*="sValue"]').on('click', function(){
if(parseInt(JSON.parse(localStorage.lootSettings)['update'])+600000 < Date.now()){
$('div[id^="timesCont"]').remove()
getLoot()
loop()
}
else{
$('div[id^="timesCont"]').remove()
cd()
loop()
}
})
$('input[id*="resetSettings"]').on('click', function(){
localStorage.lootSettings = JSON.stringify({'toLoot':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'soundAlert':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'min':2,'sec':40,'lootLevel':["4","5"],'showAll':0,'allLevels':0,'audioAlert':1,'timesAttack':1,'audio':'https://www.torn.com/casino/keno/audio/numberwin1.ogg','update':0,'lootState':1,'apiKey':"Add key"})
})
}
if(window.location.href.split('/')[3].split('.')[0] === 'preferences'){
addSettings()
}
function loop(){
setInterval(function(){
getLoot()
}, 600000);
}
function start(){
if(parseInt(JSON.parse(localStorage.lootSettings)['update'])+600000 < Date.now()){
getLoot()
loop()
}
else{
cd()
loop()
}
}
document.addEventListener('visibilitychange', function (event) {
if (document.hidden) {
sub = 1
} else {
sub = 0
}
});
waitForKeyElements("#mainContainer", start);