eliran123456 / Eliran Givoni

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