366 lines
10 KiB
JavaScript
366 lines
10 KiB
JavaScript
// PORTAL DETAILS TOOLS //////////////////////////////////////////////
|
|
// hand any of these functions the details-hash of a portal, and they
|
|
// will return useful, but raw data.
|
|
|
|
// returns a float. Displayed portal level is always rounded down from
|
|
// that value.
|
|
window.getPortalLevel = function(d) {
|
|
var lvl = 0;
|
|
var hasReso = false;
|
|
$.each(d.resonators, function(ind, reso) {
|
|
if(!reso) return true;
|
|
lvl += parseInt(reso.level);
|
|
hasReso = true;
|
|
});
|
|
return hasReso ? Math.max(1, lvl/8) : 0;
|
|
}
|
|
|
|
window.getTotalPortalEnergy = function(d) {
|
|
var nrg = 0;
|
|
$.each(d.resonators, function(ind, reso) {
|
|
if(!reso) return true;
|
|
var level = parseInt(reso.level);
|
|
var max = RESO_NRG[level];
|
|
nrg += max;
|
|
});
|
|
return nrg;
|
|
}
|
|
|
|
// For backwards compatibility
|
|
window.getPortalEnergy = window.getTotalPortalEnergy;
|
|
|
|
window.getCurrentPortalEnergy = function(d) {
|
|
var nrg = 0;
|
|
$.each(d.resonators, function(ind, reso) {
|
|
if(!reso) return true;
|
|
nrg += parseInt(reso.energy);
|
|
});
|
|
return nrg;
|
|
}
|
|
|
|
window.getPortalRange = function(d) {
|
|
// formula by the great gals and guys at
|
|
// http://decodeingress.me/2012/11/18/ingress-portal-levels-and-link-range/
|
|
|
|
var lvl = 0;
|
|
var resoMissing = false;
|
|
// currently we get a short resonator array when some are missing
|
|
if (d.resonators.length < 8) {
|
|
resoMissing = true;
|
|
}
|
|
// but in the past we used to always get an array of 8, but will 'null' objects for some entries. maybe that will return?
|
|
$.each(d.resonators, function(ind, reso) {
|
|
if(!reso) {
|
|
resoMissing = true;
|
|
return;
|
|
}
|
|
});
|
|
|
|
var range = {
|
|
base: 160*Math.pow(getPortalLevel(d), 4),
|
|
boost: getLinkAmpRangeBoost(d)
|
|
};
|
|
|
|
range.range = range.boost * range.base;
|
|
range.isLinkable = !resoMissing;
|
|
|
|
return range;
|
|
}
|
|
|
|
window.getLinkAmpRangeBoost = function(d) {
|
|
// additional range boost calculation
|
|
|
|
// link amps scale: first is full, second a quarter, the last two an eighth
|
|
var scale = [1.0, 0.25, 0.125, 0.125];
|
|
|
|
var boost = 0.0; // initial boost is 0.0 (i.e. no boost over standard range)
|
|
|
|
var linkAmps = getPortalModsByType(d, 'LINK_AMPLIFIER');
|
|
|
|
linkAmps.forEach(function(mod, i) {
|
|
// link amp stat LINK_RANGE_MULTIPLIER is 2000 for rare, and gives 2x boost to the range
|
|
// and very-rare is 7000 and gives 7x the range
|
|
var baseMultiplier = mod.stats.LINK_RANGE_MULTIPLIER/1000;
|
|
boost += baseMultiplier*scale[i];
|
|
});
|
|
|
|
return (linkAmps.length > 0) ? boost : 1.0;
|
|
}
|
|
|
|
|
|
window.getAttackApGain = function(d,fieldCount,linkCount) {
|
|
if (!fieldCount) fieldCount = 0;
|
|
|
|
var resoCount = 0;
|
|
var maxResonators = MAX_RESO_PER_PLAYER.slice(0);
|
|
var curResonators = [ 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
for(var n = PLAYER.level + 1; n < 9; n++) {
|
|
maxResonators[n] = 0;
|
|
}
|
|
$.each(d.resonators, function(ind, reso) {
|
|
if(!reso)
|
|
return true;
|
|
resoCount += 1;
|
|
var reslevel=parseInt(reso.level);
|
|
if(reso.owner === PLAYER.nickname) {
|
|
if(maxResonators[reslevel] > 0) {
|
|
maxResonators[reslevel] -= 1;
|
|
}
|
|
} else {
|
|
curResonators[reslevel] += 1;
|
|
}
|
|
});
|
|
|
|
|
|
var resoAp = resoCount * DESTROY_RESONATOR;
|
|
var linkAp = linkCount * DESTROY_LINK;
|
|
var fieldAp = fieldCount * DESTROY_FIELD;
|
|
var destroyAp = resoAp + linkAp + fieldAp;
|
|
var captureAp = CAPTURE_PORTAL + 8 * DEPLOY_RESONATOR + COMPLETION_BONUS;
|
|
var enemyAp = destroyAp + captureAp;
|
|
var deployCount = 8 - resoCount;
|
|
var completionAp = (deployCount > 0) ? COMPLETION_BONUS : 0;
|
|
var upgradeCount = 0;
|
|
var upgradeAvailable = maxResonators[8];
|
|
for(var n = 7; n >= 0; n--) {
|
|
upgradeCount += curResonators[n];
|
|
if(upgradeAvailable < upgradeCount) {
|
|
upgradeCount -= (upgradeCount - upgradeAvailable);
|
|
}
|
|
upgradeAvailable += maxResonators[n];
|
|
}
|
|
var friendlyAp = deployCount * DEPLOY_RESONATOR + upgradeCount * UPGRADE_ANOTHERS_RESONATOR + completionAp;
|
|
return {
|
|
friendlyAp: friendlyAp,
|
|
deployCount: deployCount,
|
|
upgradeCount: upgradeCount,
|
|
enemyAp: enemyAp,
|
|
destroyAp: destroyAp,
|
|
resoAp: resoAp,
|
|
captureAp: captureAp
|
|
};
|
|
}
|
|
|
|
//This function will return the potential level a player can upgrade it to
|
|
window.potentialPortalLevel = function(d) {
|
|
var current_level = getPortalLevel(d);
|
|
var potential_level = current_level;
|
|
|
|
if(PLAYER.team === d.team) {
|
|
var resonators_on_portal = d.resonators;
|
|
var resonator_levels = new Array();
|
|
// figure out how many of each of these resonators can be placed by the player
|
|
var player_resontators = new Array();
|
|
for(var i=1;i<=MAX_PORTAL_LEVEL; i++) {
|
|
player_resontators[i] = i > PLAYER.level ? 0 : MAX_RESO_PER_PLAYER[i];
|
|
}
|
|
$.each(resonators_on_portal, function(ind, reso) {
|
|
if(reso !== null && reso.owner === window.PLAYER.nickname) {
|
|
player_resontators[reso.level]--;
|
|
}
|
|
resonator_levels.push(reso === null ? 0 : reso.level);
|
|
});
|
|
|
|
resonator_levels.sort(function(a, b) {
|
|
return(a - b);
|
|
});
|
|
|
|
// Max out portal
|
|
var install_index = 0;
|
|
for(var i=MAX_PORTAL_LEVEL;i>=1; i--) {
|
|
for(var install = player_resontators[i]; install>0; install--) {
|
|
if(resonator_levels[install_index] < i) {
|
|
resonator_levels[install_index] = i;
|
|
install_index++;
|
|
}
|
|
}
|
|
}
|
|
//console.log(resonator_levels);
|
|
potential_level = resonator_levels.reduce(function(a, b) {return a + b;}) / 8;
|
|
}
|
|
return(potential_level);
|
|
}
|
|
|
|
|
|
window.fixPortalImageUrl = function(url) {
|
|
if (url) {
|
|
if (window.location.protocol === 'https:') {
|
|
url = url.indexOf('www.panoramio.com') !== -1
|
|
? url.replace(/^http:\/\/www/, 'https://ssl').replace('small', 'medium')
|
|
: url.replace(/^http:\/\//, '//');
|
|
}
|
|
return url;
|
|
} else {
|
|
return DEFAULT_PORTAL_IMG;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
window.getPortalModsByType = function(d, type) {
|
|
var mods = [];
|
|
|
|
var typeToStat = {
|
|
RES_SHIELD: 'MITIGATION',
|
|
FORCE_AMP: 'FORCE_AMPLIFIER',
|
|
TURRET: 'HIT_BONUS', // and/or ATTACK_FREQUENCY??
|
|
HEATSINK: 'HACK_SPEED',
|
|
MULTIHACK: 'BURNOUT_INSULATION',
|
|
LINK_AMPLIFIER: 'LINK_RANGE_MULTIPLIER',
|
|
ULTRA_LINK_AMP: 'OUTGOING_LINKS_BONUS', // and/or LINK_DEFENSE_BOOST??
|
|
};
|
|
|
|
var stat = typeToStat[type];
|
|
|
|
$.each(d.mods || [], function(i,mod) {
|
|
if (mod && mod.stats.hasOwnProperty(stat)) mods.push(mod);
|
|
});
|
|
|
|
|
|
// sorting mods by the stat keeps code simpler, when calculating combined mod effects
|
|
mods.sort (function(a,b) {
|
|
return b.stats[stat] - a.stats[stat];
|
|
});
|
|
|
|
return mods;
|
|
}
|
|
|
|
|
|
|
|
window.getPortalShieldMitigation = function(d) {
|
|
var shields = getPortalModsByType(d, 'RES_SHIELD');
|
|
|
|
var mitigation = 0;
|
|
$.each(shields, function(i,s) {
|
|
mitigation += parseInt(s.stats.MITIGATION);
|
|
});
|
|
|
|
return mitigation;
|
|
}
|
|
|
|
window.getPortalLinksMitigation = function(linkCount) {
|
|
var mitigation = Math.round(400/9*Math.atan(linkCount/Math.E));
|
|
return mitigation;
|
|
}
|
|
|
|
window.getPortalMitigationDetails = function(d,linkCount) {
|
|
var mitigation = {
|
|
shields: getPortalShieldMitigation(d),
|
|
links: getPortalLinksMitigation(linkCount)
|
|
};
|
|
|
|
// mitigation is limited to 95% (as confirmed by Brandon Badger on G+)
|
|
mitigation.total = Math.min(95, mitigation.shields+mitigation.links);
|
|
|
|
mitigation.excess = (mitigation.shields+mitigation.links) - mitigation.total;
|
|
|
|
return mitigation;
|
|
}
|
|
|
|
window.getMaxOutgoingLinks = function(d) {
|
|
var linkAmps = getPortalModsByType(d, 'ULTRA_LINK_AMP');
|
|
|
|
var links = 8;
|
|
|
|
linkAmps.forEach(function(mod, i) {
|
|
links += parseInt(mod.stats.OUTGOING_LINKS_BONUS);
|
|
});
|
|
|
|
return links;
|
|
};
|
|
|
|
window.getPortalHackDetails = function(d) {
|
|
|
|
var heatsinks = getPortalModsByType(d, 'HEATSINK');
|
|
var multihacks = getPortalModsByType(d, 'MULTIHACK');
|
|
|
|
// first mod of type is fully effective, the others are only 50% effective
|
|
var effectivenessReduction = [ 1, 0.5, 0.5, 0.5 ];
|
|
|
|
var cooldownTime = 300; // 5 mins - 300 seconds
|
|
|
|
$.each(heatsinks, function(index,mod) {
|
|
var hackSpeed = parseInt(mod.stats.HACK_SPEED)/1000000;
|
|
cooldownTime = Math.round(cooldownTime * (1 - hackSpeed * effectivenessReduction[index]));
|
|
});
|
|
|
|
var numHacks = 4; // default hacks
|
|
|
|
$.each(multihacks, function(index,mod) {
|
|
var extraHacks = parseInt(mod.stats.BURNOUT_INSULATION);
|
|
numHacks = numHacks + (extraHacks * effectivenessReduction[index]);
|
|
});
|
|
|
|
return {cooldown: cooldownTime, hacks: numHacks, burnout: cooldownTime*(numHacks-1)};
|
|
}
|
|
|
|
// given a detailed portal structure, return summary portal data, as seen in the map tile data
|
|
window.getPortalSummaryData = function(d) {
|
|
|
|
// NOTE: the summary data reports unclaimed portals as level 1 - not zero as elsewhere in IITC
|
|
var level = parseInt(getPortalLevel(d));
|
|
if (level == 0) level = 1; //niantic returns neutral portals as level 1, not 0 as used throughout IITC elsewhere
|
|
|
|
var resCount = 0;
|
|
if (d.resonators) {
|
|
for (var x in d.resonators) {
|
|
if (d.resonators[x]) resCount++;
|
|
}
|
|
}
|
|
var maxEnergy = getTotalPortalEnergy(d);
|
|
var curEnergy = getCurrentPortalEnergy(d);
|
|
var health = maxEnergy>0 ? parseInt(curEnergy/maxEnergy*100) : 0;
|
|
|
|
return {
|
|
level: level,
|
|
title: d.title,
|
|
image: d.image,
|
|
resCount: resCount,
|
|
latE6: d.latE6,
|
|
health: health,
|
|
team: d.team,
|
|
lngE6: d.lngE6,
|
|
type: 'portal'
|
|
};
|
|
}
|
|
|
|
window.getPortalAttackValues = function(d) {
|
|
var forceamps = getPortalModsByType(d, 'FORCE_AMP');
|
|
var turrets = getPortalModsByType(d, 'TURRET');
|
|
|
|
// at the time of writing, only rare force amps and turrets have been seen in the wild, so there's a little guesswork
|
|
// at how the stats work and combine
|
|
// algorithm has been compied from getLinkAmpRangeBoost
|
|
// FIXME: only extract stats and put the calculation in a method to be used for link range, force amplifier and attack
|
|
// frequency
|
|
// note: scanner shows rounded values (adding a second FA shows: 2.5x+0.2x=2.8x, which should be 2.5x+0.25x=2.75x)
|
|
|
|
// amplifier scale: first is full, second a quarter, the last two an eighth
|
|
var scale = [1.0, 0.25, 0.125, 0.125];
|
|
|
|
var attackValues = {
|
|
hit_bonus: 0,
|
|
force_amplifier: 0,
|
|
attack_frequency: 0,
|
|
};
|
|
|
|
forceamps.forEach(function(mod, i) {
|
|
// force amp stat FORCE_AMPLIFIER is 2000 for rare, and gives 2x boost to the range
|
|
var baseMultiplier = mod.stats.FORCE_AMPLIFIER / 1000;
|
|
attackValues.force_amplifier += baseMultiplier * scale[i];
|
|
});
|
|
|
|
turrets.forEach(function(mod, i) {
|
|
// turret stat ATTACK_FREQUENCY is 2000 for rare, and gives 2x boost to the range
|
|
var baseMultiplier = mod.stats.ATTACK_FREQUENCY / 1000;
|
|
attackValues.attack_frequency += baseMultiplier * scale[i];
|
|
|
|
attackValues.hit_bonus += mod.stats.HIT_BONUS / 10000;
|
|
});
|
|
|
|
return attackValues;
|
|
}
|
|
|
|
|