NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Eliran Givoni
// @namespace CATool
// @description Given indication about the chances of the user to take the base \ camp in precents
// @version 0.1
// @author Eliran Givoni
// @include http*://prodgame*.alliances.commandandconquer.com/*/index.aspx*
// ==/UserScript==
var chancesOfAttackTool = function()
{
var initCATool = function()
{
var get =
{
cities : ClientLib.Data.MainData.GetInstance().get_Cities(),
allCities : ClientLib.Data.MainData.GetInstance().get_Cities().get_AllCities().d,
world : ClientLib.Data.MainData.GetInstance().get_World(),
alliance : ClientLib.Data.MainData.GetInstance().get_Alliance(),
attackRange : ClientLib.Data.MainData.GetInstance().get_Server().get_MaxAttackDistance() - 0.5,
citryEvents : ["Base", "Camp"],
define :
{
camp : ClientLib.Data.WorldSector.ObjectType.NPCCamp,
base : ClientLib.Data.WorldSector.ObjectType.NPCBase,
isDestroyed : ClientLib.Data.Reports.ENPCCampType.Destroyed,
maxFailed : 20,
maxUnitsDamage : 3 * ( 6 * 7 ),
maxBuildingDestroy : 23,
layoutPrecentText : "Percentage Recommendation attack: %d%"
},
buildings : [ 58, 72, 73, 74 , 75,
77, 78, 79, 116, 117 ],
units_type : [ "AV", "AI", "AA", "NN" ],
defence_unit :
{
178 : [ 0, 0, 2 ],
181 : [ 0, 0, [ 5, 3 ] ],
184 : [ 0, 0, 1 ],
187 : [ 0, 0, 2 ],
191 : [ 0, 0, 0 ],
208 : [ 0, 0, 2 ],
179 : [ 1, 0, 2 ],
182 : [ 1, 0, [ 5, 3 ] ],
185 : [ 1, 0, 1 ],
188 : [ 1, 0, 1 ],
190 : [ 1, 0, 0 ],
210 : [ 1, 0, 2 ],
186 : [ 2, 0, 1 ],
180 : [ 2, 0, 2 ],
183 : [ 2, 0, [ 5, 3 ] ],
189 : [ 2, 0, 2 ],
192 : [ 3, 0, 0 ]
}
};
var regex =
{
allowBaseType : /^(camp|outpost|base|infected)$/i,
followBuildings : /^(74|58)$/
};
var require =
{
runOverKey : 0,
baseFail : {},
basesObj : {},
follow : [],
stopScanning : false,
/**
* @method {integer} getAllianceArmyBonuses(): get the average bonuses of the army
*/
getAllianceArmyBonuses : function() {
return Math.round( ( get.alliance.get_POIAirBonus() + get.alliance.get_POIInfantryBonus() + get.alliance.get_POIVehicleBonus() ) / 3 );
},
/**
* @method {void} getCities(): get all the owner cities
* @param {function} callback: set callback per each owner base
*/
getCities : function( callback )
{
for( var city in get.allCities ) {
if ( get.allCities.hasOwnProperty( city ) ) {
var ownBase = get.allCities[ city ];
if ( ownBase === undefined ) {
throw new Error( "Unable to find own city " + city );
}
callback( ownBase );
}
}
},
/**
* @method {object} getOwnCity(): get current own city
*/
getOwnCity : function() {
return get.cities.get_CurrentOwnCity();
},
/**
* @method {float} getOwnCityOffenseLevel(): get current own city offense level
*/
getOwnCityOffenseLevel : function() {
return parseFloat( this.getOwnCity().get_LvlOffense().toFixed(2) );
},
/**
* @method {array} getBasesInRange(): get bases in the range of the owner bases
* @param {function} callback: set callback per each coord base
*/
getBasesInRange : function( callback ) {
var cacheBases = [];
var bases = [];
this.getCities( function( baseObj ){
var x = baseObj.get_PosX();
var y = baseObj.get_PosY();
var base_level = baseObj.get_LvlBase();
var base_OL = baseObj.get_LvlOffense();
// Skip bases without offense
if ( base_OL === 0 ) {
return false;
}
for ( var scanY = y - 11; scanY <= y + 11; scanY++ ) {
for ( var scanX = x - 11; scanX <= x + 11; scanX++ ) {
var obj, coord = [ scanX, scanY ].join(":");
var rangeX = Math.abs( x - scanX );
var rangeY = Math.abs( y - scanY );
var rangeT = Math.sqrt( ( rangeX * rangeX ) + ( rangeY * rangeY ) );
// Continue if the attack range is invalid
if ( rangeT > get.attackRange || coord in cacheBases ) {
continue;
}
// Get coord object
if ( ! ( obj = require.getObjectFromCoord( scanX, scanY ) ) ) {
continue;
}
obj.ownLevel = base_level;
obj.ownOffenseLevel = base_OL;
cacheBases.push( coord );
bases.push( obj );
}
}
return true;
});
return bases;
},
/**
* @method {boolean} getObjectFromCoord(): get object in world from coord
* @param {int} scanX: coord x
* @param {int} scanY: coord y
*/
getObjectFromCoord : function( scanX, scanY ) {
var obj = get.world.GetObjectFromPosition( scanX, scanY );
if ( obj === null ) {
return false;
}
var objType = obj.Type;
var objCampType = obj.getCampType;
if ( ( get.define.base !== objType && get.define.camp !== objType ) || ( typeof objCampType === "function" && objCampType() === get.define.isDestroyed ) ) {
return false;
}
return obj;
},
/**
* @method {void} runOnEachBaseCoord(): run on each base coord
*/
runOnEachBaseCoord : function()
{
this.runOverKey = 0;
this.stopScanning = false;
this.basesObj = this.getBasesInRange();
this.runOnNextBase();
},
/**
* @method {void} runOnBaseByCoord(): run on base in specific coord
* @param {int} x: coord x
* @param {int} y: coord y
*/
runOnBaseByCoord : function( x, y, c ) {
var obj = this.getObjectFromCoord( x, y );
if ( obj ) {
this.stopScanning = false;
this.runOnNextBase( false, obj, c );
}
},
/**
* @method {void} runOnNextBase(): run on next base in array
* @param {boolean} nextKey: move to next key automaticlly
* @param {function} callback: success callback when the information recieved
*/
runOnNextBase : function( nextKey, base, callback )
{
var scan = null;
if ( nextKey ) {
this.runOverKey++;
}
if ( ! this.stopScanning && ( base = base || ( typeof this.basesObj[ this.runOverKey ] !== "undefined" ? this.basesObj[ this.runOverKey ] : null ) ) )
{
var baseId = base.getID();
get.cities.set_CurrentCityId( baseId );
scan = get.cities.GetCity( baseId );
scan.ownOffenseLevel = base.ownOffenseLevel;
if ( scan === null || scan.GetBuildingsConditionInPercent() === 0 )
{
// Write scan failed
if ( ! require.baseFail.hasOwnProperty( baseId ) ) {
require.baseFail[ baseId ] = 0;
}
require.baseFail[ baseId ]++;
// if the failed attemps pass the maximum
if ( require.baseFail[ baseId ] == get.define.maxFailed ) {
return require.runOnNextBase( true, base, callback );
}
// Try again if scan failed
return setTimeout( function(){
require.runOnNextBase( nextKey, base, callback );
}, 100 );
}
else {
if ( ! regex.allowBaseType.exec( scan.get_Name() ) ) {
return this.runOnNextBase( true, null, callback );
}
return this.cleanNeccVars() && this.scanBaseLayout( scan, callback );
}
}
return true;
},
/**
* @method {void} scanBaseLayout(): get each base layout
* @param {ClientLib.GetCity} scan: get city scan result
* @param {function} callback: success callback when the information recieved
*/
scanBaseLayout : function( scan, callback )
{
var level = scan.get_BaseLevel();
var buildings = scan.get_Buildings().d;
var total_avg = {
buildings : 0,
defences : 0
};
// if the buildings object is empty, return false
if ( Object.keys( buildings ).length > 0 )
{
total_avg.buildings = this.getBaseBuildingsComplexityAverage( buildings );
total_avg.defences = this.getBaseDefenceComplexityAvarege( scan );
total_avg.result = ( total_avg.buildings + total_avg.defences ) / 3;
total_avg.result = total_avg.result < 0 ? 0 : total_avg.result > 100 ? 100 : total_avg.result;
total_avg.result = Math.round( total_avg.result );
callback && callback( total_avg.result, scan );
}
this.runOnNextBase( true, null, callback );
},
/**
* @method {boolean} cleanNeccVars(): clean neccessery vars
*/
cleanNeccVars : function() {
this.follow = [];
return true;
},
/**
* @method {int} getBaseDefenceComplexityAvarege(): get the complexity of the base defence in average
* @param {ClientLib.Data} base: the scanned base
*/
getBaseDefenceComplexityAvarege : function( base )
{
var defences = base.get_DefenseUnits().d;
var average = 0;
var defenceLevel = 0;
for( var i in defences ) {
var unit = defences[i];
// Update defence level sum
defenceLevel += unit.get_CurrentLevel();
// set unit details
var d = {
t : unit.get_MdbUnitId(),
x : unit.get_CoordX(),
y : unit.get_CoordY()
};
// get unit info
var i = get.defence_unit[d.t];
// don't support upgrade units
if ( i == undefined ) {
continue;
}
// Scan unit possible area
var o = "xy";
var s = { x : [], y :[] }
if ( typeof i[2] === "number" ) {
i[2] = [ i[2], 0 ];
}
s.y[0] = d.y - i[2][0];
s.y[1] = d.y + i[2][0];
s.x[0] = d.x - i[2][0];
s.x[1] = d.x + i[2][0];
for( var c = 0; c <= 1; c++ ) {
var e = o[c];
var z = s[e];
z = z.map( function(v, k){
return v < 0 ? 0 : v > 8 ? 8 : v;
});
for( var f in require.follow ) {
var v = require.follow[f];
var r = v[e];
if ( r >= z[0] && r <= z[1] ) {
var a = Math.abs( z[1] - z[0] ) + 1;
if ( i[2][1] > 0 ) {
a -= ( i[2][1] * 2 ) - 1;
}
v.pu += a;
}
}
}
}
// Get the average of the defence level
defenceLevel /= Object.keys( defences ).length;
// Set the offense level with alliance bonuses
var offenseLevelIncludeBonuses = this.getOwnCityOffenseLevel() + ( this.getAllianceArmyBonuses() / 100 );
// Set the level range between own offense and defence in precent
var levelRangePrecent = Math.abs( parseFloat( ( ( offenseLevelIncludeBonuses / defenceLevel ) * 100 ).toFixed(2) ) );
// Set the level range between own offense and defence
var levelRange = parseFloat( ( defenceLevel - offenseLevelIncludeBonuses ).toFixed(2) );
// Get the avarage of the under bases
for( var p in this.follow ) {
var pu = this.follow[p].pu;
average += pu;
}
// Calc the average of the base defence
average = ( average / this.follow.length ) + levelRange;
average = average <= 0 ? 1 : average;
// Return the average of the unit defence and offense
return Math.abs( Math.round( ( average / get.define.maxUnitsDamage ) * 100 ) - 100 ) + levelRangePrecent;
},
/**
* @method {int} getBaseBuildingsComplexityAverage(): get the complexity of the base buildings in average
* @param {ClientLib.Data} buildings: the buildings of the current scan object
*/
getBaseBuildingsComplexityAverage : function( buildings )
{
var structure = [];
var average = 0;
// Store the buildings coord and ID in array
// Set important follow buildings in due array
for( var i in buildings )
{
var struc = buildings[i];
var type = struc.get_Type();
var obj = { type : type,
x : struc.get_CoordX(),
y : struc.get_CoordY() };
if ( regex.followBuildings.test( type ) )
{
obj.ub = 0;
obj.pu = 0;
obj.min = obj.x > 0 ? obj.x - 1 : 0;
obj.max = obj.y < 8 ? obj.y + 1 : 8;
this.follow.push( obj );
continue;
}
structure.push( obj );
}
// Check the position of the follow buildings compare to original buildings
for( var b in structure ) {
var i = structure[b];
for( var f in this.follow ) {
var fi = this.follow[f];
if ( i.y >= fi.y && ! ( i.x > ( fi.x + 1 ) || i.x < ( fi.x - 1 ) ) ) {
fi.ub++;
}
}
}
// Get the avarage of the units
for( var u in this.follow ) {
var ub = this.follow[u].ub;
average += Math.abs( ( ub > 0 ? Math.round( ( ub / get.define.maxBuildingDestroy ) * 100 ) : 0 ) - 100 );
}
return Math.round( average / this.follow.length );
},
/**
* @method {void} getBaseManager(): get base manager
* @param {int} scanX: coord x
* @param {int} scanY: coord y
*/
getBaseManager : function(x, y) {
var getBase = this.getObjectFromCoord(x, y);
if ( ! getBase ) {
return false;
}
return get.cities.GetCity( getBase.getID() );
},
/**
* @method {void} stopScanningProgress(): stop scanning loop
*/
stopScanningProgress : function() {
this.stopScanning = true;
}
}
// Create city clicks events
var _layoutGrid = null;
var _webFront = null;
var _label = null;
var _cache = {};
get.citryEvents.forEach( function( value )
{
var regStatus = "RegionNPC" + value + "StatusInfo";
var optValue = "__OPTM_" + value;
var region = webfrontend.gui.region[ regStatus ].prototype;
if( ! region[ optValue ] ) {
region[ optValue ] = region.onCitiesChange;
}
region.onCitiesChange = function()
{
try
{
var selectedCity = ClientLib.Vis.VisMain.GetInstance().get_SelectedObject();
if ( selectedCity !== null )
{
// save the city ID
var cityID = selectedCity.get_Id();
// Remove exists grid
if ( _layoutGrid !== null && _webFront !== null ) {
_webFront.remove( _layoutGrid );
_layoutGrid.removeAll();
}
// Prepend result to the screen
var putResultLayer = function( result )
{
var color;
if ( result >= 80 ) {
color = "rgb(67, 224, 58)";
}
else if ( result < 80 && result > 70 ) {
color = "rgb(220, 224, 58)";
}
else {
color = "rgb(224, 58, 58)";
}
// Add layer layout
_webFront = webfrontend.gui.region[ regStatus ].getInstance();
_label = new qx.ui.basic.Label().set({ value: get.define.layoutPrecentText.replace( "%d", result ) });
_layoutGrid = new qx.ui.container.Composite( new qx.ui.layout.Grid(5, 5) );
_layoutGrid.setTextColor( color );
_layoutGrid.setFont("bold");
_layoutGrid.setMarginTop(-3);
_layoutGrid.setMarginBottom(-10);
_webFront.addAfter( _layoutGrid, _webFront.getChildren()[10] );
_layoutGrid.add( _label, {
row : 1,
column : 0,
colSpan: 0
});
};
// Wait for base info
var timeout_clear = 0;
var loadCityResult = function()
{
var getCity = ClientLib.Data.MainData.GetInstance().get_Cities().GetCity( cityID );
// Wait for details response
if ( typeof getCity === "object" && typeof getCity.get_CoordId !== "undefined" && getCity.get_CoordId() > -1 )
{
// Run on the current base coord
require.runOnBaseByCoord( getCity.get_X(), getCity.get_Y(), function( result, base )
{
console.log("Standart");
// Put the result layer in the top position of the base info
putResultLayer( result );
// Save base in cache
_cache[ cityID ] = result;
});
}
// Try again if failed
else if( timeout_clear < 20 ) {
timeout_clear++;
setTimeout( loadCityResult, 100 );
}
};
// Check old cache array
if ( _cache.hasOwnProperty( cityID ) && typeof _cache[ cityID ] === "number" ) {
putResultLayer( _cache[ cityID ] );
}
else {
setTimeout( loadCityResult, 100 );
}
}
}
catch (e) {}
return this[ optValue ]();
};
});
};
var waitToInterface = function() {
try {
if ( typeof qx != 'undefined' && qx.core.Init.getApplication() ) {
initCATool();
}
else {
setTimeout( waitToInterface, 1000 );
}
} catch (e) {
console.log("CATool: ", e);
}
};
setTimeout( waitToInterface, 1000 );
};
try {
var appendScript;
appendScript = document.createElement("script");
appendScript.innerHTML = "(" + chancesOfAttackTool.toString() + ")()";
appendScript.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild( appendScript );
} catch (e) {
console.log("CATool: ", e);
}