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); }