artifact: niantic finally removed the old artifact API - we now have to use the far less useful new one

re-did the display of target/shard on the portal details - better like this I think
This commit is contained in:
Jon Atkins 2015-07-10 07:51:28 +01:00
parent aca3799a6a
commit c859a144f9
4 changed files with 177 additions and 211 deletions

View File

@ -35,23 +35,7 @@ window.artifact.requestData = function() {
if (isIdle()) {
artifact.idle = true;
} else {
// new API available in stock (2015-05-21) - 'getArtifactPortals'
// stock still uses this one, and the new method doesn't yet return anything, but they might be changing things soon...
//2015-06-13 - stock intel update using the new API
//above method takes no params, returns a list of portals. however, no longer
//1. a distinction between target portals and artifact portals
//2. some shard details in the portal summary 'unknown12' variable
// a. no sign of fragment numbers in this summary data - only in portal details
// b. not sure how resistance/enlightened targets are identified. so far, most portals have
// "[[["lightman"]], []]" (fragment at portal?)
// aod two have
// "[[["lightman"]], [["lightman"]]]"
// best guess - first array is a list of artifact types (but not numbers) at a portal
// - second array is a list of the target types for a portal
// (so "[[[]], [["lightman"]]]" would be for a target without any fragments at it
window.postAjax('artifacts', {}, artifact.handleSuccess, artifact.handleError);
window.postAjax('getArtifactPortals', {}, artifact.handleSuccess, artifact.handleError);
}
}
@ -81,34 +65,14 @@ window.artifact.handleFailure = function(data) {
window.artifact.processData = function(data) {
if (data.error || !data.artifacts) {
console.warn('Failed to find artifacts in artifact response');
if (data.error || !data.result) {
console.warn('Failed to find result in getArtifactPortals response');
return;
}
artifact.clearData();
$.each (data.artifacts, function(i,artData) {
// if we have no descriptions for a type, we don't know about it
if (!artifact.getArtifactDescriptions(artData.artifactId)) {
// jarvis and amar artifacts - fragmentInfos and targetInfos
// (future types? completely unknown at this time!)
console.warn('Note: unknown artifactId '+artData.artifactId+' - guessing how to handle it');
}
artifact.artifactTypes[artData.artifactId] = artData.artifactId;
if (artData.fragmentInfos) {
artifact.processFragmentInfos (artData.artifactId, artData.fragmentInfos);
}
if (artData.targetInfos) {
artifact.processTargetInfos (artData.artifactId, artData.targetInfos);
}
// other data in future? completely unknown!
});
artifact.processResult(data.result);
// redraw the artifact layer
artifact.updateLayer();
@ -117,38 +81,52 @@ window.artifact.processData = function(data) {
window.artifact.clearData = function() {
artifact.portalInfo = {};
artifact.artifactTypes = {};
artifact.entities = [];
}
window.artifact.processFragmentInfos = function (id, fragments) {
$.each(fragments, function(i, fragment) {
if (!artifact.portalInfo[fragment.portalGuid]) {
artifact.portalInfo[fragment.portalGuid] = { _entityData: fragment.portalInfo };
window.artifact.processResult = function (portals) {
// portals is an object, keyed from the portal GUID, containing the portal entity array
for (var guid in portals) {
var ent = portals[guid];
var data = decodeArray.portalSummary(ent);
// we no longer know the faction for the target portals, and we don't know which fragment numbers are at the portals
// all we know, from the portal summary data, for each type of artifact, is that each artifact portal is
// - a target portal or not - no idea for which faction
// - has one (or more) fragments, or not
if (!artifact.portalInfo[guid]) artifact.portalInfo[guid] = {};
// store the decoded data - needed for lat/lng for layer markers
artifact.portalInfo[guid]._data = data;
for(var type in data.artifactBrief.target) {
if (!artifact.artifactTypes[type]) artifact.artifactTypes[type] = {};
if (!artifact.portalInfo[guid][type]) artifact.portalInfo[guid][type] = {};
artifact.portalInfo[guid][type].target = TEAM_NONE; // as we no longer know the team...
}
if (!artifact.portalInfo[fragment.portalGuid][id]) artifact.portalInfo[fragment.portalGuid][id] = {};
for(var type in data.artifactBrief.fragment) {
if (!artifact.artifactTypes[type]) artifact.artifactTypes[type] = {};
if (!artifact.portalInfo[fragment.portalGuid][id].fragments) artifact.portalInfo[fragment.portalGuid][id].fragments = [];
if (!artifact.portalInfo[guid][type]) artifact.portalInfo[guid][type] = {};
$.each(fragment.fragments, function(i,f) {
artifact.portalInfo[fragment.portalGuid][id].fragments.push(f);
});
});
}
window.artifact.processTargetInfos = function (id, targets) {
$.each(targets, function(i, target) {
if (!artifact.portalInfo[target.portalGuid]) {
artifact.portalInfo[target.portalGuid] = { _entityData: target.portalInfo };
artifact.portalInfo[guid][type].fragments = true; //as we no longer have a list of the fragments there
}
if (!artifact.portalInfo[target.portalGuid][id]) artifact.portalInfo[target.portalGuid][id] = {};
artifact.portalInfo[target.portalGuid][id].target = target.team === 'RESISTANCE' ? TEAM_RES : TEAM_ENL;
});
// let's pre-generate the entities needed to render the map - array of [guid, timestamp, ent_array]
artifact.entities.push ( [guid, data.timestamp, ent] );
}
}
window.artifact.getArtifactTypes = function() {
@ -159,30 +137,9 @@ window.artifact.isArtifact = function(type) {
return type in artifact.artifactTypes;
}
window.artifact.getArtifactDescriptions = function(type) {
var descriptions = {
'jarvis': { 'title': "Jarvis Shards", 'fragmentName': "shards" },
'amar': { 'title': "Amar Artifacts", 'fragmentName': "artifacts" },
'helios': { 'title': "Helios Artifacts", 'fragmentName': "artifacts" },
'shonin': { 'title': "Sh\u014Dnin Shards", 'fragmentName': "shards" },
'lightman': { 'title': "Lightman Shards", 'fragmentName': "shards" },
};
return descriptions[type];
}
// used to render portals that would otherwise be below the visible level
window.artifact.getArtifactEntities = function() {
var entities = [];
// create fake entities from the artifact data
$.each (artifact.portalInfo, function(guid,data) {
var timestamp = 0; // we don't have a valid timestamp - so let's use 0
var ent = [ guid, timestamp, data._entityData ];
entities.push(ent);
});
return entities;
return artifact.entities;
}
window.artifact.getInterestingPortals = function() {
@ -199,103 +156,72 @@ window.artifact.getPortalData = function(guid,artifactId) {
return artifact.portalInfo[guid] && artifact.portalInfo[guid][artifactId];
}
// get the target(s) at a specified portal. returns 'false'y if the portal isn't a target
window.artifact.getPortalTarget = function(guid) {
targets = {};
if (artifact.portalInfo[guid]) {
for (var type in artifact.artifactTypes) {
if (artifact.portalInfo[guid][type].target !== undefined) {
targets[type] = artifact.portalInfo[guid][type].target;
}
}
}
return Object.keys(targets).length == 0 ? null : targets;
}
window.artifact.updateLayer = function() {
artifact._layer.clearLayers();
$.each(artifact.portalInfo, function(guid,data) {
var latlng = L.latLng ([data._entityData[2]/1E6, data._entityData[3]/1E6]);
var latlng = L.latLng ([data._data.latE6/1E6, data._data.lngE6/1E6]);
// jarvis shard icon
var iconUrl = undefined;
var iconSize = 0;
var opacity = 1.0;
$.each(data, function(type,detail) {
// redundant as of 2014-02-05 - jarvis shards removed
if (data.jarvis) {
if (data.jarvis.target) {
// target portal - show the target marker. use the count of fragments at the target to pick the right icon - it has segments that fill up
// we'll construct the URL form the type - stock seems to do that now
var count = data.jarvis.fragments ? data.jarvis.fragments.length : 0;
var iconUrl;
if (data[type].target !== undefined) {
// target portal
var iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/'+type+'_shard_target.png'
var iconSize = 100/2;
var opacity = 1.0;
var icon = L.icon({
iconUrl: iconUrl,
iconSize: [iconSize,iconSize],
iconAnchor: [iconSize/2,iconSize/2],
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
});
var marker = L.marker (latlng, {icon: icon, clickable: false, keyboard: false, opacity: opacity });
artifact._layer.addLayer(marker);
} else if (data[type].fragments) {
// fragment(s) at portal
var iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/'+type+'_shard.png'
var iconSize = 60/2;
var opacity = 0.6;
var icon = L.icon({
iconUrl: iconUrl,
iconSize: [iconSize,iconSize],
iconAnchor: [iconSize/2,iconSize/2],
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
});
var marker = L.marker (latlng, {icon: icon, clickable: false, keyboard: false, opacity: opacity });
artifact._layer.addLayer(marker);
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/jarvis_shard_target_'+count+'.png';
iconSize = 100/2; // 100 pixels - half that size works better
} else if (data.jarvis.fragments) {
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/jarvis_shard.png';
iconSize = 60/2; // 60 pixels - half that size works better
opacity = 0.6; // these often hide portals - let's make them semi transparent
}
}
// 2014-02-06: a guess at whats needed for the new artifacts
if (data.amar) {
if (data.amar.target) {
// target portal - show the target marker. use the count of fragments at the target to pick the right icon - it has segments that fill up
}); //end $.each(data, function(type,detail)
var count = data.amar.fragments ? data.amar.fragments.length : 0;
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/amar_shard_target_'+count+'.png';
iconSize = 100/2; // 100 pixels - half that size works better
} else if (data.amar.fragments) {
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/amar_shard.png';
iconSize = 60/2; // 60 pixels - half that size works better
opacity = 0.6; // these often hide portals - let's make them semi transparent
}
}
// 2014-08-09 - helios artifacts. original guess was slightly wrong
if (data.helios) {
if (data.helios.target) {
// target portal - show the target marker. helios target marker doesn't fill like the earlier jarvis/amar targets
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/helios_shard_target.png';
iconSize = 100/2; // 100 pixels - half that size works better
} else if (data.helios.fragments) {
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/helios_shard.png';
iconSize = 60/2; // 60 pixels - half that size works better
opacity = 0.6; // these often hide portals - let's make them semi transparent
}
}
// 2015-03-05 - shonin shards
if (data.shonin) {
if (data.shonin.target) {
// target portal - show the target marker.
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/shonin_shard_target.png';
iconSize = 100/2; // 100 pixels - half that size works better
} else if (data.shonin.fragments) {
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/shonin_shard.png';
iconSize = 60/2; // 60 pixels - half that size works better
opacity = 0.6; // these often hide portals - let's make them semi transparent
}
}
// 2015-04-22 - lightman fragments (guessed)
if (data.lightman) {
if (data.lightman.target) {
// target portal - show the target marker.
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/lightman_shard_target.png';
iconSize = 100/2; // 100 pixels - half that size works better
} else if (data.lightman.fragments) {
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/lightman_shard.png';
iconSize = 60/2; // 60 pixels - half that size works better
opacity = 0.6; // these often hide portals - let's make them semi transparent
}
}
if (iconUrl) {
var icon = L.icon({
iconUrl: iconUrl,
iconSize: [iconSize,iconSize],
iconAnchor: [iconSize/2,iconSize/2],
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
});
var marker = L.marker (latlng, {icon: icon, clickable: false, keyboard: false, opacity: opacity });
artifact._layer.addLayer(marker);
} else {
console.warn('Oops! no URL for artifact portal icon?!');
}
});
}); //end $.each(artifact.portalInfo, function(guid,data)
}
@ -309,9 +235,9 @@ window.artifact.showArtifactList = function() {
var first = true;
$.each(artifact.artifactTypes, function(type,type2) {
var description = artifact.getArtifactDescriptions(type);
var name = description ? description.title : ('unknown artifact type: '+type);
// no nice way to convert the Niantic internal name into the correct display name
// (we do get the description string once a portal with that shard type is selected - could cache that somewhere?)
var name = type.substr(0,1).toUpperCase() + type.substr(1) + ' shards';
if (!first) html += '<hr>';
first = false;
@ -326,29 +252,33 @@ window.artifact.showArtifactList = function() {
if (type in data) {
// this portal has data for this artifact type - add it to the table
var sortVal = 0;
var onclick = 'zoomToAndShowPortal(\''+guid+'\',['+data._entityData[2]/1E6+','+data._entityData[3]/1E6+'])';
var row = '<tr><td class="portal"><a onclick="'+onclick+'">'+escapeHtmlSpecialChars(data._entityData[8])+'</a></td>';
var onclick = 'zoomToAndShowPortal(\''+guid+'\',['+data._data.latE6/1E6+','+data._data.lngE6/1E6+'])';
var row = '<tr><td class="portal"><a onclick="'+onclick+'">'+escapeHtmlSpecialChars(data._data.title)+'</a></td>';
row += '<td class="info">';
if (data[type].target) {
row += '<span class="target '+TEAM_TO_CSS[data[type].target]+'">'+(data[type].target==TEAM_RES?'Resistance':'Enlightened')+' target</span> ';
sortVal = 100000+data[type].target;
if (data[type].target !== undefined) {
if (data[type].target == TEAM_NONE) {
row += '<span class="target">Target Portal</span> ';
} else {
row += '<span class="target '+TEAM_TO_CSS[data[type].target]+'">'+(data[type].target==TEAM_RES?'Resistance':'Enlightened')+' target</span> ';
}
}
if (data[type].fragments) {
if (data[type].target) {
if (data[type].target !== undefined) {
row += '<br>';
}
var fragmentName = description ? description.fragmentName : 'fragment';
row += '<span class="fragments'+(data[type].target?' '+TEAM_TO_CSS[data[type].target]:'')+'">'+fragmentName+': #'+data[type].fragments.join(', #')+'</span> ';
sortVal = Math.min.apply(null, data[type].fragments); // use min shard number at portal as sort key
var fragmentName = 'shard';
// row += '<span class="fragments'+(data[type].target?' '+TEAM_TO_CSS[data[type].target]:'')+'">'+fragmentName+': #'+data[type].fragments.join(', #')+'</span> ';
row += '<span class="fragments'+(data[type].target?' '+TEAM_TO_CSS[data[type].target]:'')+'">'+fragmentName+': yes</span> ';
}
row += '</td></tr>';
// sort by target portals first, then by portal GUID
var sortVal = (data[type].target !== undefined ? 'A' : 'Z') + guid;
tableRows.push ( [sortVal, row] );
}
});
@ -360,7 +290,9 @@ window.artifact.showArtifactList = function() {
// sort the rows
tableRows.sort(function(a,b) {
return a[0]-b[0];
if (a[0] == b[0]) return 0;
else if (a[0] < b[0]) return -1;
else return 1;
});
// and add them to the table
@ -371,6 +303,13 @@ window.artifact.showArtifactList = function() {
});
html += "<hr />"
+ "<p>In Summer 2015, Niantic changed the data format for artifact portals. We no longer know:</p>"
+ "<ul><li>Which team each target portal is for - only that it is a target</li>"
+ "<li>Which shards are at each portal, just that it has one or more shards</li></ul>"
+ "<p>You can select a portal and the detailed data contains the list of shard numbers, but there's still no"
+ " more information on targets.</p>";
dialog({
title: 'Artifacts',
html: html,

View File

@ -25,7 +25,33 @@
energy: arr[2],
};
}
function parseArtifact(arr) {
function parseArtifactBrief(arr) {
if (arr === null) return null;
// array index 0 is for fragments at the portal. index 1 is for target portals
// each of those is two dimensional - not sure why. part of this is to allow for multiple types of artifacts,
// with their own targets, active at once - but one level for the array is enough for that
// making a guess - first level is for different artifact types, second index would allow for
// extra data for that artifact type
function decodeArtifactArray(arr) {
var result = {};
for (var i=0; i<arr.length; i++) {
// we'll use the type as the key - and store any additional array values as the value
// that will be an empty array for now, so only object keys are useful data
result[arr[i][0]] = arr[i].slice(1);
}
return result;
}
return {
fragment: decodeArtifactArray(arr[0]),
target: decodeArtifactArray(arr[1]),
};
}
function parseArtifactDetail(arr) {
if (arr == null) { return null; }
// empty artifact data is pointless - ignore it
if (arr.length == 3 && arr[0] == "" && arr[1] == "" && arr[2].length == 0) { return null; }
@ -60,7 +86,7 @@
ornaments: a[9],
mission: a[10],
mission50plus: a[11],
unknown12: a[12],
artifactBrief: parseArtifactBrief(a[12]),
timestamp: a[13]
};
};
@ -107,7 +133,7 @@
mods: a[SUMMARY_PORTAL_DATA_LENGTH+0].map(parseMod),
resonators:a[SUMMARY_PORTAL_DATA_LENGTH+1].map(parseResonator),
owner: a[SUMMARY_PORTAL_DATA_LENGTH+2],
artifact: parseArtifact(a[SUMMARY_PORTAL_DATA_LENGTH+3]),
artifactDetail: parseArtifactDetail(a[SUMMARY_PORTAL_DATA_LENGTH+3]),
});
}

View File

@ -184,32 +184,24 @@ window.getPortalMiscDetails = function(guid,d) {
'<span title="force amplifier" class="text-overflow-ellipsis">force amplifier</span>',
'×'+attackValues.force_amplifier]);
// artifact details
// 2014-02-06: stock site changed from supporting 'jarvis shards' to 'amar artifacts'(?) - so let's see what we can do to be generic...
$.each(artifact.getArtifactTypes(),function(index,type) {
var artdata = artifact.getPortalData (guid, type);
if (artdata) {
var details = artifact.getArtifactDescriptions(type);
if (details) {
// the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns
var target = ['',''], shards = [details.fragmentName,'(none)'];
if (artdata.target) {
target = ['target', '<span class="'+TEAM_TO_CSS[artdata.target]+'">'+(artdata.target==TEAM_RES?'Resistance':'Enlightened')+'</span>'];
}
if (artdata.fragments) {
shards = [details.fragmentName, '#'+artdata.fragments.join(', #')];
}
randDetailsData.push (target, shards);
} else {
console.warn('Unknown artifact type '+type+': no names, so cannot display');
}
}
});
randDetails = '<table id="randdetails">' + genFourColumnTable(randDetailsData) + '</table>';
// artifacts - tacked on after (but not as part of) the 'randdetails' table
// instead of using the existing columns....
// fill in target status from the artifact code
var targets = artifact.getPortalTarget(guid);
if (targets) {
//currently (2015-07-10) we no longer know the team each target portal is for - so we'll just show the artifact type(s)
randDetails += '<div id="artifact_target">Target portal: '+Object.keys(targets).map(function(x) { return x.substr(0,1).toUpperCase()+x.substr(1); }).join(', ')+'</div>';
}
// shards - taken directly from the portal details
if (d.artifactDetail) {
randDetails += '<div id="artifact_fragments">Shards: '+d.artifactDetail.displayName+' #'+d.artifactDetail.fragments.join(', ')+'</div>';
}
}
return randDetails;

View File

@ -711,6 +711,15 @@ h3 {
cursor: help;
}
#artifact_target, #artifact_fragments {
margin-top: 4px;
margin-bottom: 4px;
margin-left: 8px;
margin-right: 8px;
}
/* resonators */
#resodetails {
margin-bottom: 0px;