// ==UserScript== // @author DanielOnDiordna // @name IITC plugin: Add U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ Machina Layers // @category Misc // @version 1.3.0.20221220.001100 // @updateURL https://softspot.nl/ingress/plugins/iitc-plugin-addmachinalayers.meta.js // @downloadURL https://softspot.nl/ingress/plugins/iitc-plugin-addmachinalayers.user.js // @description [danielondiordna-1.3.0.20221220.001100] Add U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ Machina Layers to older versions of IITC (before version 0.34.0), including Portals list and Portal count plugin support // @id iitc-plugin-addmachinalayers@danielondiordna // @namespace https://softspot.nl/ingress/ // @match https://intel.ingress.com/* // @grant none // ==/UserScript== function wrapper(plugin_info) { // ensure plugin framework is there, even if iitc is not yet loaded if(typeof window.plugin !== 'function') window.plugin = function() {}; // use own namespace for plugin window.plugin.addmachinalayers = function() {}; var self = window.plugin.addmachinalayers; self.id = 'addmachinalayers'; self.title = 'Add U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ Machina Layers'; self.version = '1.3.0.20221220.001100'; self.author = 'DanielOnDiordna'; self.changelog = ` Changelog: version 1.3.0.20221220.001100 - added LINK_RANGE_MAC and getPortalRange support from IITC-CE 0.34.1 to show possible link ranges for Machina portals - fixed support for IITC-CE 0.33.0 release version 1.2.0.20221219.162900 - added TEAM_CODENAMES from IITC-CE 0.34.1 which is used for teamStringToId version 1.1.0.20221218.225400 - added Portals list Team column name - added Portal count plugin support by adding a U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ Machina count version 1.0.0.20221217.205100 - first release - tested on IITC-me 0.26.0.20170108.21732 - tested on IITC-CE 0.30 release - tested on IITC-CE 0.32.1 release - tested on IITC-CE 0.33.0 release - tested on Android and iOS IITC mobile app - disabled for IITC-CE 0.34.0 which already has U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ Machina Layers - added Portals list plugin support by adding a U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ Machina filter `; self.namespace = 'window.plugin.' + self.id + '.'; self.pluginname = 'plugin-' + self.id; function createDefaultBaseMapLayersIITCme() { // copied from version IITC-me 0.26.0.20170108.21732 var baseLayers = {}; //OpenStreetMap attribution - required by several of the layers // osmAttribution = 'Map data © OpenStreetMap contributors'; // MapQuest - http://developer.mapquest.com/web/products/open/map // now requires an API key //var mqSubdomains = [ 'otile1','otile2', 'otile3', 'otile4' ]; //var mqTileUrlPrefix = window.location.protocol !== 'https:' ? 'http://{s}.mqcdn.com' : 'https://{s}-s.mqcdn.com'; //var mqMapOpt = {attribution: osmAttribution+', Tiles Courtesy of MapQuest', maxNativeZoom: 18, maxZoom: 21, subdomains: mqSubdomains}; //baseLayers['MapQuest OSM'] = new L.TileLayer(mqTileUrlPrefix+'/tiles/1.0.0/map/{z}/{x}/{y}.jpg',mqMapOpt); // MapBox - https://www.mapbox.com/api-documentation/ // Access MapBox via the GNOME Project proxy. // In the future, this URL will provide improved tiles from the GNOME Project with localized labels. var gnomeStreetUrl = 'https://gis.gnome.org/tiles/street/v1/{z}/{x}/{y}'; var gnomeAerialUrl = 'https://gis.gnome.org/tiles/satellite/v1/{z}/{x}/{y}'; baseLayers['MapBox Street'] = window.L.tileLayer(gnomeStreetUrl); baseLayers['MapBox Satellite'] = window.L.tileLayer(gnomeAerialUrl); // cartodb has some nice tiles too - both dark and light subtle maps - http://cartodb.com/basemaps/ // (not available over https though - not on the right domain name anyway) var cartoAttr = '© OpenStreetMap contributors, © CartoDB'; var cartoUrl = 'https://{s}.basemaps.cartocdn.com/{theme}/{z}/{x}/{y}.png'; baseLayers['CartoDB Dark Matter'] = window.L.tileLayer(cartoUrl,{attribution:cartoAttr,theme:'dark_all'}); baseLayers['CartoDB Positron'] = window.L.tileLayer(cartoUrl,{attribution:cartoAttr,theme:'light_all'}); // we'll include google maps too - in the ingress default style, and a few other standard ones // as the stock intel map already uses the googme maps API, we just hijack their inclusion of the javascript and API key :) var ingressGMapOptions = { backgroundColor: '#0e3d4e', //or #dddddd ? - that's the Google tile layer default styles: [ { featureType:"all", elementType:"all", stylers: [{visibility:"on"}, {hue:"#131c1c"}, {saturation:"-50"}, {invert_lightness:true}] }, { featureType:"water", elementType:"all", stylers: [{visibility:"on"}, {hue:"#005eff"}, {invert_lightness:true}] }, { featureType:"poi", stylers:[{visibility:"off"}]}, { featureType:"transit", elementType:"all", stylers:[{visibility:"off"}] } ] }; baseLayers['Google Default Ingress Map'] = new window.L.Google('ROADMAP',{maxZoom:21, mapOptions:ingressGMapOptions}); baseLayers['Google Roads'] = new window.L.Google('ROADMAP',{maxZoom:21}); baseLayers['Google Satellite'] = new window.L.Google('SATELLITE',{maxZoom:21}); baseLayers['Google Hybrid'] = new window.L.Google('HYBRID',{maxZoom:21}); baseLayers['Google Terrain'] = new window.L.Google('TERRAIN',{maxZoom:15}); return baseLayers; } self.createDefaultBaseMapLayers = function() { if (!window.L?.gridLayer?.googleMutant) return createDefaultBaseMapLayersIITCme(); var baseLayers = {}; /* // OpenStreetMap attribution - required by several of the layers osmAttribution = 'Map data © OpenStreetMap contributors'; // MapQuest - http://developer.mapquest.com/web/products/open/map // now requires an API key var mqSubdomains = [ 'otile1', 'otile2', 'otile3', 'otile4' ]; var mqTileUrlPrefix = window.location.protocol !== 'https:' ? 'http://{s}.mqcdn.com' : 'https://{s}-s.mqcdn.com'; var mqMapOpt = {attribution: osmAttribution+', Tiles Courtesy of MapQuest', maxNativeZoom: 18, maxZoom: 21, subdomains: mqSubdomains}; baseLayers['MapQuest OSM'] = new L.TileLayer(mqTileUrlPrefix+'/tiles/1.0.0/map/{z}/{x}/{y}.jpg', mqMapOpt); */ // cartodb has some nice tiles too - both dark and light subtle maps - http://cartodb.com/basemaps/ // (not available over https though - not on the right domain name anyway) var cartoAttr = '© OpenStreetMap contributors, © CartoDB'; var cartoUrl = 'https://{s}.basemaps.cartocdn.com/{theme}/{z}/{x}/{y}.png'; baseLayers['CartoDB Dark Matter'] = window.L.tileLayer(cartoUrl, {attribution: cartoAttr, theme: 'dark_all'}); baseLayers['CartoDB Positron'] = window.L.tileLayer(cartoUrl, {attribution: cartoAttr, theme: 'light_all'}); // Google Maps - including ingress default (using the stock-intel API-key) baseLayers['Google Default Ingress Map'] = window.L.gridLayer.googleMutant( { type: 'roadmap', backgroundColor: '#0e3d4e', styles: [ { featureType: 'all', elementType: 'all', stylers: [{visibility: 'on'}, {hue: '#131c1c'}, {saturation: '-50'}, {invert_lightness: true}] }, { featureType: 'water', elementType: 'all', stylers: [{visibility: 'on'}, {hue: '#005eff'}, {invert_lightness: true}] }, { featureType: 'poi', stylers: [{visibility: 'off'}] }, { featureType: 'transit', elementType: 'all', stylers: [{visibility: 'off'}] }, { featureType: 'road', elementType: 'labels.icon', stylers: [{invert_lightness: !0}] } ], }); baseLayers['Google Roads'] = window.L.gridLayer.googleMutant({type: 'roadmap'}); var trafficMutant = window.L.gridLayer.googleMutant({type: 'roadmap'}); trafficMutant.addGoogleLayer('TrafficLayer'); baseLayers['Google Roads + Traffic'] = trafficMutant; baseLayers['Google Satellite'] = window.L.gridLayer.googleMutant({type: 'satellite'}); baseLayers['Google Hybrid'] = window.L.gridLayer.googleMutant({type: 'hybrid'}); baseLayers['Google Terrain'] = window.L.gridLayer.googleMutant({type: 'terrain'}); return baseLayers; } self.setupCRS = function() { // copied from version IITC-CE 0.33.0 // use the earth radius value from s2 geometry library // https://github.com/google/s2-geometry-library-java/blob/c28f287b996c0cedc5516a0426fbd49f6c9611ec/src/com/google/common/geometry/S2LatLng.java#L31 var EARTH_RADIUS_METERS = 6367000.0; // distance calculations with that constant are a little closer to values observable in Ingress client. // difference is: // - ~0.06% when using LatLng.distanceTo() (R is 6371 vs 6367) // - ~0.17% when using Map.distance() / CRS.destance() (R is 6378.137 vs 6367) // (Yes, Leaflet is not consistent here, e.g. see https://github.com/Leaflet/Leaflet/pull/6928) // this affects LatLng.distanceTo(), which is currently used in most iitc plugins window.L.CRS.Earth.R = EARTH_RADIUS_METERS; // this affects Map.distance(), which is known to be used in draw-tools var SphericalMercator = window.L.Projection.SphericalMercator; SphericalMercator.S2 = window.L.extend({}, SphericalMercator, { R: EARTH_RADIUS_METERS, bounds: (function () { var d = EARTH_RADIUS_METERS * Math.PI; return window.L.bounds([-d, -d], [d, d]); })() }); window.L.CRS.S2 = window.L.extend({}, window.L.CRS.Earth, { code: 'Ingress', projection: SphericalMercator.S2, transformation: (function () { var scale = 0.5 / (Math.PI * SphericalMercator.S2.R); return window.L.transformation(scale, 0.5, -scale, 0.5); }()) }); }; function normLL (lat, lng, zoom) { // copied from version IITC-CE 0.33.0 return { center: [ parseFloat(lat) || 0, parseFloat(lng) || 0 ], zoom: parseInt(zoom) || window.DEFAULT_ZOOM }; } // retrieves the last shown position from URL or from a cookie self.getPosition = function() { // copied from version IITC-CE 0.33.0 var url = window.getURLParam; var zoom = url('z'); var latE6 = url('latE6'); var lngE6 = url('lngE6'); if (latE6 && lngE6) { //log.log('mappos: reading email URL params'); return normLL(parseInt(latE6)/1E6, parseInt(lngE6)/1E6, zoom); } var ll = url('ll') || url('pll'); if (ll) { //log.log('mappos: reading stock Intel URL params'); ll = ll.split(','); return normLL(ll[0], ll[1], zoom); } var lat = window.readCookie('ingress.intelmap.lat'); var lng = window.readCookie('ingress.intelmap.lng'); if (lat && lng) { //log.log('mappos: reading cookies'); return normLL(lat, lng, window.readCookie('ingress.intelmap.zoom')); } }; function createFactionLayersArray() { // copied from version IITC-CE 0.34.0 return window.TEAM_NAMES.map(() => window.L.layerGroup()); } self.createDefaultOverlays = function() { // copied from version IITC-CE 0.34.0 /* global portalsFactionLayers: true, linksFactionLayers: true, fieldsFactionLayers: true -- eslint*/ /* eslint-disable dot-notation */ var addLayers = {}; window.portalsFactionLayers = []; var portalsLayers = []; for (var i = 0; i <= 8; i++) { portalsFactionLayers[i] = createFactionLayersArray(); portalsLayers[i] = window.L.layerGroup(); var t = (i === 0 ? 'Unclaimed/Placeholder' : 'Level ' + i) + ' Portals'; addLayers[t] = portalsLayers[i]; } window.fieldsFactionLayers = createFactionLayersArray(); var fieldsLayer = window.L.layerGroup(); addLayers['Fields'] = fieldsLayer; window.linksFactionLayers = createFactionLayersArray(); var linksLayer = window.L.layerGroup(); addLayers['Links'] = linksLayer; // faction-specific layers // these layers don't actually contain any data. instead, every time they're added/removed from the map, // the matching sub-layers within the above portals/fields/links are added/removed from their parent with // the below 'onoverlayadd/onoverlayremove' events var factionLayers = createFactionLayersArray(); factionLayers.forEach(function (facLayer, facIdx) { facLayer.on('add remove', function (e) { var fn = e.type + 'Layer'; console.log(fn); fieldsLayer[fn](fieldsFactionLayers[facIdx]); linksLayer[fn](linksFactionLayers[facIdx]); portalsLayers.forEach(function (portals, lvl) { portals[fn](portalsFactionLayers[lvl][facIdx]); }); }); addLayers[window.TEAM_NAMES[facIdx]] = facLayer; }); // to avoid any favouritism, we'll put the player's own faction layer first if (window.PLAYER.team !== 'RESISTANCE') { delete addLayers[window.TEAM_NAME_RES]; addLayers[window.TEAM_NAME_RES] = factionLayers[window.TEAM_RES]; } // and just put U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅ faction last delete addLayers[window.TEAM_NAME_MAC]; addLayers[window.TEAM_NAME_MAC] = factionLayers[window.TEAM_MAC]; return addLayers; /* eslint-enable dot-notation */ }; // to be extended in app.js (or by plugins: `setup.priority = 'boot';`) window.mapOptions = { preferCanvas: 'PREFER_CANVAS' in window ? window.PREFER_CANVAS : true // default }; function evalFuncReplace(funcName,...searchreplaceArgs) { // my own design // funcName must be a string // pass search,replace arguments in pairs, or arrays of 2: search,replace,[search,replace] // or add params combined in arrays of 3: search,replace,[search,replace,params],search,replace let funcString = ''; try { funcString = eval(`${funcName}.toString()`); } catch(e) { console.error(`eval ${funcName} toString failed`,e); return false; } if (!funcString) { console.error(`FAILED: eval ${funcName} toString empty result`); return false; } let searchList = []; let replaceList = []; let regexparamList = []; for (let cnt = 0; cnt < searchreplaceArgs.length; cnt++) { if (searchreplaceArgs[cnt] instanceof Array) { if (searchreplaceArgs[cnt].length >= 2) { searchList.push(searchreplaceArgs[cnt][0]); replaceList.push(searchreplaceArgs[cnt][1]); regexparamList.push(searchreplaceArgs[cnt].length >= 3 ? searchreplaceArgs[cnt][2] : ''); } } else { searchList.push(searchreplaceArgs[cnt]); replaceList.push(cnt + 1 < searchreplaceArgs.length && !(searchreplaceArgs[cnt + 1] instanceof Array) ? searchreplaceArgs[cnt + 1] : ''); regexparamList.push(''); cnt++; } } let newfuncString = funcString; for (let cnt = 0; cnt < searchList.length; cnt++) { let searchRegExp = searchList[cnt]; if (!(searchRegExp instanceof RegExp)) searchRegExp = new RegExp(searchRegExp,regexparamList[cnt]); let replacewithstring = (cnt < replaceList.length ? replaceList[cnt] : ''); if (newfuncString === (newfuncString = newfuncString.replace(searchRegExp,replacewithstring))) { console.warn(`WARNING: ${funcName} replace ${searchRegExp} unchanged`); } } try { if (funcString == eval(`${funcName} = ${newfuncString}`)) { console.warn(`WARNING: eval ${funcName} unchanged`); } } catch(e) { console.error(`FAILED: eval ${funcName}`,e,newfuncString); return false; } return true; } function setup() { if (!('TEAM_MAC' in window)) { // copied from IITC-CE 0.34.0: window.TEAM_MAC = 3; // append new values: window.COLORS.push('#FF0028'); window.TEAM_TO_CSS.push('mac'); } if (!('TEAM_NAMES' in window)) { window.TEAM_NAMES = ['Neutral', 'Resistance', 'Enlightened']; } if (window.TEAM_NAMES.length == 3) { // append new value: window.TEAM_NAMES.push('U̶͚̓̍N̴̖̈K̠͔̍͑̂͜N̞̥͋̀̉Ȯ̶̹͕̀W̶̢͚͑̚͝Ṉ̨̟̒̅'); } if (!('LINK_RANGE_MAC' in window)) { // copied from IITC-CE 0.34.1 window.LINK_RANGE_MAC = [0, 0, 500, 750, 1000, 1500, 2000, 3000, 5000, 5000]; // in meters } if (!('TEAM_CODENAMES' in window)) { // copied from IITC-CE 0.34.1 window.TEAM_CODENAMES = ['NEUTRAL', 'RESISTANCE', 'ENLIGHTENED', 'MACHINA']; } if (!('TEAM_NAME_NONE' in window)) { // copied from IITC-CE 0.34.0: window.TEAM_NAME_NONE = window.TEAM_NAMES[window.TEAM_NONE]; window.TEAM_NAME_RES = window.TEAM_NAMES[window.TEAM_RES]; window.TEAM_NAME_ENL = window.TEAM_NAMES[window.TEAM_ENL]; window.TEAM_NAME_MAC = window.TEAM_NAMES[window.TEAM_MAC]; } if (!('TEAM_CODES' in window)) { // add new variables: window.TEAM_CODES = ['N', 'R', 'E']; } if (window.TEAM_CODES.length == 3) { // append new value: window.TEAM_CODES.push('M'); } if ('teamStringToId' in window && typeof window.teamStringToId == 'function' && (window.teamStringToId.toString().match(/ENLIGHTENED/) || !window.teamStringToId.toString().match(/TEAM_CODENAMES/) || !window.teamStringToId.toString().match(/TEAM_NAMES/))) { // replace function: window.teamStringToId = function (teamStr) { var teamIndex = window.TEAM_CODENAMES.indexOf(teamStr); // added in IITC-CE 0.34.1 if (teamIndex >= 0) return teamIndex; teamIndex = window.TEAM_NAMES.indexOf(teamStr); // added in IITC-CE 0.34.0 if (teamIndex >= 0) return teamIndex; teamIndex = window.TEAM_CODES.indexOf(teamStr); if (teamIndex >= 0) return teamIndex; return window.TEAM_NONE; } } // add new styles let machinaStyle = document.head.appendChild(document.createElement('style')); if (!document.head.innerHTML.match(/\.mac \{\s+color:.*?\}/)) { machinaStyle.innerHTML = ` .mac { color: #ff2020 !important; } #mobileinfo .mac .filllevel { background-color: #ff0028 !important; } `; } if ('setupMap' in window && typeof window.setupMap == 'function' && !window.setupMap.toString().match(/createDefaultOverlays/) || window.setupMap.toString().match(/mapRunsUserAction/)) { // handle all from 0.33.0 and before, and not 0.34.0: evalFuncReplace( 'window.setupMap', [ // version 0.33.0 '(setupCRS)', `${self.namespace}$1` ], [ // version 0.33.0 '(createDefaultOverlays)', `${self.namespace}$1` ], [ // version 0.33.0 '(getPosition)', `${self.namespace}$1` ], [ 'var layerChooser = ', '' ], [ '(L\\.layerGroup\\(\\)\\])', 'L.layerGroup(), $1', 'g' ], [ '(createDefaultBaseMapLayers)', `${self.namespace}$1`, 'g' ], [ '(\\s+)(if \\(!isLayerGroupDisplayed)', '$1addLayers[window.TEAM_NAMES[window.TEAM_MAC]] = factionLayers[window.TEAM_MAC];$1$2' ], [ '(\\s+setFactionLayersState \\(TEAM_NONE)', '\n if (!isLayerGroupDisplayed(window.TEAM_NAMES[window.TEAM_MAC], true)) hiddenLayer.push (factionLayers[window.TEAM_MAC]);\n' + ' setFactionLayersState (window.TEAM_MAC, isLayerGroupDisplayed(window.TEAM_NAMES[window.TEAM_MAC], true));$1' ], [ '(break;)(\\s+\\})', `$1 case window.TEAM_NAMES[window.TEAM_MAC]: setFactionLayersState (window.TEAM_MAC, displayed); break;$2` ], [ '(portals|fields|links)(FactionLayers =)', 'window.$1$2', 'g' ] ); evalFuncReplace('window.Render.prototype.bringPortalsToFront',['(portalsFactionLayers)','window.$1','g']); evalFuncReplace('window.Render.prototype.addPortalToMapLayer',['(portalsFactionLayers)','window.$1','g']); evalFuncReplace('window.Render.prototype.removePortalFromMapLayer',['(portalsFactionLayers)','window.$1','g']); evalFuncReplace('window.Render.prototype.deleteLinkEntity',['(linksFactionLayers)','window.$1','g']); evalFuncReplace('window.Render.prototype.createLinkEntity',['(linksFactionLayers)','window.$1','g']); evalFuncReplace('window.Render.prototype.deleteFieldEntity',['(fieldsFactionLayers)','window.$1','g']); evalFuncReplace('window.Render.prototype.createFieldEntity',['(fieldsFactionLayers)','window.$1','g']); } if ('getPortalRange' in window && typeof window.getPortalRange == 'function' && !window.getPortalRange.toString().match(/LINK_RANGE_MAC/)) { evalFuncReplace('window.getPortalRange',['(base:)\\s?(160.*?4\\),)',`$1 d.team === 'M' ? window.LINK_RANGE_MAC[d.level + 1] : $2`],['(getPortalLevel)',`window.$1`]); } function updatePortalsListPlugin() { // modify plugin Portals list, if enabled, and only if not Machine ready: if (!window.plugin || !('portalslist' in window.plugin)) return false; if ('macP' in window.plugin.portalslist) return true; machinaStyle.innerHTML += ` #portalslist table tr.mac td, #portalslist .filterU̶͚ , #portalslist .filters .filterU̶͚ { background-color: #a00; } #portalslist .filters .filterU̶͚ { grid-row: 2; } @media (orientation: portrait) { #portalslist .filters .filterU̶͚.name { grid-column: 1; } #portalslist .filters .filterU̶͚.count { grid-column: 2; } #portalslist .filters .filterU̶͚ { grid-row: 4; } }`; window.plugin.portalslist.macP = 0; window.plugin.portalslist.fields.filter(el=>{return el.title == "Team"})[0].format = function(cell, portal, value) { $(cell).text(['NEU', 'RES', 'ENL', 'UNK'][value]); } evalFuncReplace( 'window.plugin.portalslist.getPortals', [ '(default:)', `case window.TEAM_MAC: window.plugin.portalslist.macP++; break; $1` ] ); evalFuncReplace( 'window.plugin.portalslist.displayPL', [ '(window\\.plugin\\.portalslist\\.filter = 0;)', `window.plugin.portalslist.macP = 0; $1` ] ); evalFuncReplace( 'window.plugin.portalslist.portalTable', [ '(["\']Enlightened["\'])', `$1,"${window.TEAM_NAMES[window.TEAM_MAC]}"` ], [ '(\'enlP\')', `$1, 'macP'` ], [ '(case 2:\\s+cell.*?\'%\\)\';)(\\s+\\})', `$1 break; case 3: cell.textContent = window.plugin.portalslist.macP + ' (' + Math.round(window.plugin.portalslist.macP/length*100) + '%)';$2`, 's' ], ['(case) 6(:\\s+return reversed)',`$1 7$2`], ['(case) 5(:\\s+return reversed)',`$1 6$2`], ['(case) 4(:\\s+return reversed)',`$1 5$2`], ['(case) 3(:\\s+return reversed)',`case 3:\n $1 4$2`] ); return true; } function updatePortalCountsPlugin() { // modify plugin Portal count, if enabled, and only if not Machine ready: if (!window.plugin || !('portalcounts' in window.plugin)) return false; if (!('getPortals' in window.plugin.portalcounts) || !(window.plugin.portalcounts.getPortals.toString().match(/PortalsEnl/)) || window.plugin.portalcounts.getPortals.toString().match(/PortalsMac/)) return true; machinaStyle.innerHTML += ` #portalcounts table th.res { background-color: #005684; } #portalcounts table th.enl { background-color: #017f01; } #portalcounts table th.mac { background-color: #a00; } #portalcounts table th.res, #portalcounts table td.res { color: ${window.COLORS[window.TEAM_RES]} !important;} #portalcounts table th.enl, #portalcounts table td.enl { color: ${window.COLORS[window.TEAM_ENL]} !important;} #portalcounts table th.mac, #portalcounts table td.mac { color: ${window.COLORS[window.TEAM_MAC]} !important;} `; window.plugin.portalcounts.CENTER_X = 250; evalFuncReplace( 'window.plugin.portalcounts.getPortals', [ '(self\\.neuP = 0;)', `$1\n self.macP = 0;` ], [ '(self\\.)(PortalsRes)( = new Array\\(\\);)', `$1$2$3\n $1PortalsMac$3\n` ], [ '(self\\.)(PortalsRes)(\\[level\\] = 0;)', `$1$2$3\n $1PortalsMac$3` ], [ '(default:)', `case 3 : self.macP++; self.PortalsMac[level]++; break; $1` ], [ '( \\+ self\\.resP);', `$1 + self.macP;` ], [ '(Enlightened)(Resistance)', `$2$1' + window.TEAM_NAMES[window.TEAM_MAC] + '` ], [ '(self\\.PortalsEnl\\[level\\] \\|\\| self\\.PortalsRes\\[level\\])', `$1 || self.PortalsMac[level]` ], [ '(\'\\+self\\.PortalsEnl\\[level\\]\\+\'<\\/td>)(\'\\+self\\.PortalsRes\\[level\\]\\+\')', `$2$1'+self.PortalsMac[level]+'` ], [ '(\'\\+self\\.enlP\\+\'<\\/td>)(\'\\+self\\.resP\\+\'<\\/td>)', `$2$1'+self.macP+'` ], [ 'colspan="2"', 'colspan="3"' ], [ 'svg width="300"', 'svg width="350"' ], [ '(val\\+self\\.PortalsEnl\\[i\\])', `$1+self.PortalsMac[i]` ], [ '(self\\.makeBar|self\\.makePie)', `// $1`, 'g' ], [ '(\\/\\/ pie graph)', ` self.makeBar(all , 'All', '#FFFFFF', 0*(self.BAR_WIDTH + self.BAR_PADDING),'All').appendTo(svg); self.makeBar(self.PortalsRes, 'Res', COLORS[1], 1*(self.BAR_WIDTH + self.BAR_PADDING),window.TEAM_NAMES[window.TEAM_RES]).appendTo(svg); self.makeBar(self.PortalsEnl, 'Enl', COLORS[2], 2*(self.BAR_WIDTH + self.BAR_PADDING),window.TEAM_NAMES[window.TEAM_ENL]).appendTo(svg); self.makeBar(self.PortalsMac, window.TEAM_NAMES[window.TEAM_MAC].substring(0, 3), COLORS[3], 3*(self.BAR_WIDTH + self.BAR_PADDING),window.TEAM_NAMES[window.TEAM_MAC]).appendTo(svg); $1` ], [ '(\\/\\/ outer part)', ` self.makePie(0, self.neuP/total, COLORS[0],window.TEAM_NAMES[window.TEAM_NONE]).appendTo(g); self.makePie(self.neuP/total, (self.neuP + self.resP)/total, COLORS[1],window.TEAM_NAMES[window.TEAM_RES]).appendTo(g); self.makePie((self.neuP + self.resP)/total, (self.neuP + self.resP + self.enlP)/total, COLORS[2],window.TEAM_NAMES[window.TEAM_ENL]).appendTo(g); self.makePie((self.neuP + self.resP + self.enlP)/total, 1, COLORS[3],window.TEAM_NAMES[window.TEAM_MAC]).appendTo(g); $1` ], [ '(var angle = 0;)(\\s+for\\(var i.*?})(.*?)\\s+(for\\(var)', `$1$3$2\n\n$4`, 's' ], [ 'for\\(var i=self\\.PortalsRes\\.length-1;i>=0;i--\\)', 'for(var i=0;i\'\\))', `\n/*\n $1` ], [ '\\s+(counts \\+= \\$)', `\n*/\n $1` ], [ '(\\+ self\\.neuP);', `$1 + self.macP;` ], [ '(width: \'auto\')', `id: 'plugin-portal-counts',\n $1` ] ); evalFuncReplace( 'window.plugin.portalcounts.makeBar', [ '(shift)', `$1, title` ], [ '(LVL\\[i\\])', `$1,\n title: 'L' + i` ], [ '(\'middle\')', `$1,\n title: title` ] ); evalFuncReplace( 'window.plugin.portalcounts.makePie', [ '(color)', `$1, title` ], [ '(y: ly)', `$1,\n title: title` ], [ '(p2y\\))', `$1,\n title: title` ] ); evalFuncReplace( 'window.plugin.portalcounts.makeRing', [ '(color)', `$1, title` ], [ '(\'Z\')', `$1,\n title: title` ] ); return true; } // place in a timeout so they will run an event later, when all plugins have initialized: setTimeout(updatePortalsListPlugin,0); setTimeout(updatePortalCountsPlugin,0); console.log('IITC plugin loaded: ' + self.title + ' version ' + self.version); }; setup.info = plugin_info; //add the script info data to the function as a property if (window.script_info?.script?.version.match(/^0\.26/)) { // version 0.26 does not have a prepPluginsToLoad function setup(); } else { setup.priority = 'boot'; if(!window.bootPlugins) window.bootPlugins = []; window.bootPlugins.push(setup); // if IITC has already booted, immediately run the 'setup' function if(window.iitcLoaded && typeof setup === 'function') setup(); } } // wrapper end // inject code into site context var script = document.createElement('script'); var info = {}; if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description }; script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');')); (document.body || document.head || document.documentElement).appendChild(script);