// ==UserScript==
// @name DripStat DropOut
// @namespace anonycat
// @version 0.7.260
// @description Calculates stats in DripStat, and provides a control panel for automation.
// @match https://dripstat.com/game/
// @grant none
// ==/UserScript==
//Throttle auto purchases to one every 3 seconds, to minimize risk of desyncs with the server
var lastBuy = 0;
//Track how many manual clicks have been made by second over the last 60 seconds
var clickHistory = [];
while (lastBuy < 60)
{clickHistory[lastBuy++] = 0;}
//Convert upgrade indices to their order in the shop
var upgIndex = [1,1,1,1,1,1,1,1,1,1,1];
var oldByteCount = 0;
var first = 1;
function init()
{
localStats.dropout = new Object;
//Should powerups and upgrades be automatically purchased as they become affordable?
//(As of level 5, also includes facilities for grabbing spring beans that appear)
localStats.dropout.autoBuy=0;
//Should the BPS rate be increased to simulate automatic cup clicks?
localStats.dropout.autoClick=0;
//Should memory automatically be dripped?
//0 = no auto-drip, 1 = auto-drip when buffer is full, 2 = auto-drip as needed to create enough space to pay for upgrades
localStats.dropout.autoDrip=0;
//Should we take manual clicks into account when figuring out the payback rate of powerups?
localStats.dropout.clickRate=1;
//If autoclicking is enabled, each second will pick a random multiplier and count off that many cup clicks.
//A multiplier of 0 means no clicking, only natural BPS intake.
//Multipliers greater than 20 are rejected by the server, so we won't ever generate such a thing here.
//Lowest possible multiplier to select
localStats.dropout.cupmultl = 0;
//Average multiplier
localStats.dropout.cupmultm = 5;
//There is no variable for the highest multiplier; it's automatically figured as 2*Middle - Low (but capped at 20).
//string (int) seconds to formatted time
String.prototype.toHHMMSS = function () {
var seconds = parseInt(this, 10); // don't forget the second param
if(seconds <= 0)
return "no time";
var days = Math.floor(seconds / 86400);
seconds -= days*86400;
var hours = Math.floor(seconds / 3600);
seconds -= hours*3600;
var minutes = Math.floor(seconds / 60);
seconds -= minutes*60;
if (hours < 10 && days) {hours = "0"+hours;}
if (minutes < 10 && hours) {minutes = "0"+minutes;}
if (seconds < 10) {seconds = "0"+seconds;}
//note: this output contains zero-width spaces before each colon, mostly to fit in upgrade boxes
if(days)
return days+"d "+hours+":"+minutes+":"+seconds;
else if(hours)
return hours+":"+minutes+":"+seconds;
else
return minutes+":"+seconds;
}
//function to update the control panel
localStats.dropout.updatehud = function(type) {
if(type & 1)
{
if(this.autoBuy)
{
$(".apn").css('background-color', '');
$(".apy").css('background-color', '#AFA');
}
else
{
$(".apy").css('background-color', '');
$(".apn").css('background-color', '#FAA');
}
}
if(type & 2)
{
if(localStats.dropout.autoClick)
{
$(".acn").css('background-color', '');
$(".acy").css('background-color', '#AFA');
}
else
{
$(".acy").css('background-color', '');
$(".acn").css('background-color', '#FAA');
}
}
if(type & 4)
{
if(localStats.dropout.autoDrip==2)
{
$(".drip0").css('background-color', '');
$(".drip1").css('background-color', '');
$(".drip2").css('background-color', '#AFA');
}
else if(localStats.dropout.autoDrip)
{
$(".drip2").css('background-color', '');
$(".drip0").css('background-color', '');
$(".drip1").css('background-color', '#AAF');
}
else
{
$(".drip2").css('background-color', '');
$(".drip1").css('background-color', '');
$(".drip0").css('background-color', '#FAA');
}
}
if(type & 8)
{
if(localStats.dropout.clickRate)
{
$(".crn").css('background-color', '');
$(".cry").css('background-color', '#AFA');
}
else
{
$(".cry").css('background-color', '');
$(".crn").css('background-color', '#FAA');
}
}
if(type & 16)
$("#multl").html("Min. Rate: "+String(localStats.dropout.cupmultl));
if(type & 32)
$("#multm").html("Avg. Rate: "+String(localStats.dropout.cupmultm));
}
//Create the control panel with divs
$('#bpsChartContainer').parent().append("<table style='width:100%; height:105px; line-height:1.3; border-collapse: collapse'><tr>"
+"<td rowspan=2 style='width:33%; border: 2px solid black'><div id='next-purchase-container'></div></td>"
+"<td colspan=2 style='width:12%; height:35px; border: 2px solid black; border-bottom: none' class='apy apn' onclick='localStats.dropout.autoBuy = 1 - localStats.dropout.autoBuy; localStats.dropout.updatehud(1)'>Auto Buy</td>"
+"<td colspan=3 style='width:18%; border: 2px solid black; border-bottom: none' class='drip0 drip1 drip2' onclick='localStats.dropout.autoDrip = (1 + localStats.dropout.autoDrip) % 3; localStats.dropout.updatehud(4)'>Auto Drip</td>"
+"<td colspan=2 style='width:12%; border: 2px solid black; border-bottom: none' class='acy acn' onclick='localStats.dropout.autoClick = 1 - localStats.dropout.autoClick; localStats.dropout.updatehud(2)'>Auto Click</td>"
+"<td style='width:15%; border: 2px solid black; border-right:1px solid black'> Auto Click <div id='multl'></div></td>"
+"<td style='width:5%; border-top: 2px solid black; border-bottom: 2px solid black; background-color: #AFA' onclick='localStats.dropout.cupmultl = Math.min(localStats.dropout.cupmultl + 1, localStats.dropout.cupmultm); localStats.dropout.updatehud(16)'> + </td>"
+"<td style='width:5%; border: 2px solid black; border-left: 1px solid black; background-color: #FAA' onclick='localStats.dropout.cupmultl = Math.max(localStats.dropout.cupmultl - 1, 0); localStats.dropout.updatehud(16)'> - </td></tr>"
+"<tr><td style='width:6%; height:35px; border: 2px solid black; border-top: 1px solid black' class='apn' onclick='localStats.dropout.autoBuy = 0; localStats.dropout.updatehud(1)'> Off </td>"
+"<td style='width:6%; border: 2px solid black; border-top: 1px solid black' class='apy' onclick='localStats.dropout.autoBuy = 1; localStats.dropout.updatehud(1)'> On </td>"
+"<td style='width:6%; border: 2px solid black; border-top: 1px solid black' class='drip0' onclick='localStats.dropout.autoDrip = 0; localStats.dropout.updatehud(4)'> Never </td>"
+"<td style='width:6%; border: 2px solid black; border-top: 1px solid black' class='drip1' onclick='localStats.dropout.autoDrip = 1; localStats.dropout.updatehud(4)'> At <br /> Limit </td>"
+"<td style='width:6%; border: 2px solid black; border-top: 1px solid black' class='drip2' onclick='localStats.dropout.autoDrip = 2; localStats.dropout.updatehud(4)'> For <br /> Costs </td>"
+"<td style='width:6%; border: 2px solid black; border-top: 1px solid black' class='acn' onclick='localStats.dropout.autoClick = 0; localStats.dropout.updatehud(2)'> Off </td>"
+"<td style='width:6%; border: 2px solid black; border-top: 1px solid black' class='acy' onclick='localStats.dropout.autoClick = 1; localStats.dropout.updatehud(2)'> On </td>"
+"<td style='width:15%; border: 2px solid black; border-right:1px solid black'> Auto Click <div id='multm'></div></td>"
+"<td style='width:5%; border-top: 2px solid black; border-bottom: 2px solid black; background-color: #AFA' onclick='localStats.dropout.cupmultm = Math.min(localStats.dropout.cupmultm + 1, 20); localStats.dropout.updatehud(32)'> + </td>"
+"<td style='width:5%; border: 2px solid black; border-left: 1px solid black; background-color: #FAA' onclick='localStats.dropout.cupmultm = Math.max(localStats.dropout.cupmultm - 1, localStats.dropout.cupmultl); localStats.dropout.updatehud(32)'> - </td></tr>"
+"<tr><td style='height:35px; border: 2px solid black; border-right: none'> Average Click Rate - Last 10s: </td>"
+"<td style='border: 2px solid black; border-left: none'><strong><div id='click-rate-10'></div></strong></td>"
+"<td colspan=2 style='border: 2px solid black; border-right: none'> Last 60s: </td>"
+"<td style='border: 2px solid black; border-left: none'><strong><div id='click-rate-60'></div></strong></td>"
+"<td colspan=4 style='border: 2px solid black; border-right: none' class='cry crn' onclick='localStats.dropout.clickRate = 1 - localStats.dropout.clickRate; localStats.dropout.updatehud(8)'> Take click rate into account? </td>"
+"<td style='border: 2px solid black; border-left: 1px solid black; border-right: 1px solid black' class='cry' onclick='localStats.dropout.clickRate = 1; localStats.dropout.updatehud(8)'> Yes </td>"
+"<td style='border: 2px solid black; border-left: none' class='crn' onclick='localStats.dropout.clickRate = 0; localStats.dropout.updatehud(8)'> No </td></tr></table>");
$("#next-purchase-container").html("<div id='next-purchase-label'></div><div id='next-purchase-payback'></div><div id='next-purchase-time'></div><div id='max-space-label'></div>");
$("head").append("<style id='tweaks'></style>");
$("#tweaks").text('#upgrades .item {height: 50px}'
+'#bpsChartContainer {padding-bottom: 2px}'
+'#upgrades {height: 85px; line-height: 1.2}'
+'#upgrades .upgcontainer {height: 85px; overflow-y: hidden}'
+'#upgrades .upgROI {font-weight: bolder; text-align: center; color:#F00}'
+'.storeItem {position: relative}'
+'.storeItemName {font-size: 18px}'
+'.storePriceRow {display: table}'
+'.storeROI {display: table-cell; padding-left: 4px; font-size: 0.9em; font-weight: bolder; color: #F00}'
+'.powerup-one {position: absolute; text-align: right; font-weight: bolder; font-size: 18px; top: 1px; right: 67px}'
+'.powerup-all {position: absolute; text-align: right; font-weight: bolder; font-size: 18px; top: 26px; right: 70px; color: #BD511E}');
localStats.dropout.updatehud(63);
}
function byteConvert(val)
{
if(val < 1000)
return +val.toFixed(2)+" B";
else
return NumUtils.byteConvert(val,2);
}
function byteConvert2(val)
{
if(val < 1000)
return val.toFixed(0)+" B";
else
return NumUtils.byteConvert(val,2);
}
function loop()
{
var powerupCounter = 1;
var mult = 0;
var multm = 0;
var bpc = CoffeeCup.calcBytesPerClick();
var bpcBoosts = localStats.powerUps[0].purchasedUpgrades.length;
var bpsDiff = localStats.byteCount - oldByteCount;
if(first)
{
first=0;
$('.storeItem').each(function(index){
$(this).html("<div class='storeItemAmount'>"+localStats.powerUps[index].count+"</div>"
+"<div class='storeItemName'>"+localStats.powerUps[index].name+"</div>"
+"<div class='storePriceRow'>"
+"<div class='storePrice'>"+byteConvert2(localStats.powerUps[index].currentPrice)+"</div>"
+"<div class='storeROI'>(--:--)</div></div>"
+"<div class='powerup-one'>"+byteConvert(localStats.powerUps[index].currentBps)+"ps each</div>"
+"<div class='powerup-all'>"+byteConvert2(localStats.powerUps[index].totalBps)+"ps total</div>");
});
}
else
{
clickHistory.pop();
clickHistory.unshift(Math.round(Math.max(0,(bpsDiff - localStats.bps)/bpc)));
}
var avg10 = 0;
var avg60 = 0;
for(var a = 0; a < 60; a++)
{
avg60 += clickHistory[a];
if(a<10)
avg10 += clickHistory[a];
}
avg10 /= 10;
avg60 /= 60;
if (localStats.dropout.autoClick)
{
multm = localStats.dropout.cupmultm;
mult = Math.min(20-clickHistory[0],Math.floor(Math.random()*(2 * multm - localStats.dropout.cupmultl)+localStats.dropout.cupmultl));
if(mult)
localStats.byteCount += bpc * mult;
if(localStats.byteCount >= localStats.memoryCapacity)
localStats.byteCount = localStats.memoryCapacity;
}
var bps = bpc * localStats.dropout.clickRate * (avg60 + multm) + localStats.bps;
if (localStats.dropout.autoDrip && localStats.byteCount == localStats.memoryCapacity)
dripper.dripGlobal();
//Upgrades aren't necessarily in order, so untangle their order first
$(".upgcontainer").each(function(upg) {
if(this.children[0].className == "item")
{
var pos = Number(this.children[0].style.backgroundPosition.split(' ')[1].split('px')[0]) / -50;
upgIndex[pos] = upg+1;
if($(this).find('.upgROI').length==0)
$(this).append("<div class='upgROI'></div>");
}
})
var data = Array();
var upgdata = [1,1,1,1,1,1,1,1,1,1,1];
var min = 1e+38;
var bytesNeeded=0;
var minObj = {};
localStats.powerUps.slice(0).forEach(function(powerUp) {
powerUp.position = "pu"+powerupCounter;
var hasUpgrade = false;
//Don't calculate ROI on upgrades that have zero powerups fueling them
if(powerUp.count)
powerUp.upgrades.forEach(function(upgrade) {
if(hasUpgrade)
return;
hasUpgrade = true;
upgrade.position = "upg"+upgIndex[powerupCounter-1];
//Let's see which upgrade provides the biggest bang for the buck
//(computed as "time taken before this upgrade will recoup its own cost").
//If the price is so high that the current buffer can't possibly hold enough
//(even if we cashed out for more space right now),
//the "real" price includes what it takes to earn that extra buffer space.
//If there's an object we can afford right now,
//which will pay itself back in 2 hours,
//and another object that would nominally pay itself back in 1h50m,
//except that we can't afford it for 20 more minutes,
//we should account for that in the time before recouping.
//Add the time spent waiting to accrue sufficient funds.
//Oh, and if we need 100MB for something, but can only hold 80MB
//(with 70MB of it filled already), we can't just drip 20MB and keep the rest.
//We have to drip everything at once, shooting all the way to 150MB.
//Thus we can't make any progress on affording the item until a drip,
//and then it costs a full additional 100MB after starting from scratch.
if(upgrade.price > localStats.byteCount + localStats.memoryCapacity)
bytesNeeded = 2 * upgrade.price - (localStats.memoryCapacity + localStats.byteCount);
else if(upgrade.price > localStats.memoryCapacity)
bytesNeeded = upgrade.price;
else if(upgrade.price > localStats.byteCount)
bytesNeeded = upgrade.price - localStats.byteCount;
else
bytesNeeded = 0;
//Cursor upgrades boost clicking too, so if autoclicks are on, take the rate
//and overall BPS into account when determining its value.
if(powerupCounter == 1)
upgrade.value = upgrade.price/((powerUp.totalBps * (1 + localStats.dropout.clickRate * (avg60 + multm) * bpcBoosts * 0.1) + localStats.dropout.clickRate * (avg60 + multm) * bpc) / 10) + bytesNeeded / bps;
else
upgrade.value = upgrade.price/(powerUp.totalBps * (0.1 + localStats.dropout.clickRate * (avg60 + multm) * bpcBoosts * 0.01)) + bytesNeeded / bps;
min = Math.min(min, upgrade.value);
if(upgrade.value == min)
minObj = upgrade;
upgdata[upgIndex[powerupCounter-1]-1]=upgrade.value;
});
powerupCounter++;
//Same procedure on the main power-ups themselves.
if(powerUp.currentPrice > localStats.byteCount + localStats.memoryCapacity)
bytesNeeded = 2 * powerUp.currentPrice - (localStats.memoryCapacity + localStats.byteCount);
else if(powerUp.currentPrice > localStats.memoryCapacity)
bytesNeeded = powerUp.currentPrice;
else if(powerUp.currentPrice > localStats.byteCount)
bytesNeeded = powerUp.currentPrice - localStats.byteCount;
else
bytesNeeded = 0;
powerUp.value = powerUp.currentPrice/(powerUp.currentBps * (1 + localStats.dropout.clickRate * (avg60 + multm) * bpcBoosts * 0.1)) + bytesNeeded / bps;
data.push(powerUp.value);
if(!(powerUp.name == "Spring Framework" && springPowerup.isLocked))
{
min = Math.min(min, powerUp.value);
if(powerUp.value == min)
minObj = powerUp;
}
});
$('.storeItem, .upgcontainer').css('background-color', '');
var selector = minObj.position;
$("#"+selector).css('background-color', '#B2EDED');
$('.storeItem').each(function(index){
$(this).find('.storeROI').html("("+String(data[index]).toHHMMSS()+")");
$(this).find('.powerup-one').html(byteConvert(localStats.powerUps[index].currentBps)+'ps each');
$(this).find('.powerup-all').html(byteConvert2(localStats.powerUps[index].totalBps)+'ps total');
});
$('.upgROI').each(function(index){
$(this).html(String(upgdata[index]).toHHMMSS());
});
var label = minObj.name;
if(minObj.powerup) //Is our best deal an upgrade or a powerup? They use different syntax.
{
label = "<span style='color:#F00'>"+label+"</span>";
var price = minObj.price;
}
else
var price = minObj.currentPrice;
var limitTime = Number((localStats.memoryCapacity - localStats.byteCount)/bps).toFixed(0);
var limitstr = "";
if(price > localStats.byteCount + localStats.memoryCapacity)
{
var time = Number((2 * price - (localStats.memoryCapacity + localStats.byteCount))/bps).toFixed(0);
if(localStats.memoryCapacity==localStats.byteCount)
limitstr = " (Drip NOW!)";
}
else if(price > localStats.memoryCapacity)
{
var time = Number(price/bps).toFixed(0);
limitstr = " (Drip NOW!)";
if(localStats.dropout.autoDrip >= 2)
dripper.dripGlobal();
}
else
var time = Number((price - localStats.byteCount)/bps).toFixed(0);
//Now to fill the control panel.
$("#next-purchase-label").html("Next purchase: <strong>"+label+"</strong>");
$("#next-purchase-payback").html("Pays for itself in "+String(minObj.value).toHHMMSS());
if(time <= 0)
$("#next-purchase-time").html("Affordable now");
else
$("#next-purchase-time").html("Affordable in "+String(time).toHHMMSS()+limitstr);
if(limitTime <= 0)
$("#max-space-label").html("Capacity is maxed out!");
else
$("#max-space-label").html("Capacity maxes out in "+String(limitTime).toHHMMSS());
$("#click-rate-10").html(String(avg10.toFixed(1)));
$("#click-rate-60").html(String(avg60.toFixed(1)));
if(localStats.dropout.autoBuy && price<=localStats.byteCount && lastBuy >= 3)
{
minObj.buy(localStats);
lastBuy = 0;
}
else if (lastBuy < 3)
lastBuy += 1;
if(springPowerup.isLocked && mine.beanCount > 0 && AnonymousUserManager.canGrabBean() && localStats.byteCount > 0)
{
if($('.vex').length)
vex.closeAll();
Mine.onGrab();
}
if($("#networkError")[0].style['cssText'] == "display: block;")
window.location.reload(false);
oldByteCount = localStats.byteCount;
}
init();
setInterval(function(){loop();}, 1000);