World settings - buildings troops and config

Coordinator
Apr 6, 2011 at 9:15 PM

Just grabbed some useful bits off tribalwars.nat forum - I remember somewhere seeing a way to get an xml list of all the worlds on a server - but cannot find it now...

 

[QUOTE=cheesasaurus;2784454]for speed 1, on .net servers:[url=http://www.twstats.com/en5/index.php?page=buildings]buildings[/url][url=http://www.twstats.com/en5/index.php?page=units]units[/url]
the base build time is divided by the world speed.
[i]time it takes for a specific-level building to be constructed, with a 100% time factor[/i][code]duration = [base build time]*[build time factor]^(next level - 1)[/code]
To get the actual build time of a building, multiply it by the time factor of the village headquarters when the construction begins.[code]time factor = 1.05^(-(level of the village headquarters))[/code]A level 1 village headquarters has a time factor of about 0.952381, or 95.2381%
To get the actual build time of a unit, multiply the "base build time" by the time factor of the building that makes it, at the time the creation begins. The time factors of the the barracks, stable, workshop, and academy are all the same.[code]time factor = 2/3*1.06^(-building level)[/code]A level 24 barracks has a time factor of about 0.164652, or 16.4652%A level 25 barracks has a time factor of about 0.155332, or 15.5332%
You can retrieve the building and unit information for different servers using the interface php.[code]interface.php?func=get_building_infointerface.php?func=get_unit_info[/code][/QUOTE]

 

[QUOTE=SlowTarget;2189595][code]javascript:function MyGetConfig(){var oRequest=new XMLHttpRequest();var sURL="http://"+window.location.hostname+"/interface.php?func=get_config";oRequest.open("GET",sURL,0);oRequest.send(null);if(oRequest.status==200)return oRequest.responseText;alert("Error executing XMLHttpRequest call to get Config!");}alert(MyGetConfig());[/code]
This doesn't do much - just pops up an alert with the server config... I thought it might be handy for all those farming/timing scripts...[/QUOTE]

 

[QUOTE=DaMouse404;4313618]Small update do this, I wrote the following segment of code to utilise the new DOM Storage stuff so that we don't spam TW with the large amount of scripts using world config:
[code]var config = (function(){ var storage = ( typeof localStorage != "undefined" ); var out = ''; if ( storage && localStorage["worldconfig"] != null ) { out = localStorage["worldconfig"]; } else { var oRequest=new XMLHttpRequest(); var sURL="http://"+window.location.hostname+"/interface.php?func=get_config"; oRequest.open("GET",sURL,0); oRequest.send(null);
if(oRequest.status==200) { out = oRequest.responseText; if ( storage ) { localStorage["worldconfig"] = out; } } } if ( out !== '' ) { out = new DOMParser().parseFromString(out, "text/xml") return function(config_var) { return out.getElementsByTagName(config_var)[0].childNodes[0].nodeValue } } alert("Error getting config!");q();})();[/code]
It will still make a request per use on Opera because of the lack of any kind of modern features as well as Chrome which seems to prefer the Gears DB API.
It's a quick fix to a small problem and should have a side effect of improving responsiveness minorly. 
To use:[code]if(config("knight")==0){ alert("No Pallys");}[/code]
-DaMouse[/QUOTE]

Coordinator
Apr 7, 2011 at 3:25 AM

What do you think about have a scripting template?

I've started to adopt something like below.

Writing a script would involve:

  1. Copying the Template
  2. Search/Replace the word TEMPLATE with your script NameSpace (eg. OverviewSorter)
  3. Customize the Script Name, Version, Author, etc...
  4. Add your code to the runScript function
  5. Defining any required config values

NOTE: at some point, it will be useful to cache the worldConfig,unitConfig,buildingConfig in LocalStorage for improved performance.

 

/* 
	Author	: Dale McKay
	Email	: dalesmckay@gmail.com
	Credit	: based on SlowTargets original Sorting Script.
	
	TODO	:
	
	NOTES	:
		* Sample Client-side script launcher:
		javascript:var vScript={URLs:['http://crosstrigger.com/tw/v7/TEMPLATE.js','http://taktimer.net/scripts/dales/TEMPLATE.js'],id:'fnTEMPLATE',config:{},action:function(){win[vScript.id](vScript.config);},runOnce:true};var win=(window.frames.length>0)?window.main:window;win.$(win.document).ready(function(){var isLoaded=false;var ii=0;function fnEmbedScript(){if(isLoaded||(ii>=vScript.URLs.length)){return;}win.$.getScript(vScript.URLs[ii]+'?'+Math.round(Math.random()*1000000),function(){if(!isLoaded){isLoaded=true;win.setTimeout(function(a,b){vScript.action();},200);}});ii++;if(!isLoaded){win.setTimeout(function(a,b){fnEmbedScript();},3000);}}if(win[vScript.id]){if(!vScript.runOnce){vScript.action();}}else{fnEmbedScript();}});void(0);
____________________________________________________________

Copyright (C) 2011 Dale McKay, all rights reserved
version 1.0, 6 February 2011

This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
This notice may not be removed or altered from any source distribution.
____________________________________________________________
*/

function fnTEMPLATE(config){
	var theScript=new TEMPLATE(config);
	theScript.execute();
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Class: TEMPLATE                                                          */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* This is basically a NameSpace to prevent cluttering the window variable  */
function TEMPLATE(config){
	try{
		var myself=this;

		/* ************** */
		/* CUSTOMIZE THIS */
		/* ************** */
		myself.name='Script Name';
		myself.version=1.00;
		myself.minGameVersion=7.00;
		myself.author={
			name:'dalesmckay',
			email:'dalesmckay@gmail.com'
		};
		myself.debugEnabled=(typeof(config)!='undefined')&&(typeof(config.debugEnabled!='undefined')&&config.debugEnabled;
		/* ************** */

		myself.debugID = myself.name.replace(/\s/g,'');
		myself.win=(window.frames.length>0)?window.main:window;
		
		/* Shallow Copy */
		myself.config=config;
		
		myself.setup();
		
		return myself;
	}
	catch(objError){
		this.handleError(objError);
		throw('');
	}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.runScript(){
	var myself=this;

	/* >>> YOUR IMPLEMENTATION HERE <<< */


} /* TEMPLATE::runScript */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.handleError=function(objError){
	/* >>> CUSTOM HANDLING CAN OVERRIDE THE FOLLOWING <<< */

	var errMsg=String(objError.message||objError||'');
	if(errMsg){
		myself.print('Error: ' + errMsg);
		alert('Error: ' + errMsg);
	}
}; /* TEMPLATE::handleError */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.print=function(msg,id){
	var myself=this;
	if(myself.win.$('#' + myself.debugID).length <= 0){
		myself.win.$('body').append('<div id="' + myself.debugID + '"></div>');
	}
	
	myself.win.$('#' + myself.debugID).append('<span id="'+((typeof(id)=='undefined')?'':id)+'">'+msg+'</span><br/>');
}; /* TEMPLATE::print */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.debug=function(msg){
	var myself=this;
	if((typeof(myself.debugEnabled) != 'undefined') && myself.debugEnabled){
		myself.print(msg);
	}
}; /* TEMPLATE::debug */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

/* TODO: possibly add a maxVersion parameter */
TEMPLATE.prototype.hasMinVersion=function(minVersion){
	return (myself.gameVersion >= minVersion);
}; /* TEMPLATE::hasMinVersion */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.ajax=function(url,method,params,type,isAsync){
	var myself=this;

	var error = null;
	var payload = null;

	myself.win.$.ajax({
		'async':isAsync,
		'url':url,
		'data':params,
		'dataType':type,
		'type':String(method||'GET').toUpperCase(),
		'error':function(req,status,err){error='ajax: ' + status;},
		'success':function(data,status,req){payload=data;}
	});

	if(error){
		throw(error);
	}

	return payload;
}; /* TEMPLATE::ajax */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.printVersion=function(){
	var myself=this;

	myself.print('<span style="color:red;">Provide the following info when asking for help:</span>');
	myself.print("=========================");
	myself.print(myself.author.name + "'s " + myself.name + ": v" + myself.version.toFixed(2));
	myself.print("=========================");
	myself.print("Premium: "+(myself.isPremium?"yes":"no"));
	myself.print("Church : "+(myself.hasChurch?"yes":"no"));
	myself.print("Statue : "+(myself.hasPaladin?"yes":"no"));
	myself.print("Archer : "+(myself.hasArchers?"yes":"no"));
	myself.print("Militia: "+(myself.hasMilitia?"yes":"no"));
	myself.print("Notes  : "+(myself.hasVillageNotes?"yes":"no"));
	myself.print("Sitter : "+(myself.win.location.href.match(/t\=\d+/i)?"yes":"no"));
	myself.print("=========================");
	myself.print("Version: "+myself.win.game_data.version);
	myself.print("World  : "+myself.win.game_data.world);
	myself.print("Screen : "+myself.win.game_data.screen);
	myself.print("Mode   : "+myself.win.game_data.mode);
	myself.print("URL    : "+myself.win.location.href);
	myself.print("Browser: "+navigator.userAgent);
	myself.print("=========================");
	
	return true;
}; /* TEMPLATE::printVersion */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.setup=function(){
	var myself=this;
	myself.debug('CALL: TEMPLATE::setup');

	/* HACK: fix null mode */
	if(!myself.win.game_data.mode){
		var vmode=myself.win.$('#overview_menu td[class="selected"] a').attr('href');
		vmode=vmode?vmode.match(/mode\=(\w*)/i):null;
		myself.win.game_data.mode=vmode?vmode[1]:null;
	}
	
	myself.gameVersion = myself.win.game_data.version.match(/[\d|\.]+/g);
	myself.gameVersion = (myself.gameVersion?parseFloat(myself.gameVersion[1]):-1);
		
	myself.worldConfig = myself.win.$(myself.ajax('/interface.php','GET',{'func':'get_config'},'xml',false)).find('config');
	myself.unitConfig = myself.win.$(myself.ajax('/interface.php','GET',{'func':'get_unit_info'},'xml',false)).find('config');
	myself.buildingConfig = myself.win.$(myself.ajax('/interface.php','GET',{'func':'get_building_info'},'xml',false)).find('config');

	myself.isPremium = (myself.win.$('#quickbar_outer').length>0);
	myself.hasChurch = (parseInt(myself.worldConfig.find('game church').text()||'0',10)>0);
	myself.hasPaladin = (parseInt(myself.worldConfig.find('game knight').text()||'0',10)>0);
	myself.hasArchers = (parseInt(myself.worldConfig.find('game archer').text()||'0',10)>0);
	myself.hasMilitia = (myself.unitConfig.find('militia').length>0);
	myself.hasVillageNotes = (myself.win.$('[src*="note.png"],[class*="note-icon"]').length>0);
	/* myself.isMobile = */
		
	myself.printVersion();

	if(!myself.hasMinVersion(myself.minGameVersion)){
		throw('This script requires v'+myself.minGameVersion.toFixed(2)+' or higher.\nYou are running: v'+myself.gameVersion.toFixed(2));
	}
}; /* TEMPLATE::setup */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.execute=function(){
	try{
		var myself=this;
		myself.debug('CALL: TEMPLATE::execute');

		if(myself.win.$('#' + myself.debugID + '_done').length > 0){
			throw('This script has already been executed');
		}

		myself.runScript();
		
		myself.print('<br/>execution completed',myself.debugID + '_done');
	}
	catch(objError){
		this.handleError(objError);
	}
}; /* TEMPLATE::execute */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

TEMPLATE.prototype.asString=function(AObj){
	function fnAsString(object,depth,max){
		depth=depth||0;
		max=max||10;

		if(depth>max){
			return false;
		}

		var indent="";
		for(var ii=0; ii<depth; ii++){
			indent+="\t";
		}

		var output="";  
		for(var key in object){
			output+="\n"+indent+key+": ";
			switch(typeof object[key]){
				case "object":output+=fnAsString(object[key],depth+1,max);break;
				/* case "function":output+="function";break; */
				default:output+=object[key];break;        
			}
		}

		return output;
	}

	return fnAsString(AObj||this);
}; /* TEMPLATE::asString */
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

 




        
    
Coordinator
Apr 7, 2011 at 4:48 AM

http://forum.tribalwars.us/showthread.php?t=389

http://forum.tribalwars.net/showpost.php?p=3172119&postcount=10

http://forum.die-staemme.de/showthread.php?t=71112

http://forum.tribalwars.co.uk/showthread.php?t=6760

 

.NET Server List (not sure how to get list of all International Servers though - maybe from TWStats or Innogames sites for now)

http://www.tribalwars.net/backend/get_servers.php

http://www.tribalwars.net/backend/get_servers_xml.php

http://www.tribalwars.net/backend/get_servers_csv.php

Coordinator
Apr 7, 2011 at 10:16 PM

Here's 2 magical Classes.

 

CacheProvider is used for Caching Javascript values (including JSON Objects).

If LocalStorage is not available, it will simply use an in memory JSON Object for caching instead as a fallback.

Obviously, the fallback will not persist across different script sessions.

 

TWConfig is used to access the world,units,buildings XML configuration data.

It will first check LocalStorage to see if the data is available. If not, it will download from TW and cache in localStorage.

javascript:

/* Cache Class - uses an in-memory object if LocalStorage is not available */
function CacheProvider(useLocalStorageIfAvailable){
	var myself=this;

	myself._cache={};
	myself._useLocalStorage=((typeof(useLocalStorageIfAvailable)=='undefined')||useLocalStorageIfAvailable)&&CacheProvider.hasLocalStorage;
	
	return{
		fetch:function(key){
			return (myself._useLocalStorage?JSON.parse(localStorage.getItem(key)):myself._cache[key])||undefined;
		},
		
		store:function(key,value){
			if(myself._useLocalStorage){
				try{
					localStorage.setItem(key,JSON.stringify(value));
				}
				catch(objError){
					if(objError.name=='QUOTA_EXCEEDED_ERR'){
						throw new Exception(value);
					}
				}
			}
			else{
				myself._cache[key]=value;
			}

			return value;
		},

		clear:function(key){
			if(myself._useLocalStorage){
				localStorage.removeItem(key);
			}
			else{
				delete myself._cache[key];
			}
		}
	};
}

try{
	CacheProvider.hasLocalStorage=('localStorage' in window)&&(window['localStorage']!==null);
}
catch(objError){
	CacheProvider.hasLocalStorage = false;
}

/* Tribal Wars Configuration Class */
function TWConfig(cache){
	var myself=this;	
	cache=cache||(new CacheProvider());
	var $=(window.main||window).$;
	
	function xmlToStr(xml){
		return (window.ActiveXObject)?(xml?xml.xml:''):(new XMLSerializer()).serializeToString(xml);
	}

	function fnAjax(url,method,params,type){
		var error = null;
		var payload = null;

		$.ajax({
			'async':false,
			'url':url,
			'data':params,
			'dataType':type,
			'type':String(method||'GET').toUpperCase(),
			'error':function(req,status,err){error='ajax: ' + status;},
			'success':function(data,status,req){payload=data;}
		});

		if(error){
			throw(error);
		}

		return payload;
	}
	
	myself.worldConfig=$(cache.fetch('worldConfig')||cache.store('worldConfig',xmlToStr(fnAjax('/interface.php','GET',{'func':'get_config'},'xml',false)))).filter('config');
	myself.unitConfig=$(cache.fetch('unitConfig')||cache.store('unitConfig',xmlToStr(fnAjax('/interface.php','GET',{'func':'get_unit_info'},'xml',false)))).filter('config');
	myself.buildingConfig=$(cache.fetch('buildingConfig')||cache.store('buildingConfig',xmlToStr(fnAjax('/interface.php','GET',{'func':'get_building_info'},'xml',false)))).filter('config');
	

	return myself;
}



/* Example Usage */
var twconfig=new TWConfig();
alert(twconfig.unitConfig.find('spear wood').text());
void(0);

Developer
Apr 10, 2011 at 12:54 AM
Edited Apr 10, 2011 at 12:57 AM

I think implementing the template would be difficult and nerve-wracking at first, but would quickly make the scripts more uniform, and scripting/debugging much easier.

Just my two cents ^.^

Coordinator
Apr 10, 2011 at 12:18 PM
Edited Apr 10, 2011 at 12:27 PM

The framework is evolving... I've come up with a much simpler Template.


javascript:

function TEMPLATE(twsfw){
	var myself=this;
	
	myself.name='TEMPLATE';
	myself.version=1.00;
	myself.minGameVersion=7.00;
	myself.author={
		'name':'YOUR_SCRIPTING_IDENTITY',
		'email':'YOUR_EMAIL_ADDRESS'
	};
	myself.debugEnabled=true;
	
	myself.execute(twsfw);
}

TEMPLATE.prototype.execute=function(twsfw){
	/* >>> WRITE YOUR SCRIPT CODE HERE <<< */

	/* eg. 
		alert('spear.wood='+twsfw.unitConfig.find('spear wood').text());
	*/
};

(function(){
	var win=(window.main||window);
	var framework={
		'id':'fnTWScriptingFramework',
		'root':'http://dl.dropbox.com/u/25377948/twscripts',
		'branch':'development',
		'name':'twsfw.js',
		
		'execute':function(){win[this.id]({
			'branch':[this.root,this.branch,''].join('/'),
			'class':TEMPLATE
		});},
		'runOnce':true
	};
	
	if(!win[framework.id]){
		win.$.getScript(
			[framework.root,framework.branch,framework.name].join('/'),
			function(){
				framework.execute();
			}
		);
	}
	else if(!framework.runOnce){
		framework.execute();
	}
})();

void(0);