diff --git a/plugins/guess-player-levels.user.js b/plugins/guess-player-levels.user.js index eb4b6afc..64895a0c 100644 --- a/plugins/guess-player-levels.user.js +++ b/plugins/guess-player-levels.user.js @@ -20,6 +20,7 @@ // use own namespace for plugin window.plugin.guessPlayerLevels = function() {}; +window.plugin.guessPlayerLevels.BURSTER_RANGES = [0, 42, 48, 58, 72, 90, 112, 138, 168]; // we prepend a hash sign (#) in front of the player name in storage in order to prevent accessing a pre-defined property // (like constructor, __defineGetter__, etc. @@ -178,24 +179,208 @@ window.plugin.guessPlayerLevels.extractPortalData = function(data) { } window.plugin.guessPlayerLevels.extractChatData = function(data) { + var attackData = {}; + function addAttackMessage(nick, timestamp, portal) { + if(!attackData[nick]) attackData[nick] = {}; + if(!attackData[nick][timestamp]) attackData[nick][timestamp] = []; + attackData[nick][timestamp].push(portal); + } + data.raw.result.forEach(function(msg) { var plext = msg[2].plext; + + // search for "x deployed an Ly Resonator on z" if(plext.plextType == 'SYSTEM_BROADCAST' && plext.markup.length==5 && plext.markup[0][0] == 'PLAYER' && plext.markup[1][0] == 'TEXT' && plext.markup[1][1].plain == ' deployed an ' && plext.markup[2][0] == 'TEXT' - && plext.markup[2][0] == 'TEXT' && plext.markup[3][0] == 'TEXT' && plext.markup[3][1].plain == ' Resonator on ') { var nick = plext.markup[0][1].plain; var lvl = parseInt(plext.markup[2][1].plain.substr(1)); window.plugin.guessPlayerLevels.savePlayerLevel(nick, lvl, true); } + + // search for "x destroyed an Ly Resonator on z" + if(plext.plextType == 'SYSTEM_BROADCAST' + && plext.markup.length==5 + && plext.markup[0][0] == 'PLAYER' + && plext.markup[1][0] == 'TEXT' + && plext.markup[1][1].plain == ' destroyed an ' + && plext.markup[2][0] == 'TEXT' + && plext.markup[3][0] == 'TEXT' + && plext.markup[3][1].plain == ' Resonator on ') { + var nick = plext.markup[0][1].plain; + var portal = plext.markup[4][1]; + addAttackMessage(nick, msg[1], portal) + } + + // search for "Your Lx Resonator on y was destroyed by z" + if(plext.plextType == 'SYSTEM_NARROWCAST' + && plext.markup.length==6 + && plext.markup[0][0] == 'TEXT' + && plext.markup[0][1].plain == 'Your ' + && plext.markup[1][0] == 'TEXT' + && plext.markup[2][0] == 'TEXT' + && plext.markup[2][1].plain == ' Resonator on ' + && plext.markup[3][0] == 'PORTAL' + && plext.markup[4][0] == 'TEXT' + && plext.markup[4][1].plain == ' was destroyed by ' + && plext.markup[5][0] == 'PLAYER') { + var nick = plext.markup[5][1].plain; + var portal = plext.markup[3][1]; + addAttackMessage(nick, msg[1], portal) + } + + // search for "Your Portal x neutralized by y" + // search for "Your Portal x is under attack by y" + if(plext.plextType == 'SYSTEM_NARROWCAST' + && plext.markup.length==4 + && plext.markup[0][0] == 'TEXT' + && plext.markup[0][1].plain == 'Your Portal ' + && plext.markup[1][0] == 'PORTAL' + && plext.markup[2][0] == 'TEXT' + && (plext.markup[2][1].plain == ' neutralized by ' || plext.markup[2][1].plain == ' is under attack by ') + && plext.markup[3][0] == 'PLAYER') { + var nick = plext.markup[3][1].plain; + var portal = plext.markup[1][1]; + addAttackMessage(nick, msg[1], portal) + } }); + + for(nick in attackData) { + for(timestamp in attackData[nick]) { + // remove duplicates + var latlngs = []; + var portals = {}; + attackData[nick][timestamp].forEach(function(portal) { + if(portals.hasOwnProperty(portal.guid)) + return; + portals[portal.guid] = 1; + latlngs.push({x: portal.lngE6/1E6, y:portal.latE6/1E6}); + }); + if(latlngs.length < 2) // we need at least 2 portals to calculate burster range + continue; + + window.plugin.guessPlayerLevels.handleAttackData(nick, latlngs); + } + } }; +window.plugin.guessPlayerLevels.handleAttackData = function(nick, latlngs) { + /* + This is basically the smallest enclosing circle problem. The algorithm is for points on a plane, but for our ranges + (X8 has 168m) this should work. + http://www.cs.uu.nl/docs/vakken/ga/slides4b.pdf + http://nayuki.eigenstate.org/page/smallest-enclosing-circle + http://everything2.com/title/Circumcenter + */ + var circle = { + x: latlngs[0].x, + y: latlngs[0].y, + radius: 0 + }; + for(var i=1; i range) + range = d; + } + + // same as above. ignore nonsense data + if(range > 1000) { + return; + } + + var burster = window.plugin.guessPlayerLevels.BURSTER_RANGES; + for(var i=1; i burster[i]) { + window.plugin.guessPlayerLevels.savePlayerLevel(nick, Math.min(i+1, MAX_PORTAL_LEVEL), false); + } + } + + //L.circle(latlng, range, { + // weight:1, + // title: nick + ", " + range + "m" + //}).addTo(map); +} + +window.plugin.guessPlayerLevels.calculateCircleWithAnchor = function(latlngs, anchor) { + var circle = { + x: anchor.x, + y: anchor.y, + radius: 0 + }; + for(var i=0; i