// ==UserScript==
// @name infinite retail smasher
// @namespace [email protected]
// @description plays BvS infinite retail (poorly)
// @include http://*animecubed.com/billy/bvs/shop-retail.html
// @include https://*animecubedgaming.com/billy/bvs/shop-retail.html
// @version 3.5
// @grant none
// ==/UserScript==
// returns true if on the main page and an appropriate level is checked
var preflightCheck = function() {
// Check that infinite is actually unlocked
var infinite = document.querySelector('#diff9');
var multiplier = document.querySelector('[name="rtmultiplier"]');
if (multiplier) {
multiplier.value = 5;
}
if (infinite) {
infinite.checked = true;
var difficultyinput = document.querySelector('[name=bfrilvl]');
if (multiplier) {
multiplier.value = 1;
}
if (difficultyinput.value >= 11) {
difficultyinput.value = 11;
if (multiplier) {
multiplier.value = 5;
}
}
return true;
} else {
var tier8 = document.querySelector('#diff8');
if (tier8) {
tier8.checked = true;
return true;
} else {
var tier7 = document.querySelector('#diff7');
if (tier7) {
tier7.checked = true;
return true;
}
}
}
return false;
};
// returns false if recommended actions are set; otherwise
// selects recommended actions and returns true.
var setActions = function() {
if (!document.suppressrt) return;
var barge = document.querySelector('#supK1');
var deathstare = document.querySelector('#supN6');
var newrelease = document.querySelector('#supM1');
var boot = document.querySelector('#supI');
var bogo = document.querySelector('#supH1');
var entice = document.querySelector('#supJ1');
var waifu = document.querySelector('#supV2');
var upsell = document.querySelector('#supV4');
var hey = document.querySelector('#supV5');
var trapdoor = document.querySelector('#supK6');
var hype = document.querySelector('#supK9');
var voice = document.querySelector('#supK7');
var submanager = document.querySelector('select[name="pick_subm"]');
var changed = false;
if (!barge.checked) { barge.checked = true; changed = true; }
if (!deathstare.checked) { deathstare.checked = true; changed = true; }
if (newrelease.checked) { newrelease.checked = false; changed = true; }
if (boot.checked) { boot.checked = false; changed = true; }
if (!bogo.checked) { bogo.checked = true; changed = true; }
if (!entice.checked) { entice.checked = true; changed = true; }
if (waifu.checked) { waifu.checked = false; changed = true; }
if (!upsell.checked) { upsell.checked = true; changed = true; }
if (hey.checked) { hey.checked = false; changed = true; }
if (!hype.checked) { hype.checked = true; changed = true; }
if (trapdoor && trapdoor.checked) { trapdoor.checked = false; changed = true; }
if (voice && voice.checked) { voice.checked = false; changed = true;}
if (submanager.value != 3) { submanager.value = 3; changed = true; }
if (changed) { console.log('Not using recommended actions!'); }
return changed;
};
var Customer = function Customer(name, splusl, bounty, RPT, status, select) {
this.isEmpty = !name;
if (this.isEmpty) return;
this.name = name;
this.splusl = splusl;
this.bounty = bounty;
this.RPT = RPT;
this.status = status;
// function to select this customer
this.select = select;
};
var hypeDamage = function(customer) {
if (customer.isEmpty) {
return 0;
}
// TODO: improve this heuristic?
if (customer.status == 'Hyped') {
// TODO: this should probably just be return normalDamage(customer, 4)
if (customer.splusl > 6) {
return 4 * customer.RPT / 2;
} else if (customer.splusl > 2) {
return (customer.splusl) * customer.RPT / 2;
} else {
return 0;
}
}
if (customer.splusl <= 6) {
return (customer.splusl + 1) * customer.RPT;
}
var damage = (customer.splusl + 6) / 2;
if (customer.status == 'Annoying') {
return damage * (customer.RPT-1) + customer.splusl + 1 ;
}
return damage * customer.RPT;
};
var normalDamage = function(customer, amount) {
if (customer.isEmpty) {
return 0;
}
if (customer.status == 'Hyped') {
// TODO: improve this heuristic?
if (customer.splusl > amount + 2) {
return (amount) * customer.RPT / 2;
} else if (customer.splusl > 2) {
return (customer.splusl) * customer.RPT / 2;
} else {
return 0;
}
} else {
if (amount >= customer.splusl) {
amount = customer.splusl;
} else if (amount >= customer.splusl - 3) {
amount = customer.splusl - 3;
} else if (amount >= customer.splusl - 6) {
amount = customer.splusl - 6;
}
if (customer.splusl > amount) {
return amount * customer.RPT;
} else {
return (customer.splusl + 1) * customer.RPT;
}
}
};
var customerRPT = {
// These are my current guesses on average rage/turn
'Fanboy': 2/9,
'Fangirl': 2/3,
'Bad Haggler': 1/2,
'Buffet': 1/3,
'Ghost Michelle': 4/3,
'Hater': 1,
'One-Up': 2/3,
'Shipper': 1/2,
'Skimmer': 1/3,
'Soft Talker': 1,
'Wanderer': 1/4,
'Umm': 1,
'Clumsy Cosplayer': 3/4,
'Model': 2/3,
'Cardrat': 1,
'Bender': 1,
'Cropduster': 5000,
'Fighter': 3/2,
'Childswarm': 2/3,
'MeowMeow': 1, // 100% act
// Other customers omitted for irrelevance in infinite
};
var parseCustomer = function(text, select) {
var empty = /Empty/.test(text);
if (empty) { return new Customer(); }
var loaf = /Loaf: ([0-9]+)/.exec(text);
var stingy = /S.: ([0-9]+)/.exec(text);
var annoying = /Annoying/.test(text);
var hyped = /Hyped/.test(text);
var stinky = /Stinky/.test(text);
var bounty = /\(\+([0-9]+)\)/.exec(text);
var splusl = 0;
if (loaf) splusl += parseInt(loaf[1],10);
if (stingy) splusl += parseInt(stingy[1],10);
var customerType =
/(Fanboy|Fangirl|Bad Haggler|Buffet|Ghost Michelle|Hater|One-Up|Shipper|Skimmer|Soft Talker|Wanderer|Umm|Clumsy Cosplayer|Model|Cardrat|Bender|Cropduster|Fighter|Childswarm|MeowMeow|Tsk)/.exec(text);
if (!customerType) {
// console.log('Assuming that customer is shadow player...')
customerType = ['', 'Shadow Player'];
}
var RPT = customerRPT[customerType[1]];
if (customerType[1] == 'Tsk') {
var dirtyLevel = /Dirty Store \(Level ([0-9]+)\)/.exec(document.body.textContent);
if (dirtyLevel) {
RPT = parseInt(dirtyLevel[1],10) * 0.4;
} else {
RPT = 0;
}
} else if (customerType[1] == 'Shadow Player') {
RPT = -1/3;
}
if (annoying) {
RPT += 1;
}
return new Customer(customerType[1], splusl, bounty ? bounty[1] : 0, RPT, hyped ? 'Hyped' : stinky ? 'Stinky' : annoying ? 'Annoying' : '', select);
};
var storeParser = function() {
// Always answer the phone immediately; suboptimal, but who cares
var phone = document.querySelector('input[value="PHONE"]');
if (phone) {
phone.checked = true;
return;
}
// Is the store empty?
var grumble = document.querySelector('input[value="S"]');
if (grumble) {
var opm = document.querySelector('input[value="F"]');
if (opm) {
opm.checked = true;
} else {
grumble.checked = true;
}
return;
}
var store = document.querySelectorAll('table[cellpadding="0"]')[4];
var storeTable = store.children[0];
var rows = storeTable.children;
var topRow = rows[0].children;
var bottomRow = rows[1].children;
var parsedTopRow = [];
var parsedBottomRow = [];
// Now we try to parse the store >_>
for (var col = 0; col < 7; col++) {
var thingy = topRow[col].querySelector('span[onclick]');
if (col === 0 || col == 3 || col == 6) {
parsedTopRow.push(new Customer());
} else { parsedTopRow.push(parseCustomer(topRow[col].textContent, thingy ? thingy.onclick : null)); }
thingy = bottomRow[col].querySelector('span[onclick]');
if (col == 2 || col == 4 || col == 5) {
parsedBottomRow.push(new Customer());
} else { parsedBottomRow.push(parseCustomer(bottomRow[col].textContent, thingy ? thingy.onclick : null)); }
}
var highestHypeDamage = 0;
var selectHype = function() { alert('Something has gone wrong(selectHype)'); };
// console.log(parsedTopRow);
// console.log(parsedBottomRow);
for (var i = 0; i < 7; ++i) {
// console.log(parsedTopRow[i]);
if (hypeDamage(parsedTopRow[i]) > highestHypeDamage) {
highestHypeDamage = hypeDamage(parsedTopRow[i]);
selectHype = parsedTopRow[i].select || selectHype;
}
// console.log(parsedBottomRow[i]);
if (hypeDamage(parsedBottomRow[i]) > highestHypeDamage) {
highestHypeDamage = hypeDamage(parsedBottomRow[i]);
selectHype = parsedBottomRow[i].select || selectHype;
}
}
console.log('Best hype damage: ' + highestHypeDamage);
if (highestHypeDamage <= 0) {
// Nothing for us to do here
// Select any customer, nobody cares
document.querySelector('select[name="actiontarget"]').value = 'custid1';
fallback();
return;
}
var highestNormalDamage = 0;
var selectNormal = function() { alert('Something has gone wrong(selectNormal)'); };
for (i = 0; i < 7; ++i) {
if (normalDamage(parsedTopRow[i], 5) > highestNormalDamage) {
highestNormalDamage = normalDamage(parsedTopRow[i], 5);
selectNormal = parsedTopRow[i].select || selectNormal;
}
if (normalDamage(parsedBottomRow[i], 5) > highestNormalDamage) {
highestNormalDamage = normalDamage(parsedBottomRow[i],5);
selectNormal = parsedBottomRow[i].select || selectNormal;
}
}
console.log('Best Savings Punch (5X) damage: ' + highestNormalDamage);
var leftSide = [parsedTopRow[1], parsedTopRow[2], parsedBottomRow[0], parsedBottomRow[1]];
var leftDamage = leftSide.map(function(c) { return normalDamage(c,3); }).reduce(function(s,e) { return s+e; });
console.log ('Left Pitch damage: ' + leftDamage);
var rightSide = [parsedTopRow[4], parsedTopRow[5], parsedBottomRow[3], parsedBottomRow[6]];
var rightDamage = rightSide.map(function(c) { return normalDamage(c,3); }).reduce(function(s,e) { return s+e; });
console.log ('Right Pitch damage: ' + rightDamage);
var selectPitch = function() { alert ('Something has gone wrong(selectPitch)'); };
if (leftDamage > rightDamage) {
if (leftDamage <= 0) {
selectPitch = leftSide.map(function(c) { return c.select; }).reduce(function(s,e) { return s || e; }) || rightSide.map(function(c) { return c.select; }).reduce(function(s,e) { return s || e; });
} else {
selectPitch = leftSide.map(function(c) { return c.select; }).reduce(function(s,e) { return s || e; }) || selectPitch;
}
} else {
selectPitch = rightSide.map(function(c) { return c.select; }).reduce(function(s,e) { return s || e; }) || selectPitch;
}
var ckDamageTopRow = parsedTopRow.map(function(c) { return normalDamage(c,4); });
var ckDamageBottomRow = parsedBottomRow.map(function(c) { return normalDamage(c,4); });
var figuresDamage = ckDamageTopRow[1] + ckDamageTopRow[2];
console.log('Figures Coupon Kick damage: ' + figuresDamage);
var mangaDamage = ckDamageBottomRow[0] + ckDamageBottomRow[1];
console.log('Manga Coupon Kick damage: ' + mangaDamage);
var keyChainsDamage = ckDamageTopRow[4] + ckDamageTopRow[5];
console.log('Key Chains Coupon Kick damage: ' + keyChainsDamage);
var entrywayDamage = ckDamageBottomRow[6];
console.log('Entryway Coupon Kick damage: ' + entrywayDamage);
var registerDamage = ckDamageBottomRow[3];
console.log('Register Coupon Kick damage: ' + registerDamage);
var maxCkDamage = Math.max(figuresDamage, mangaDamage, keyChainsDamage, entrywayDamage, registerDamage);
var selectCk = function() { alert ('Something has gone wrong(selectCk)'); };
if (maxCkDamage <= 0) {
selectCk = function() {
leftSide.map(function(c) { return c.select; }).reduce(function(s,e) { return s || e; }) || rightSide.map(function(c) { return c.select; }).reduce(function(s,e) { return s || e; });
};
}
if (figuresDamage == maxCkDamage) {
selectCk = parsedTopRow[1].select || parsedTopRow[2].select || selectCk;
} else if (mangaDamage == maxCkDamage) {
selectCk = parsedBottomRow[0].select || parsedBottomRow[1].select || selectCk;
} else if (keyChainsDamage == maxCkDamage) {
selectCk = parsedTopRow[4].select || parsedTopRow[5].select || selectCk;
} else if (entrywayDamage == maxCkDamage) {
selectCk = parsedBottomRow[6].select || selectCk;
} else {
selectCk = parsedBottomRow[3].select || selectCk;
}
var maxPitchDamage = Math.max(leftDamage, rightDamage);
var damages = [highestHypeDamage, highestNormalDamage, maxCkDamage, maxPitchDamage];
console.log(damages);
damages.sort(function(a, b) { return b - a; });
var maxDamage = damages[0];
console.log(damages);
var triedHype = false;
var triedPitch = false;
var triedCk = false;
var triedNormal = false;
while (damages.length) {
console.log(damages);
if (damages[0] <= 0) {
break; // Too lazy to write proper fallback code; just check out the good customer
}
if ((!triedHype) && (highestHypeDamage == damages[0])) {
if (tryHype()) {
console.log('Recommended action: Hype');
selectHype();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryFreeShuffle()) {
console.log('Recommended action: shuffle');
// TODO: select target better
selectHype();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryThinkFast()) {
console.log('Recommended action: Think Fast');
return;
} else {
// Since there's no way to get to hype, throw it away and try the rest
console.log('Giving up on Hype');
triedHype = true;
damages.shift();
continue;
}
} else if ((!triedPitch) && (maxPitchDamage == damages[0])) {
if (tryPitch()) {
console.log('Recommended action: Pitch');
selectPitch();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryFreeShuffle()) {
console.log('Recommended action: shuffle');
selectHype();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryThinkFast()) {
console.log('Recommended action: Think Fast');
return;
} else {
// Since there's no way to get to pitch, throw it away and try the rest
console.log('Giving up on Pitch');
triedPitch = true;
damages.shift();
continue;
}
} else if ((!triedCk) && (maxCkDamage == damages[0])) {
if (tryCk()) {
console.log('Recommended action: Coupon Kick');
selectCk();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryFreeShuffle()) {
console.log('Recommended action: shuffle');
selectHype();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryThinkFast()) {
console.log('Recommended action: Think Fast');
return;
} else {
// Since there's no way to get to CK, throw it away and try the rest
console.log('Giving up on Coupon Kick');
triedCk = true;
damages.shift();
continue;
}
} else if (!triedNormal) {
if (tryNormal()) {
console.log('Recommended action: Savings Punch');
selectNormal();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryFreeShuffle()) {
console.log('Recommended action: shuffle');
selectHype();
return;
} else if ((damages.length == 1 || damages[0] > damages[1] + 1) && tryThinkFast()) {
console.log('Recommended action: Think Fast');
return;
} else {
// Since there's no way to get to sp, throw it away and try the rest
console.log('Giving up on Savings Punch');
triedNormal = true;
damages.shift();
continue;
}
}
}
document.querySelector('select[name="actiontarget"]').value = 'custid1';
fallback(maxDamage > 1);
};
var tryHype = function() {
var hype = document.querySelector('input[value="12"]');
if (hype) {
hype.checked = true;
return true;
}
return false;
};
var tryPitch = function() {
var pitch = document.querySelector('input[value="3"][type="radio"]');
if (pitch) {
pitch.checked = true;
return true;
}
return false;
};
var tryCk = function() {
var coupon = document.querySelector('input[value="2"][type="radio"]');
if (coupon) {
coupon.checked = true;
return true;
}
return false;
};
var tryNormal = function() {
var savings = document.querySelector('input[value="1"][type="radio"]');
if (savings) {
savings.checked = true;
return true;
}
return false;
};
var tryFreeShuffle = function() {
var bogo = document.querySelector('input[value="11"]');
if (bogo) {
bogo.checked = true;
return true;
}
var barge = document.querySelector('input[value="5"]');
if (barge) {
barge.checked = true;
return true;
}
return false;
};
var tryThinkFast = function() {
var thinkfast = document.querySelector('input[value="6"]');
if (thinkfast) {
thinkfast.checked = true;
return true;
}
return false;
};
var fallback = function(freeshuffle) {
var hype = document.querySelector('input[value="12"]');
if (hype) {
hype.checked = true;
return;
}
var savings = document.querySelector('input[value="1"][type="radio"]');
if (savings) {
savings.checked = true;
return;
}
var coupon = document.querySelector('input[value="2"][type="radio"]');
if (coupon) {
coupon.checked = true;
return;
}
var pitch = document.querySelector('input[value="3"][type="radio"]');
if (pitch) {
pitch.checked = true;
return;
}
var barge = document.querySelector('input[value="5"]');
if (barge) {
barge.checked = true;
return;
}
if (freeshuffle) {
var freebies = document.querySelector('input[value="Q"]');
if (freebies) {
freebies.checked = true;
return;
}
}
/*
// TODO: logic on whether to do this, it's not a good action if there are hypes
var deodorant = document.querySelector('input[value="L"]');
if (deodorant) {
deodorant.checked = true;
return;
}
*/
var deathstare = document.querySelector('input[value="8"]');
if (deathstare) {
deathstare.checked = true;
return;
}
var thinkfast = document.querySelector('input[value="6"]');
if (thinkfast) {
thinkfast.checked = true;
return;
}
var wutever = document.querySelector('input[type="radio"]');
wutever.checked = true;
return;
};
var main = function() {
if (document.enterretail) {
document.addEventListener('keyup', function(e) {
if (e.code != 'KeyD') return;
document.enterretail.submit();
}, false);
return;
}
if (preflightCheck()) {
console.log('Stand by for takeoff');
var recommendedActions = setActions();
var acted = false;
document.addEventListener('keyup', function(e) {
if (e.code != 'KeyD') return;
if (acted) return;
acted = true;
if (!recommendedActions) {
if (document.querySelector('a[href="javascript:document.startgame.submit();"]')) {
document.startgame.submit();
} else {
alert('no shifts left!');
}
} else {
document.suppressrt.submit();
}
}, false);
} else if (document.makeaction) {
// We're in the store! Let's play.
storeParser();
document.addEventListener('keyup', function(e) {
if (e.code != 'KeyD') return;
if (acted) return;
acted = true;
document.makeaction.submit();
});
} else if (document.movingon) {
document.addEventListener('keyup', function(e) {
if (acted) return;
acted = true;
if (e.code != 'KeyD') return;
document.movingon.submit();
});
} else {
console.log('You want the script? You can\'t handle the script!');
}
};
main();