Calculates investments + auto missions

By niknah Last update Jun 14, 2009 — Installed 4,112 times.

There are 5 previous versions of this script.

// ==UserScript==
// @name           Calculates investments + auto missions
// @namespace      HighlightInvestments
// @description    Highlight the best investments for those mass manufactured games: ganster battle, sex games, knighted, etc.
// @include        http://apps*.facebook.com/*/investments.php*
// @include        http://apps*.facebook.com/*/missions.php*
// ==/UserScript==

Number.prototype.formatMoney = function(){
	var c=0;
	var d=".";
	var t=",";
	var n = this, c = isNaN(c = Math.abs(c)) ? 2 : c, d = d == undefined ? "," : d, t = t == undefined ? "." : t, s = n < 0 ? "-" : "", i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
	return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

nHtml={
gameNameRe:new RegExp("^/([^/]+)/"),

FindByAttr:function(obj,tag,attr,className) {
	if(attr=="className") { attr="class"; }
	var q=document.evaluate(".//"+tag+"[@"+attr+"='"+className+"']",obj,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
	if(q && q.singleNodeValue) { return q.singleNodeValue; }
	return null;
},
FindByXPath:function(obj,xpath) {
	try {
		var q=document.evaluate(xpath,obj,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
	} catch(e) {
		GM_log('bad xpath:'+xpath);
	}
	if(q && q.singleNodeValue) { return q.singleNodeValue; }
	return null;
},

GameName:function() {
	var m=this.gameNameRe.exec(window.location.pathname);
	if(m) { return m[1]; }
	GM_log('Error unknown game:'+window.location.pathname);
	return null;
},
GMSetValue:function(n,v) {
	return GM_setValue(this.GameName()+"__"+n,v);
},
GMGetValue:function(n,v) {
	return GM_getValue(this.GameName()+"__"+n,v);
},

Click:function(obj) {
	var evt = document.createEvent("MouseEvents");
	evt.initMouseEvent("click", true, true, window,
		0, 0, 0, 0, 0, false, false, false, false, 0, null);
	return !obj.dispatchEvent(evt);
},

spaceTags:{
	'td':1,
	'br':1,
	'hr':1,
	'span':1,
	'table':1
},
GetText:function(obj) {
	var txt='';
	if(obj.textContent!=undefined) { return obj.textContent; }
	for(var o=0; o<obj.childNodes.length; o++) {
		var child=obj.childNodes[o];
		txt+=this.GetText(child);
	}
	return txt;
}

};

HighlightInvestments={
costs:[],
costsByName:{},
bestReturn:null,
hasConstructedOver:0,
cashNeeded10:0,
daysTillBuy:0,
daysTillBuy10:0,

FindByClassName:function(obj,tag,className) {
	var divs=obj.getElementsByTagName(tag);
	for(var d=0; d<divs.length; d++) {
		var div=divs[d];
		if(div.className==className) {
			return div;
		}
	}
	return null;
},
billionRe:new RegExp('billion','i'),
millionRe:new RegExp('million','i'),
trillionRe:new RegExp('trillion','i'),
NumberOnly:function(num) {
	var numOnly=parseFloat(num.replace(/[^0-9\.]/g,""));
	if(this.millionRe.exec(num)) {
		numOnly*=1000000;
	}
	else if(this.billionRe.exec(num)) {
		numOnly*=1000000000;
	}
	else if(this.trillionRe.exec(num)) {
		numOnly*=1000000000000;
	}
	return numOnly;
},
contructedOverRe:new RegExp('<b>([^<]*)</b>','i'),
FindCosts:function() {
	var tds=document.getElementsByTagName('td');
	for(var t=0; t<tds.length; t++) {
		var td=tds[t];
		if(td.className!="investmentsBox") { continue; }
		var nameObj=this.FindByClassName(td,"div","property_name");
		if(nameObj==null) { continue; }
		
		var a=document.createElement('a');
		a.name=nameObj.innerHTML;
		td.insertBefore(a,td.childNodes[0]);
		var constructedObj=this.FindByClassName(td,"div","property_constructed_over");
		var constructedOver=null;
		if(constructedObj!=null) {
			var coMatch=this.contructedOverRe.exec(constructedObj.innerHTML);
			constructedOver=coMatch[1];
		}
		var revObj=this.FindByClassName(td,"span","property_revenue");
		var costObj=this.FindByClassName(td,"span","itemCost");
		var buyObj=this.FindByClassName(td,"form","buyForm");
		var ownedObj=this.FindByClassName(td,"span","itemDetails");
		var owned=this.NumberOnly(ownedObj.innerHTML);

		var inps=buyObj.getElementsByTagName('input');
		var buyButton=null;
		for(var i=0; i<inps.length; i++) {
			var inp=inps[i];
			if(inp.type=="submit") {
				buyButton=inp;
				break;
			}
		}
		var name=nameObj.innerHTML;
		var rev=this.NumberOnly(revObj.innerHTML);
		var cost=this.NumberOnly(costObj.innerHTML);
		var origCost=cost/((owned/10)+1);
		var costObj={
			'rev':rev,'cost':cost,'buyButton':buyButton,
			'name':name,
			'nameObj':nameObj,'buyForm':buyObj,
			'owned':owned,
			'origCost':origCost,
			'costTotal':cost,
			'constructedOver':constructedOver
		};
		this.costs.push(costObj);
		this.costsByName[name]=costObj;
	}

	for(var c=0; c<this.costs.length; c++) {
		var cost=this.costs[c];
		if(cost.constructedOver==null) { continue; }
		var over=this.costsByName[cost.constructedOver];
		if(!over) {
			GM_log('Could not find:'+cost.constructedOver);
			continue;
		}
		var owned=over.owned-10;
		if(owned<10) {
			owned=0;
		}
		var last10OverCost=over.origCost+(owned*over.origCost/10);

		cost.costTotal=last10OverCost+cost.cost;
		cost.last10OverCost=last10OverCost;
		this.hasConstructedOver++;
	}
},
FormatPercent:function(num) {
	return (Math.floor(num*100000)/1000)+"%";
},
ClickBuy:function(items) {
	var propToClick=this.bestReturn;
	if(propToClick.constructedOver!=null) {
		// check if we can buy this, if not let's but the property that we need.
		var over=this.costsByName[propToClick.constructedOver];
		if(over.owned==0) {
			propToClick=over;
		}
	}
	var buySel=propToClick.buyForm.getElementsByTagName('select')[0];
	for(var b=0; b<buySel.options.length; b++) {
		if(buySel.options[b].value==items) {
			buySel.selectedIndex=b;
		}
	}
	propToClick.buyButton.click();
},
AddListener:function(objId,type,func) {
	document.getElementById(objId).addEventListener(type,func,false);
},
moneyRe:new RegExp('([0-9,\\.]+)','im'),
moneySuffixRe:new RegExp('([0-9,\\.]+)\\s*GC','im'),
moneyPrefixRe:new RegExp('Cigs:\\s*([0-9,\\.]+)','im'),
ShowCosts:function() {
	this.FindCosts();
	var bestReturnPerDay=-1;
	var bestReturn=null;
	var headerObj=this.FindByClassName(document.body,"div","headerDivStyle1");
	if(!headerObj) {
		headerObj=nHtml.FindByXPath(document.body,"//div[contains(@id,'status_box')]");
	}
	if(!headerObj) {
		GM_log("Could not find header");
		return;
	}

	var earnPerHour=undefined;
	var statusM=this.moneyRe.exec(nHtml.GetText(headerObj));
	if(statusM) {
		earnPerHour=this.NumberOnly(statusM[1]);
	}

	var cashObj=this.FindByClassName(document.body,"div","cash");
	var moneyM=null;
	if(!cashObj) {
		cashObj=nHtml.FindByXPath(document.body,"//div[contains(@id,'header_stats')]");
		var txt=nHtml.GetText(cashObj);
		moneyM=this.moneyPrefixRe.exec(txt);
		if(!moneyM) {
			moneyM=this.moneySuffixRe.exec(txt);
		}
	} else {
		moneyM=this.moneyRe.exec(cashObj.innerHTML);
	}
	var cash=undefined;
	if(moneyM) {
		cash=this.NumberOnly(moneyM[1]);
	}


	for(var c=0; c<this.costs.length; c++) {
		var cost=this.costs[c];
		var returnPerDay=(cost.rev*24)/cost.costTotal;
//GM_log(cost.name+",rev:"+cost.rev+",cost:"+cost.costTotal);
		var info=document.createElement('span');
		var infoMess=this.FormatPercent(returnPerDay) + " return per day";
		if(cost.last10OverCost) {
			var constructedOverObj=this.costsByName[cost.constructedOver];
			infoMess+=" @ $"+cost.last10OverCost+" per "+constructedOverObj.name;
		}
		info.innerHTML=infoMess;
		cost.nameObj.parentNode.insertBefore(info,cost.nameObj.nextSibling);
		if(this.hasConstructedOver>0 && cost.constructedOver==null) {
			// on gangster battle, knighed, etc.
			// we ignore the primary properties cause
			// they're needed to purchase later ones.
			continue; 
		}
		if(bestReturnPerDay<returnPerDay) {
			bestReturnPerDay=returnPerDay;
			bestReturn=cost;
		}
	}
	var bestLink=document.createElement('span');
	var overMess='';
	var bestToBuy=bestReturn;
	if(bestReturn.constructedOver!=null) {
		var bestOverObj=this.costsByName[bestReturn.constructedOver];
		if(bestOverObj.owned==0) {
			overMess+="(buy "+bestOverObj.name+
			" @ $"+bestOverObj.cost.formatMoney()+")";
			bestToBuy=bestOverObj;
		}
	}
	var cashNeeded=bestToBuy.cost-cash;
	var cashNeeded10=(bestToBuy.cost*10)-cash;
	var earnPerDay=earnPerHour*24;
	var cashNeededMess='';
	if(cashNeeded>0) {
		cashNeededMess=" Need $"+
			cashNeeded.formatMoney()+
			" more, wait "+
			(Math.floor((cashNeeded/earnPerDay)*100)/100)+" days";
	} else if(cashNeeded10>0) {
		cashNeededMess=" Need $"+
			cashNeeded10.formatMoney()+
			" more for 10, wait "+
			(Math.floor((cashNeeded10/earnPerDay)*100)/100)+" days";
	}
	this.cashNeeded10=cashNeeded10;
	this.daysTillBuy=cashNeeded/earnPerDay;
	this.daysTillBuy10=cashNeeded10/earnPerDay;

	var autoBuy10=nHtml.GMGetValue('AutoBuy10');
	bestLink.innerHTML="<br />Best: <a href='#"+bestReturn.name+
		"'>"+bestReturn.name+
		"</a> "+this.FormatPercent(bestReturnPerDay)+
		" return per day @ $"+
		bestReturn.cost.formatMoney()+" each"+
		"<br />Buy: <a id='hi_buy1' href='javascript:;'>1</a>, "+
		"<a id='hi_buy5' href='javascript:;'>5</a>, "+
		"<a id='hi_buy10' href='javascript:;'>10</a>, "+
		"<a id='hi_buy_auto10' href='javascript:;'>"+
		(autoBuy10?"Disable ":"")+
		"Auto buy 10</a> "+
		overMess+cashNeededMess+
		"<br />"
		;

	var overheadObj=this.FindByClassName(document.body,'span','revenue');
	overheadObj.parentNode.insertBefore(bestLink,overheadObj);
	this.bestReturn=bestReturn;
	this.AddListener('hi_buy1','click',function() { HighlightInvestments.ClickBuy(1); } );
	this.AddListener('hi_buy5','click',function() { HighlightInvestments.ClickBuy(5); } );
	this.AddListener('hi_buy10','click',function() { HighlightInvestments.ClickBuy(10); } );
	this.AddListener('hi_buy_auto10','click',function() { nHtml.GMSetValue('AutoBuy10',nHtml.GMGetValue('AutoBuy10')?0:1); HighlightInvestments.AutoBuy(); } );
},

AutoBuy:function() {
	var buyWait=500;
	var noAutoBuy=false;
	if(nHtml.GMGetValue('AutoBuy10')) {
		if(this.cashNeeded10>0) {
			GM_log("Waiting for "+this.daysTillBuy10+" days till we can afford it");
			buyWait=Math.floor(this.daysTillBuy10*1000*(60*60*24))+(60*60*24);
		}
	} else { 
		noAutoBuy=true; 
	}
	var waitUntil=new Date().getTime()+buyWait;
//~~~
	if(buyWait>500 || noAutoBuy) {
		// nothing to auto buy, let's go back to missions page if we have an auto mission setup.
		Missions.GoToAutoMissionPage();
		return;
	}
	window.setTimeout(function() {
		HighlightInvestments.ClickBuy(10);
	},buyWait);
}

};

Missions={



GetMissions:function() {
	var missions={};
	var divs=document.body.getElementsByTagName('div');
	for(var d=0; d<divs.length; d++) {
		var div=divs[d];
		if(div.className!="missionsBox") { continue; }
		var click=nHtml.FindByAttr(div,'input','type','submit');
		if(!click) { 
			GM_log('could not find submit button');
			continue; 
		}
		var spans=div.getElementsByTagName('span');
		var name;
		if(spans.length>0) { name=spans[0].innerHTML; }
		else {
			GM_log('no name for mission');
			continue; 
		}
		missions[name]={'click':click};
	}
	return missions;
},

AddAutoMissionButton:function() {
	var missions=this.GetMissions();
	for(var missionName in missions) {
		var mission=missions[missionName];
		var a=document.createElement('a');
		a.innerHTML='Auto run';
		var span=document.createElement('span');
		span.innerHTML=missionName;
		span.style.display='none';
		a.appendChild(span);
		a.addEventListener('click',function(e) {
			Missions.SetAutoMission(e.target.getElementsByTagName('span')[0].innerHTML.toString());
			Missions.AutoMission();
		},false);
		mission.click.parentNode.insertBefore(a,mission.click);
	}
},
SetAutoMission:function(mission) {
	nHtml.GMSetValue("AutoMission",mission);
},
enduranceRe:new RegExp("([0-9]+)\\s*/\\s*([0-9]+)"),
endurancePrefixRe:new RegExp('(Endurance|Boldness):\\s*([0-9,\\.]+)\\s*/\\s*([0-9,\\.]+)','im'),
GetEndurance:function() {
	var endObj=nHtml.FindByAttr(document.body,'div','className','endurance');

	var endM=null;
	var txt='';
	if(!endObj) {
		endObj=nHtml.FindByXPath(document.body,"//div[contains(@id,'header_stats')]");
		if(endObj) {
			txt=nHtml.GetText(endObj);
			var m=this.endurancePrefixRe.exec(txt);
			if(m) { endM=[m[0],m[2],m[3]]; }
		}
	}
	

	if(!endObj) {
		GM_log('cannot find endurance');
		return null;
	}

	if(!endM) {
		txt=nHtml.GetText(endObj);
		endM=this.enduranceRe.exec(txt);
	}
	if(endM.length>2) {
		return {
			'num':HighlightInvestments.NumberOnly(endM[1]),
			'max':HighlightInvestments.NumberOnly(endM[2])
		};
	} else {
		GM_log('cannot find endurance:'+txt);
	}
	return null;
},
GetAutoMission:function() {
	return nHtml.GMGetValue("AutoMission",'');
},
// go to mission page if auto mission is running.
GoToAutoMissionPage:function() {
	var missionName=this.GetAutoMission();
	if(!missionName || missionName.length<=0) { return false; }
	window.location.href='missions.php?skip_invites=1';
	return true;w
},
AutoMission:function() {
	var missionName=this.GetAutoMission();
	if(!missionName || missionName.length<=0) { return; }
	var missions=this.GetMissions();
	var gameName=nHtml.GameName();
	var tired=false;
	var t=nHtml.FindByXPath(document.body,"//table[contains(@id,'missionsTable')]");
	var endurance=this.GetEndurance();
	if(endurance==null) { 
		return; 
	}
	if(endurance.num>=(endurance.max-1)) {
		var mission=missions[missionName];
		if(mission) {
			window.setTimeout(function() {
				nHtml.Click(mission.click);
			},5000);
		} else {
			GM_log('Could not find mission');
		}
	}
	if(t) {
		var a=document.createElement('a');
		a.addEventListener('click',function() {
			Missions.SetAutoMission('');
		},false);
		a.innerHTML='Stop auto run:'+missionName;
		t.parentNode.insertBefore(a,t);
	} else {
		GM_log('Could not find missions table');
	}
	var waitSecs=5000;
	var enduranceToWait=endurance.max-endurance.num-1;
	waitSecs=320*1000*enduranceToWait;
	if(waitSecs<5000) { waitSecs=5000; }
	var mess=document.createElement('div');
	mess.innerHTML='Wait for '+Math.floor(waitSecs/1000/60)+" mins for "+enduranceToWait+" endurance";
	t.parentNode.insertBefore(mess,t);

	window.setTimeout(function() { window.history.go(0); },waitSecs);
}

};

window.addEventListener("load", function(e) {
	var href=window.location.href;
	if(href.indexOf('/investments.php')>=0) {
		HighlightInvestments.ShowCosts();
		HighlightInvestments.AutoBuy();
	} else if(href.indexOf('/missions.php')>=0) {
		Missions.AddAutoMissionButton();
		Missions.AutoMission();
	}
},false);