// ==UserScript== // @author birkett83 // @name Landgrab // @category Misc // @version 0.2 // @description A mini-game based on ingress and IITC // @id landgrab@birkett83 // @namespace https://github.com/IITC-CE/ingress-intel-total-conversion // @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/birkett83/landgrab.meta.js // @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/birkett83/landgrab.user.js // @issueTracker https://github.com/birkett83/landgrab/issues // @homepageURL https://birkett83.github.io/landgrab/ // @preview https://github.com/birkett83/landgrab/raw/main/assets/landgrab.png // @match https://intel.ingress.com/* // @grant none // ==/UserScript== /* globals $, L, d3 DSF*/ // https://github.com/d3/d3-delaunay v6.0.2 Copyright 2018-2021 Observable, Inc., 2021 Mapbox !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,(function(t){"use strict";const i=134217729;function e(t,i,e,n,s){let l,h,r,o,a=i[0],c=n[0],u=0,f=0;c>a==c>-a?(l=a,a=i[++u]):(l=c,c=n[++f]);let _=0;if(ua==c>-a?(h=a+l,r=l-(h-a),a=i[++u]):(h=c+l,r=l-(h-c),c=n[++f]),l=h,0!==r&&(s[_++]=r);ua==c>-a?(h=l+a,o=h-l,r=l-(h-o)+(a-o),a=i[++u]):(h=l+c,o=h-l,r=l-(h-o)+(c-o),c=n[++f]),l=h,0!==r&&(s[_++]=r);for(;u0!=d>0)return g;const y=Math.abs(_+d);return Math.abs(g)>=33306690738754716e-32*y?g:-function(t,n,a,c,u,f,_){let d,g,y,m,x,p,w,v,b,T,M,A,k,$,P,S,I,z;const F=t-u,U=a-u,K=n-f,L=c-f;$=F*L,p=i*F,w=p-(p-F),v=F-w,p=i*L,b=p-(p-L),T=L-b,P=v*T-($-w*b-v*b-w*T),S=K*U,p=i*K,w=p-(p-K),v=K-w,p=i*U,b=p-(p-U),T=U-b,I=v*T-(S-w*b-v*b-w*T),M=P-I,x=P-M,s[0]=P-(M+x)+(x-I),A=$+M,x=A-$,k=$-(A-x)+(M-x),M=k-S,x=k-M,s[1]=k-(M+x)+(x-S),z=A+M,x=z-A,s[2]=A-(z-x)+(M-x),s[3]=z;let j=function(t,i){let e=i[0];for(let n=1;n=H||-j>=H)return j;if(x=t-F,d=t-(F+x)+(x-u),x=a-U,y=a-(U+x)+(x-u),x=n-K,g=n-(K+x)+(x-f),x=c-L,m=c-(L+x)+(x-f),0===d&&0===g&&0===y&&0===m)return j;if(H=11093356479670487e-47*_+33306690738754706e-32*Math.abs(j),j+=F*m+L*d-(K*y+U*g),j>=H||-j>=H)return j;$=d*L,p=i*d,w=p-(p-d),v=d-w,p=i*L,b=p-(p-L),T=L-b,P=v*T-($-w*b-v*b-w*T),S=g*U,p=i*g,w=p-(p-g),v=g-w,p=i*U,b=p-(p-U),T=U-b,I=v*T-(S-w*b-v*b-w*T),M=P-I,x=P-M,o[0]=P-(M+x)+(x-I),A=$+M,x=A-$,k=$-(A-x)+(M-x),M=k-S,x=k-M,o[1]=k-(M+x)+(x-S),z=A+M,x=z-A,o[2]=A-(z-x)+(M-x),o[3]=z;const E=e(4,s,4,o,l);$=F*m,p=i*F,w=p-(p-F),v=F-w,p=i*m,b=p-(p-m),T=m-b,P=v*T-($-w*b-v*b-w*T),S=K*y,p=i*K,w=p-(p-K),v=K-w,p=i*y,b=p-(p-y),T=y-b,I=v*T-(S-w*b-v*b-w*T),M=P-I,x=P-M,o[0]=P-(M+x)+(x-I),A=$+M,x=A-$,k=$-(A-x)+(M-x),M=k-S,x=k-M,o[1]=k-(M+x)+(x-S),z=A+M,x=z-A,o[2]=A-(z-x)+(M-x),o[3]=z;const C=e(E,l,4,o,h);$=d*m,p=i*d,w=p-(p-d),v=d-w,p=i*m,b=p-(p-m),T=m-b,P=v*T-($-w*b-v*b-w*T),S=g*y,p=i*g,w=p-(p-g),v=g-w,p=i*y,b=p-(p-y),T=y-b,I=v*T-(S-w*b-v*b-w*T),M=P-I,x=P-M,o[0]=P-(M+x)+(x-I),A=$+M,x=A-$,k=$-(A-x)+(M-x),M=k-S,x=k-M,o[1]=k-(M+x)+(x-S),z=A+M,x=z-A,o[2]=A-(z-x)+(M-x),o[3]=z;const N=e(C,h,4,o,r);return r[N-1]}(t,n,a,c,u,f,y)}const c=Math.pow(2,-52),u=new Uint32Array(512);class f{static from(t,i=x,e=p){const n=t.length,s=new Float64Array(2*n);for(let l=0;l>1;if(i>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*i-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(i)),this._hullPrev=new Uint32Array(i),this._hullNext=new Uint32Array(i),this._hullTri=new Uint32Array(i),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(i),this._dists=new Float64Array(i),this.update()}update(){const{coords:t,_hullPrev:i,_hullNext:e,_hullTri:n,_hullHash:s}=this,l=t.length>>1;let h=1/0,r=1/0,o=-1/0,u=-1/0;for(let i=0;io&&(o=e),n>u&&(u=n),this._ids[i]=i}const f=(h+o)/2,d=(r+u)/2;let m,x,p,w=1/0;for(let i=0;i0&&(x=i,w=e)}let T=t[2*x],M=t[2*x+1],A=1/0;for(let i=0;in&&(i[e++]=s,n=this._dists[s])}return this.hull=i.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(a(v,b,T,M,k,$)<0){const t=x,i=T,e=M;x=p,T=k,M=$,p=t,k=i,$=e}const P=function(t,i,e,n,s,l){const h=e-t,r=n-i,o=s-t,a=l-i,c=h*h+r*r,u=o*o+a*a,f=.5/(h*a-r*o);return{x:t+(a*c-r*u)*f,y:i+(h*u-o*c)*f}}(v,b,T,M,k,$);this._cx=P.x,this._cy=P.y;for(let i=0;i0&&Math.abs(u-l)<=c&&Math.abs(f-h)<=c)continue;if(l=u,h=f,o===m||o===x||o===p)continue;let _=0;for(let t=0,i=this._hashKey(u,f);t=0;)if(g=d,g===_){g=-1;break}if(-1===g)continue;let y=this._addTriangle(g,o,e[g],-1,-1,n[g]);n[o]=this._legalize(y+2),n[g]=y,S++;let w=e[g];for(;d=e[w],a(u,f,t[2*w],t[2*w+1],t[2*d],t[2*d+1])<0;)y=this._addTriangle(w,o,d,n[o],-1,n[w]),n[o]=this._legalize(y+2),e[w]=w,S--,w=d;if(g===_)for(;d=i[g],a(u,f,t[2*d],t[2*d+1],t[2*g],t[2*g+1])<0;)y=this._addTriangle(d,o,g,-1,n[g],n[d]),this._legalize(y+2),n[d]=y,e[g]=g,S--,g=d;this._hullStart=i[o]=g,e[g]=i[w]=o,e[o]=w,s[this._hashKey(u,f)]=o,s[this._hashKey(t[2*g],t[2*g+1])]=g}this.hull=new Uint32Array(S);for(let t=0,i=this._hullStart;t0?3-e:1+e)/4}(t-this._cx,i-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:i,_halfedges:e,coords:n}=this;let s=0,l=0;for(;;){const h=e[t],r=t-t%3;if(l=r+(t+2)%3,-1===h){if(0===s)break;t=u[--s];continue}const o=h-h%3,a=r+(t+1)%3,c=o+(h+2)%3,f=i[l],_=i[t],g=i[a],y=i[c];if(d(n[2*f],n[2*f+1],n[2*_],n[2*_+1],n[2*g],n[2*g+1],n[2*y],n[2*y+1])){i[t]=y,i[h]=f;const n=e[c];if(-1===n){let i=this._hullStart;do{if(this._hullTri[i]===c){this._hullTri[i]=t;break}i=this._hullPrev[i]}while(i!==this._hullStart)}this._link(t,n),this._link(h,e[l]),this._link(l,c);const r=o+(h+1)%3;s=e&&i[t[h]]>l;)t[h+1]=t[h--];t[h+1]=n}else{let s=e+1,l=n;m(t,e+n>>1,s),i[t[e]]>i[t[n]]&&m(t,e,n),i[t[s]]>i[t[n]]&&m(t,s,n),i[t[e]]>i[t[s]]&&m(t,e,s);const h=t[s],r=i[h];for(;;){do{s++}while(i[t[s]]r);if(l=l-e?(y(t,i,s,n),y(t,i,e,l-1)):(y(t,i,e,l-1),y(t,i,s,n))}}function m(t,i,e){const n=t[i];t[i]=t[e],t[e]=n}function x(t){return t[0]}function p(t){return t[1]}const w=1e-6;class v{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,i){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+i}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,i){this._+=`L${this._x1=+t},${this._y1=+i}`}arc(t,i,e){const n=(t=+t)+(e=+e),s=i=+i;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${n},${s}`:(Math.abs(this._x1-n)>w||Math.abs(this._y1-s)>w)&&(this._+="L"+n+","+s),e&&(this._+=`A${e},${e},0,1,1,${t-e},${i}A${e},${e},0,1,1,${this._x1=n},${this._y1=s}`)}rect(t,i,e,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+i}h${+e}v${+n}h${-e}Z`}value(){return this._||null}}class b{constructor(){this._=[]}moveTo(t,i){this._.push([t,i])}closePath(){this._.push(this._[0].slice())}lineTo(t,i){this._.push([t,i])}value(){return this._.length?this._:null}}class T{constructor(t,[i,e,n,s]=[0,0,960,500]){if(!((n=+n)>=(i=+i)&&(s=+s)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=n,this.xmin=i,this.ymax=s,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:i,triangles:e},vectors:n}=this,s=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let i,n,l=0,h=0,r=e.length;l1;)s-=2;for(let t=2;t4)for(let t=0;t0){if(i>=this.ymax)return null;(s=(this.ymax-i)/n)0){if(t>=this.xmax)return null;(s=(this.xmax-t)/e)this.xmax?2:0)|(ithis.ymax?8:0)}}const M=2*Math.PI,A=Math.pow;function k(t){return t[0]}function $(t){return t[1]}function P(t,i,e){return[t+Math.sin(t+i)*e,i+Math.cos(t-i)*e]}class S{static from(t,i=k,e=$,n){return new S("length"in t?function(t,i,e,n){const s=t.length,l=new Float64Array(2*s);for(let h=0;h2&&function(t){const{triangles:i,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:i.length/2},((t,i)=>i)).sort(((t,e)=>i[2*t]-i[2*e]||i[2*t+1]-i[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],n=[i[2*t],i[2*t+1],i[2*e],i[2*e+1]],s=1e-8*Math.hypot(n[3]-n[1],n[2]-n[0]);for(let t=0,e=i.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=n[0],l[n[0]]=1,2===n.length&&(l[n[1]]=0,this.triangles[1]=n[1],this.triangles[2]=n[1]))}voronoi(t){return new T(this,t)}*neighbors(t){const{inedges:i,hull:e,_hullIndex:n,halfedges:s,triangles:l,collinear:h}=this;if(h){const i=h.indexOf(t);return i>0&&(yield h[i-1]),void(i=0&&s!==e&&s!==n;)e=s;return s}_step(t,i,e){const{inedges:n,hull:s,_hullIndex:l,halfedges:h,triangles:r,points:o}=this;if(-1===n[t]||!o.length)return(t+1)%(o.length>>1);let a=t,c=A(i-o[2*t],2)+A(e-o[2*t+1],2);const u=n[t];let f=u;do{let n=r[f];const u=A(i-o[2*n],2)+A(e-o[2*n+1],2);if(u this.rank(r2)) { this._rep[r2] = r1; this._score[r1] = score; } else if (this.rank(r2) > this.rank(r1)) { this._rep[r1] = r2; this._score[r2] = score; } else { this._rank[r1] = this.rank(r1) + 1; this._rep[r2] = r1; this._score[r1] = score; } } 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 var landgrab = window.plugin.landgrab = function() {}; landgrab.portalInfo = []; landgrab.portalIndex = {}; landgrab.voronoi = null; landgrab.voronoiStale = true; landgrab.voronoiStyle = { stroke: true, color: '#FF00FF', weight: 4, opacity: 0.5, interactive: false, fill: true, fillColor: null, // to use the same as 'color' for fill fillOpacity: 0.2, dashArray: '' }; landgrab.captureColorGradient = ['#000000', '#b20000', '#da0000', '#e72400', '#f14c00', '#fa7400', '#fc9200', '#feb900', '#ffde04', '#ffe432', '#ffea60']; landgrab.visitColorGradient = ['#000000', '#b200b2', '#da00da', '#e700c3', '#f100a5', '#fa0086', '#fc006a', '#fe0045', '#ff0421', '#ff321b', '#ff6015']; landgrab.newScoreObj = function() { return { score: [], totalScore: 0, dsf: new DSF(), stale: false, } } landgrab.capture = landgrab.newScoreObj(); landgrab.visit = landgrab.newScoreObj(); landgrab.disabledMessage = null; landgrab.contentHTML = null; landgrab.onPortalSelected = function() { landgrab.drawPolygons(); } landgrab.onPortalDetailsUpdated = function() { if(typeof(Storage) === "undefined") { $('#portaldetails > .imgpreview').after(landgrab.disabledMessage); return; } var guid = window.selectedPortal, details = window.portalDetail.get(guid), nickname = window.PLAYER.nickname; if(details) { let [idx, portalInfo] = landgrab.getPortal(guid); if(details.history) { if(details.history.captured && !portalInfo.captured) { portalInfo.captured = true; landgrab.capture.stale = true; landgrab.storePortalInfo(); } if(details.history.visited && !portalInfo.visited) { portalInfo.visited = true; landgrab.visit.stale = true; landgrab.storePortalInfo(); } if (landgrab.visit.stale || landgrab.capture.stale) { landgrab.computeScores(); } } $('#portaldetails > .imgpreview').after( '' + '' + '' + '' + '' + '
VisitedCaptured
Portal Score' + landgrab.visit.score[idx] + '' + landgrab.capture.score[idx] + '
Local Score' + landgrab.visit.dsf.score(idx) + '' + landgrab.capture.dsf.score(idx) + '
Total Score' + landgrab.visit.totalScore + '' + landgrab.capture.totalScore + '
'); } } landgrab.mapDataRefreshEnd = function () { landgrab.removeMissingPortals(); if (landgrab.voronoiStale) { // Using the d3-geo-voronoi package which does proper spherical geometry // produced extremely bad results when portals are close together, probably // because of rounding errors in the trigonometric functions blowing up. // Instead we will just ignore all that and pretend these are coordinates // a plane. This will totally break near the anti-prime meridian. // To the people of Fiji, I humbly apologise, I could not get it to work // properly for you. landgrab.voronoi = d3.Delaunay.from( Object.values(landgrab.portalInfo), p => p.lat, p => p.lng ).voronoi([-180000000, -180000000, 180000000, 180000000]); landgrab.voronoiStale = false; // need to recompute scores. landgrab.capture.stale = true; landgrab.visit.stale = true; } if (landgrab.visit.stale || landgrab.capture.stale) { landgrab.computeScores(); } landgrab.storePortalInfo(); } landgrab.removeMissingPortals = function () { // When a portal has been removed from the game, it may still exist in portalInfo // This is particularly annoying if the player has not captured the portal before // it was removed, in which case they're stuck with a ghost portal they can never // capture. if (!(window.getDataZoomTileParameters().hasPortals)) { return; } let bounds = window.map.getBounds(); let missingPortals = new Set(landgrab.portalInfo .filter(p => bounds.contains([p.lat/1000000, p.lng/1000000])) .filter(p => !(p.guid in window.portals)) .map(p => p.guid)) if (missingPortals.size > 0) { console.log("Removed", missingPortals); landgrab.voronoiStale = true; // Rebuild portalInfo and portalIndex without the portals that have been removed let portalInfo = landgrab.portalInfo .filter(p => !(missingPortals.has(p.guid))) landgrab.portalInfo = []; landgrab.portalIndex = {} for (let p of portalInfo) { landgrab.addPortal(p.guid, p.lat, p.lng, p.captured, p.visited); } } } landgrab.computeScores = function() { var uncaptured = []; let capture = landgrab.newScoreObj(); var unvisited = []; let visit = landgrab.newScoreObj(); for (let [i, portalInfo] of landgrab.portalInfo.entries()) { if (!portalInfo.captured) { uncaptured.push(i) } if (!portalInfo.visited) { unvisited.push(i) } } landgrab.computeScoresInner( 0, uncaptured, capture); landgrab.computeScoresInner( 0, unvisited, visit); landgrab.capture = capture; landgrab.visit = visit; landgrab.drawPolygons(); } landgrab.computeScoresInner = function(depth, neighbors, scores) { var newneighbors = [] for (let i of neighbors) { // Check if we've seen this portal before if (scores.score[i] != undefined) { continue }; scores.score[i] = depth; scores.dsf.addScore(i, depth); scores.totalScore += depth; for (let n of landgrab.voronoi.neighbors(i)) { if (scores.score[n] == undefined) { newneighbors.push(n); } // Neighbouring portals that are both {visit, captur}ed means they're // part of the same blob if (scores.score[i] && scores.score[n]) { scores.dsf.union(i, n); } } } if (newneighbors.length) { landgrab.computeScoresInner(depth+1, newneighbors, scores); } } landgrab.drawPolygons = function() { landgrab.drawLayer(landgrab.captureLayer, landgrab.capture, landgrab.captureColorGradient); landgrab.drawLayer(landgrab.visitLayer, landgrab.visit, landgrab.visitColorGradient); } landgrab.drawLayer = function(layer, scores, colors) { layer.clearLayers(); var selectedRep; if (window.selectedPortal) { let idx = landgrab.portalIndex[window.selectedPortal]; selectedRep = scores.dsf.find(idx); } for (let [i, portalInfo] of landgrab.portalInfo.entries()) { let score = scores.score[i]; if (score > 0) { let color = colors[score % colors.length]; let style = {...landgrab.voronoiStyle, color: color}; if (selectedRep) { let rep = scores.dsf.find(i); if (selectedRep != rep) { style = {...style, opacity: 0.25, fillOpacity: 0.1} } } layer.addLayer( new L.polygon( landgrab.voronoi.cellPolygon(i) .map(p => [p[0]/1000000, p[1]/1000000]), style) ); } } } landgrab.getPortal = function(guid) { var idx = landgrab.portalIndex[guid]; /*if (idx == undefined) { console.log("guid not found", guid); return [null, null]; }*/ var portalInfo = landgrab.portalInfo[idx]; /*if (portalInfo == undefined) { console.log("index not found", guid, idx); return [null, null]; } if (portalInfo.guid != guid) { console.log("guid mismatch", guid, idx, portalInfo.guid); return [null, null]; }*/ return [idx, portalInfo]; } landgrab.onPortalAdded = function (data) { let guid = data.portal.options.guid; let portal = data.portal; let history = portal.options.data.history; // Bug in stock ingress means history often doesn't show. Something about caching. // Reload the page and eventually it does. Or something. // For now we just ignore portals that don't have history. if (!history) { return } landgrab.addPortal( guid, portal.options.data.latE6, portal.options.data.lngE6, history.captured, history.visited, ) } landgrab.addPortal = function(guid, lat, lng, captured, visited) { // It would be nice to use the guid as our portal identifiers everywhere // but sadly d3.Delunay only works on integer indices. let [idx, portalInfo] = landgrab.getPortal(guid); if (idx == undefined) { // We have not seen this portal before. // This means we need to compute a new voronoi diagram (and scores) // We'll do that in mapDataRefreshEnd landgrab.voronoiStale = true; //console.log(history); let newlen = landgrab.portalInfo.push({ guid: guid, lat: lat, lng: lng, captured: captured, visited: visited, }); landgrab.portalIndex[guid] = newlen - 1; } else { if(captured && !portalInfo.captured) { // This portal has been captured since we last saw it. We need to recompute scores. // We'll do that in mapDataRefreshEnd portalInfo.captured = true; landgrab.capture.stale = true; } if(visited && !portalInfo.visited) { portalInfo.visited = true; landgrab.visit.stale = true; } // Portal may have moved portalInfo.lat = lat; portalInfo.lng = lng; } } const key = 'plugin-landgrab-portalinfo'; const formatKey = 'plugin-landgrab-dataformat'; landgrab.loadPortalInfo = function() { if(localStorage[key] == undefined) { return; } let dataFormat = localStorage[formatKey] || 0; let loadVersion = []; loadVersion[0] = function () { var portalInfo = JSON.parse(localStorage[key]); if (!portalInfo instanceof Array) {return}; // We don't set landgrab.portalInfo to the value loaded from JSON directly // because we need to construct landgrab.portalIndex as well. // So instead we call addPortal on each item. for (let p of portalInfo) { // migration from older versions that didn't store visit info if (p.visited == undefined) { p.visited = p.captured; } landgrab.addPortal(p.guid, p.lat * 1000000, p.lng * 1000000, p.captured, p.visited); } } loadVersion[1] = function () { var portalInfo = JSON.parse(localStorage[key]); for (let p of portalInfo) { landgrab.addPortal(p.guid, p.lat, p.lng, p.captured, p.visited); } } loadVersion[dataFormat](); } landgrab.storePortalInfo = function() { // Find indicies of all visited portals and their neighbours. // We need the immediate neighbours of captured portals to correctly // draw the Voronoi diagram. Filtering in this way reduced my // portalInfo list from 20000 down to 7000 so it should help with // performance. let indicies = {}; for (let [i, portalInfo] of landgrab.portalInfo.entries()) { if (portalInfo.visited) { indicies[i] = true; for (let j of landgrab.voronoi.neighbors(i)) { indicies[j] = true; } } } // This won't take effect until the next time IITC is loaded but that's fine. let newPortalInfo = landgrab.portalInfo.filter((_, idx) => indicies[idx]); //localStorage.removeItem('plugin-landgrab-portalinfo'); localStorage[key] = JSON.stringify(newPortalInfo); localStorage[formatKey] = 1; } /***************************************************************************************************************************************************************/ /** HIGHLIGHTER ************************************************************************************************************************************************/ /***************************************************************************************************************************************************************/ landgrab.highlighter = { highlight: function(data) { var guid = data.portal.options.ent[0]; let [idx, portalInfo] = landgrab.getPortal(guid); var style = {}; if (portalInfo) { if (portalInfo.captured) { // captured - no highlights } else if (portalInfo.visited) { style.fillColor = '#FF0000'; style.fillOpacity = 1.0; } else { // we have an 'portalInfo' entry for the portal, but it's not visited style.fillColor = '#CC00FF'; style.fillOpacity = 1.0; } } else { // no visit data at all style.fillColor = '#CC00FF'; style.fillOpacity = 1.0; } data.portal.setStyle(style); }, } landgrab.setupCSS = function() { $("