T3 WW Crop counter

By msimic Last update May 28, 2009 — Installed 8,642 times.

There are 1 previous version of this script.

// ==UserScript==
// @name T3 WW Crop counter
// @author :)
// @include http://*.travian.*/*.php*
// @exclude http://*.travian.*/hilfe.php*
// @exclude http://*.travian.*/log*.php*
// @exclude http://*.travian.*/index.php*
// @exclude http://*.travian.*/anleitung.php*
// @exclude http://*.travian.*/impressum.php*
// @exclude http://*.travian.*/anmelden.php*
// @exclude http://*.travian.*/gutscheine.php*
// @exclude http://*.travian.*/spielregeln.php*
// @exclude http://*.travian.*/links.php*
// @exclude http://*.travian.*/geschichte.php*
// @exclude http://*.travian.*/tutorial.php*
// @exclude http://*.travian.*/ad/*
// @exclude http://*.travian.*/chat/*
// @exclude http://forum.travian.*
// @exclude http://shop.travian.*
// @version 1.01
// @description  Enables some Travian v3 features
// ==/UserScript==

function funcionPrincipal(e){
	// Momento de inicio de ejecucion del script
	var tiempo_ejecucion = new Date().getTime();

	var version = "1.0";


	var actual = new Array(4);		// Informacion de recursos almacenados
	var total = new Array(4);		// Capacidad de los almacenes y granero
	var produccion = new Array(4);		// Produccion por segundo

	var XPFirst = XPathResult.FIRST_ORDERED_NODE_TYPE;		// Constante que devuelve el primer elemento por XPath
	var XPList  = XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;		// Constante que devuelve una lista de elementos por XPath
	var XPIter  = XPathResult.UNORDERED_NODE_ITERATOR_TYPE;		// Constante que deuvelve un iterador de elementos por XPath

	/**
	 * Funcion que no hace absolutamente nada. Usada cuando no hay mas remedio que usar alguna funcion
	 */
	function dummy(){}
	
	function basename(path) { return path.replace( /.*\//, "" ); }

	/**
	 * Funcion que convierte un numero en su version de 2 digitos. Anyade un 0 si solo tiene un digito
	 *
	 * Params:
	 *	n	Numero a convertir
	 * 
	 * Returns:
	 * 	El numero convertido con al menos dos digitos
	 */
	function LZ(n){	return (n > 9 ? n : '0' + n); }

	/**
	 * Crea un nuevo elemento de tipo DIV con un contenido prefijado
	 * 
	 * Params:
	 *	content	Contenido del nuevo elemento creado
	 * 
	 * Returns:
	 * 	Referencia al nuevo elemento
	 */
	function div(content){ return elem("div", content); }

	/**
	 * Wrapper para la funcion getElementById
	 *
	 * Params:
	 *	id	Texto del ID del elemento a buscar
	 * 
	 * Returns:
	 * 	Elemento del documento con el ID especificado
	 */
	function get(id){ return document.getElementById(id); }

	/**
	 * Multiplica cada elemento de un array por un valor
	 *
	 * Params:
	 *	a	Array con los elementos a procesar
	 *	n	Valor numero por el que multiplicar el array
	 *
	 * Returns:
	 *	Nuevo array con los valores calculados
	 */
	function arrayByN(a, n){ 
		var b = arrayClone(a); 
		for(var i in b){ b[i] *= n; } 
		return b; 
	} 

	/**
	 * Realiza una copia por valor de un array
	 * 
	 * Params:
	 *	a	Array a copiar
	 *
	 * Returns:
	 *	Referencia a un nuevo array con el mismo contenido que el original
	 */
	function arrayClone(a){ 
		var b = new Array(); 
		for(var i in a){ b[i] = a[i]; } 
		return b; 
	} 

	/**
	 * Suma el contenido de dos arrays. Si cualquiera de los dos tiene valor nulo, se devuelve una copia del otro
	 * 
	 * Params:
	 *	a	Primer array sumando
	 *	b	Segundo array sumando
	 *
	 * Returns:
	 *	Referencia a un nuevo array con la suma
	 */
	function arrayAdd(a, b){ 
		if(!a){ return arrayClone(b); } 
		if(!b){ return arrayClone(a); } 
		var c = new Array(); 
		for(var i = 0; i < Math.max(a.length,b.length); c[i] = a[i] + b[i++]); 
		return c; 
	}

	/**
	 * Comprueba si un valor esta presente en un array determinado
	 *
	 * Params:
	 *	array	Array a comprobar
	 *	value	Valor a buscar en el array
	 *
	 * Returns:
	 *	true si el valor esta en el array, false en caso contrario
	 */
	function arrayValueExist(array, value){
		for(var i = 0; i < array.length; i++) if (array[i] == value) return true;
		return false;
	}

	// Removes leading whitespaces
	String.prototype.LTrim = function () {
		return this.replace(/^\s+/, '');
	}

	// Removes ending whitespaces
	String.prototype.RTrim = function () {
		return this.replace(/\s+$/, '');
	}

	// Removes leading and ending whitespaces
	String.prototype.trim = function () {
		return this.LTrim().RTrim();
	}

	Date.prototype.toShortFormat = function(){
			return this.getDate()+"/"+LZ(this.getMonth()+1) + " "+LZ(this.getHours())+":"+LZ(this.getMinutes()); //+":"+LZ(this.getSeconds());
	}
	
	/**
	 * Elimina un elemento
	 *
	 * Param:
	 *	elem	El elemento a eliminar
	 */
	function removeElement(elem){ if (elem) elem.parentNode.removeChild(elem) }

	/**
	 * Suma todos los valores de un array
	 * 
	 * Params:
	 *	a	Array a sumar
	 *
	 * Returns:
	 *	Valor con la suma de todos los elementos del array
	 */
	function arrayToInt(a){ 
		var h = 0; 
		for(var i in a){ h += a[i]; }
		return h; 
	}

	/**
	 * Inserta un nodo despues de otro
	 * 
	 * Params:
	 *	node		Nodo de referencia
	 *	referenceNode	Nodo a insertar
	 */
	function insertAfter(node, referenceNode) {
		node.parentNode.insertBefore(referenceNode, node.nextSibling);
	}

	/**
	 * Crea un elemento cualquiera con un contenido
	 * 
	 * Params:
	 *	tag	Etiqueta del nuevo elemento
	 *	content	Contenido del nuevo elemento en formato texto
	 *
	 * Returns:
	 *	Referencia al nuevo elemento creado
	 */
	function elem(tag, content){ 
		var ret = document.createElement(tag);  
		ret.innerHTML = content;  
		return ret;
	}

	/**
	 * Realiza una busqueda en el documento usando XPath
	 * 
	 * Params:
	 *	xpath	Expresion de busqueda
	 *	xpres	Tipo de busqueda
	 *
	 * Returns:
	 *	Referencia a un elemento resultado de XPath
	 */
	function find(xpath, xpres){
		var ret = document.evaluate(xpath, document, null, xpres, null);
		return  xpres == XPFirst ? ret.singleNodeValue : ret;
	}

	/**
	 * Crea o actualiza el valor de una cookie con una determinada duracion
	 * 
	 * Params:
	 *	name	Nombre de la cookie
	 *	value	Contenido de la cookie
	 *	days	Duracion de la validez de la cookie
	 */
	 /*
	function createCookie(name, value, days){
		if (typeof GM_setValue == "undefined"){
			if (days){
				var date = new Date();
				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
				var expires = "; expires=" + date.toGMTString();
			}else{ var expires = ""; }
			document.cookie = name + "=" + value + expires + "; path=/";
		}else GM_setValue(name, value);
	}
*/
	/**
	 * Recupera el valor de una cookie
	 * 
	 * Params:
	 *	name	Nombre de la cookie
	 *
	 * Returns:
	 *	Contenido de la cookie o null si no existe
	 */
	 /*
	function readCookie(name){
		if (typeof GM_getValue == 'undefined'){
			var ca = document.cookie.split(';');
			var nameEQ = name + "=";
			for(var i = 0; i < ca.length; i++) {
				var c = ca[i];
				while (c.charAt(0) == ' ') c = c.substring(1, c.length); // Elimina espacios
				if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
			}
			return null;
		}else return GM_getValue(name, null);
	}
*/
	/**
	 * Elimina una cookie
	 * 
	 * Params:
	 *	name	Nombre de la cookie
	 */
	//function eraseCookie(name){ createCookie(name, "", -1); }

	/**
	 * Calcula el identificador de una casilla partiendo de sus coordenadas X e Y
	 *
	 * Params:
	 *	x	Coordenada X
	 *	y	Coordenada Y
	 *
	 * Returns:
	 *	ID de la casilla correspondiente a las coordenadas
	 */
	function xy2id(x, y){ return (1 + (parseInt(x) + 400) + (801 * Math.abs(parseInt(y) - 400))); }

	/**
	 * Calcula el numero de segundos de una hora expresada en formato xx:xx:xx
	 * 
	 * Params:
	 *	myElement	Texto con la hora a calcular
	 *
	 * Returns:
	 *	Numero de segundos que expresa la hora
	 */
	function calcular_segundos(myElement) {
		var p = myElement.split(":");
		return (p[0] * 3600) + (p[1] * 60) + (p[2] * 1);
	}

	/**
	 * Convierte una cantidad en segundos en su representacion en horas. 
	 * Funcion inversa de "calcular_segundos"
	 * 
	 * Params:
	 *	s	Numero de segundos
	 *
	 * Returns:
	 *	Texto con la representacion en horas o la cadena "0:00:0?" si es negativo
	 */
	function formatear_tiempo(s){
		if(s > -1){
			var horas = Math.floor(s/3600);
			var minutos = Math.floor(s/60) % 60;
			var segundos = s % 60;
			var t = horas + ":" + LZ(minutos) + ":" + LZ(segundos);
		}else{
			var t = "0:00:0?";
		}
		return t;
	}

	/**
	 * Recupera informacion generica inicial para el resto de funciones
	 */
	function getGeneralData(){
		// Idioma
		//find("//script[@type='text/javascript']", XPFirst).src.search(/\/([^\/]+)?3.js$/);
		//find("//img[contains(@src, 'plus.gif')]", XPFirst).src.search(/\/img\/([^\/]+)\//);
		//idioma = "en";//RegExp.$1;
		//idioma = RegExp.$1;
		// Ruta al pack grafico
		find("//link[@rel='stylesheet']", XPFirst).href.search(/^(.*\/)(.*).css$/);
		pack_grafico = RegExp.$1;
		// Identificador de usuario
		//find("//td[@class='menu']", XPFirst).innerHTML.search(/spieler.php\?uid=(\d+)"/);
		//uid = RegExp.$1;
		// Nombre del servidor
		location.href.search(/http:\/\/(.*)\//);
		server = RegExp.$1;
		// Por cada tipo de recurso: cantidad actual almacenada, capacidad total del almacen / granero y produccion por segundo
		for (var i = 0; i < 4; i++){
			actual[3-i] = get('l'+(i+1)).innerHTML.split("/")[0];
			total[3-i] = get('l'+(i+1)).innerHTML.split("/")[1];
			produccion[3-i] = get('l'+(i+1)).title/3600;
		}
	}

	/**
	 * Funcion que devuelve la version del juego de Travian que esta tratando
	 *
	 * Returns:
	 *      La version del juego
	 */
	function getVersion(){
		find("//script[@type='text/javascript']", XPFirst).src.search(/(\d).js$/);
		//return RegExp.$1; return "3";
	}

	function pc2aldeas(puntos){ return Math.round(Math.pow((puntos / 1000) / 1.6, 1 / 2.3)); }
	function aldeas2pc(aldeas){ return Math.round(1.6 * Math.pow(aldeas, 2.3)) * 1000; }

	/**
	 * Convierte todos los enlaces a la propia pagina del tipo "#" como enlaces vacios de javascript
	 */
	function sanearEnlaces(){
		var a = find("//a[@href='#']", XPList);
		for (var i = 0; i < a.snapshotLength; i++) a.snapshotItem(i).href = 'javascript:void(0)';
	}

	/**
	 * Calcula y muestra el tiempo que ha tardado desde el inicio de ejecucion del script
	 */
	function calcularTiempoEjecucion(){
		var tiempo = new Date().getTime() - tiempo_ejecucion;
		var div = find("//div[@id='ltime']", XPFirst);
		div.appendChild(elem("P", "TB: " + tiempo + " ms"));
	}


	/* Acciones generales a todas las paginas */
	getGeneralData();
	sanearEnlaces();

	/* Acciones especificas para algunas paginas */

	
	if (find("//td[@class='s7 f8']", XPFirst)) {

		var t0 = new Date();
		var arr0Time = get("tp1").innerHTML.split(":");

		t0.setHours(arr0Time[0]);
		t0.setMinutes(arr0Time[1]);
		t0.setSeconds(arr0Time[2]);

		var tFix = new Date();
		tFix.setHours(parseInt(arr0Time[0])+1);
		
		//get the list of the incomming trasports.
		var oTransportTable = new Array();

		// recoge los nombres de cada uno
		xpathResult = find('//div[@id="content"]//tr[@class="cbg1"]//td[@class="s7"]', XPIter);
		while ((oTransportTable[oTransportTable.length] = xpathResult.iterateNext())) {}

		var tmpT = new Date(t0);
		var tmpActualCrop = parseInt(actual[3]);
		var tmpTotal	= parseInt(total[3]);
		var tmpProduction = parseInt(produccion[3]);
//alert(produccion[3]);
		var arrQFix = new Array(24); 	//24h period;
		var ECQFix = 0; 		//estimated Crop Quatity (pe perioade fixe de 1 h)
		for(var i=0;i<24;i++){arrQFix[i]=0}
		arrQFix[0]="-"

		for (i=0;i<oTransportTable.length-1;i++) {
			//celula cu cropu
			var oTCrop = oTransportTable[i].firstChild.childNodes[oTransportTable[i].firstChild.childNodes.length-1];

			//celula cu timpul estimat
			var oTTime = oTransportTable[i].parentNode.previousSibling.previousSibling.childNodes[2].firstChild;

			//celula cu timpul
			var oTimer= get("timer"+(i+1));
			
			// cantitate asteptata
			var qCrop = parseInt(oTCrop.nodeValue);
			//alert(qCrop);
			qCrop = isNaN(qCrop)?0:qCrop;

			// timpul transport
			var tTime = oTTime.nodeValue.split(" ")[1];

			// timpul estimat
			var ti = new Date(t0);
			ti.setSeconds(ti.getSeconds()+calcular_segundos(oTimer.innerHTML));

			// delta t = intervalul de timp dintre t[i-1] si t[i]
			var dt = (ti-tmpT)/1000; //time difference in seconds
			
			// calculeaza crop la timpul estimat! ECQ = Estimated Crop Quantity 
			//ECQ = CurrentCrop+qCrop+(qProductieCrop*dt);
			ECQ = tmpActualCrop+qCrop+(tmpProduction*dt);

			if (ECQ>tmpTotal) {
				sResult = "<font color=green><b>+"+(ECQ-tmpTotal)+"!!!</b></font>"
			}
			else {
				if (ECQ>0){
					sResult = "<font color=green>"+(ECQ)+"</font>"
				}
				else {
					sResult = "<font color=red><b>"+(ECQ)+"!!!</b></font>"
				}
			}


			oSpan = elem("SPAN","  ["+sResult+"]");
			oTTime.parentNode.appendChild(oSpan);

			iHour = Math.floor((ti-t0)/(3600*1000))+1;
			// calculeaza total crop /h

			arrQFix[iHour] += qCrop;
			
			tmpT = ti;
			tmpActualCrop=ECQ;
			if (ECQ>tmpTotal) tmpActualCrop=tmpTotal;
		}

		//build time table
		oRightDiv = get("sright");
		for(var i=0;i<24;i++){
			arrQFix[i] = "<TR><TD>"+i+"</TD><TD align=left nowrap >"+t0.toShortFormat()+"</TD><TD align=right>"+arrQFix[i]+"</TD>";
			t0.setHours(t0.getHours()+1);
			t0.setMinutes(0);
			t0.setSeconds(0);
		}
		oCQTable = elem("TABLE","<TR class='cbg1'><TD></TD><TD>Period</TD><TD>Crop Quantity</TD></TR>"+arrQFix.join("</TR>"));
		//class="tbg" cellspacing="1" cellpadding="2"
		oCQTable.setAttribute("class", "tbg");
		oCQTable.setAttribute("width", "300");
		oCQTable.setAttribute("cellspacing", "1");
		oCQTable.setAttribute("cellpadding", "2");
		oRightDiv.appendChild(oCQTable);
	}
	

	/* Mas acciones generales */ 
	calcularTiempoEjecucion();
	/**/
};

function short_time_format(s){
	if(s > -1){
		var horas = Math.floor(s/3600);
		var minutos = Math.floor(s/60) % 60;
		var t = horas + ":" + ((minutos<=9)?"0":"")+minutos;
	}else{
		var t = "0:0?";
	}
	return t;
}
// GreaseMonkey ejecuta sus scripts en el evento DOMContentLoaded, por lo que se puede ejecutar directamente,
// Opera por el contrario necesita agregar la funcion a dicho evento
window.addEventListener('DOMContentLoaded', funcionPrincipal, false);
funcionPrincipal();

/*
TODO:
	- Solo mostrar ampliaciones de niveles superiores al 10 para la aldea principal
	- Calculo de tiempo de llegada de ataque para coordinar ataques
        - Bloc de notas para cada aldea o jugador?
	- Conseguir enlace a una calculadora multilenguaje
	- Cola de espera (lo ultimo que hare cuando abandone el desarrollo del script, no antes)
	- Traduccion a turco, polaco, rumano y danes

FIXME:
	- El refresco de materiales falla cuando se produce mas de 3600 la hora de un material concreto
	- Comprobar los costes de los niveles de todos los edificios asi como las diferencias entre servidores (http://help.travian.com)
	- Costes para los niveles de las minas a partir del 10
	- Refinar las traducciones en ingles, italiano, aleman, frances, holandes, portugues y ruso
	- Mejorar los apa?os en el codigo marcados por FIXMEs
	- Comentar mas en detalle algunas cosas
*/