Merge pull request #197 from integ3r/dev
Passcode redemption overhaul: now with plaintext copying, default display, JS console logging, heuristic item inference, and more
This commit is contained in:
commit
f9eec96d65
@ -1,96 +1,227 @@
|
|||||||
|
|
||||||
// REDEEMING /////////////////////////////////////////////////////////
|
// REDEEMING /////////////////////////////////////////////////////////
|
||||||
|
|
||||||
window.handleRedeemResponse = function(data, textStatus, jqXHR) {
|
/* Resource type names mapped to actual names and abbreviations.
|
||||||
if(data.error) {
|
* Add more here if necessary.
|
||||||
var error = '';
|
*/
|
||||||
if(data.error === 'ALREADY_REDEEMED') {
|
window.REDEEM_RESOURCES = {
|
||||||
error = 'The passcode has already been redeemed.';
|
RES_SHIELD: {long: 'Portal Shield', short: 'SH'},
|
||||||
} else if(data.error === 'ALREADY_REDEEMED_BY_PLAYER') {
|
EMITTER_A: {long: 'Resonator', short: 'R'},
|
||||||
error = 'You have already redeemed this passcode.';
|
EMP_BURSTER: {long: 'XMP Burster', short: 'X'},
|
||||||
} else if(data.error === 'INVALID_PASSCODE') {
|
POWER_CUBE: {long: 'Power Cube', short: 'C'}
|
||||||
error = 'This passcode is invalid.';
|
};
|
||||||
} else {
|
|
||||||
error = 'There was a problem redeeming the passcode. Try again?';
|
/* Redemption errors. Very self-explanatory.
|
||||||
|
*/
|
||||||
|
window.REDEEM_ERRORS = {
|
||||||
|
ALREADY_REDEEMED: 'The passcode has already been redeemed.',
|
||||||
|
ALREADY_REDEEMED_BY_PLAYER : 'You have already redeemed this passcode.',
|
||||||
|
INVALID_PASSCODE: 'This passcode is invalid.'
|
||||||
|
};
|
||||||
|
|
||||||
|
/* These are HTTP status codes returned by the redemption API.
|
||||||
|
* TODO: Move to another file? Use more generally across IITC?
|
||||||
|
*/
|
||||||
|
window.REDEEM_STATUSES = {
|
||||||
|
429: 'You have been rate-limited by the server. Wait a bit and try again.',
|
||||||
|
500: 'Internal server error'
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Encouragement for people who got it in.
|
||||||
|
* Just for fun.
|
||||||
|
*/
|
||||||
|
window.REDEEM_ENCOURAGEMENT = [
|
||||||
|
"Passcode accepted!",
|
||||||
|
"Access granted.",
|
||||||
|
"Asset transfer in progress.",
|
||||||
|
"Well done, Agent.",
|
||||||
|
"Make the " + {'RESISTANCE' : 'Resistance', 'ALIENS' : 'Enlightened'}[PLAYER.team] + " proud!"
|
||||||
|
];
|
||||||
|
|
||||||
|
/* Redemption "handlers" handle decoding and formatting for rewards.
|
||||||
|
*
|
||||||
|
* Redemption "decoders" are used for returning the primary attribute (key) from
|
||||||
|
* different types of items. Pretty self-explanatory.
|
||||||
|
*
|
||||||
|
* Redemption "formatters" are used for formatting specific types of password rewards.
|
||||||
|
* Right now, Ingress has resourceWithLevels (leveled resources) and modResource (mods).
|
||||||
|
* Resources with levels have levels, and mods have rarity. Format them appropriately.
|
||||||
|
*/
|
||||||
|
window.REDEEM_HANDLERS = {
|
||||||
|
'resourceWithLevels' : {
|
||||||
|
decode: function(type, resource) {return resource.level;},
|
||||||
|
format: function(acquired, level) {
|
||||||
|
var prefix = '<span style="color: ' + (window.COLORS_LVL[level] || 'white') + ';">';
|
||||||
|
var suffix = '</span>';
|
||||||
|
return {
|
||||||
|
table: '<td>' + prefix + 'L' + level + suffix + '</td><td>' + acquired.name.long + ' [' + acquired.count + ']</td>',
|
||||||
|
html: acquired.count + '×' + acquired.name.short + prefix + level + suffix,
|
||||||
|
plain: acquired.count + '@' + acquired.name.short + level
|
||||||
|
};
|
||||||
}
|
}
|
||||||
alert('<strong>' + data.error + '</strong>\n' + error);
|
},
|
||||||
} else if(data.result) {
|
'modResource' : {
|
||||||
var tblResult = $('<table class="redeem-result" />');
|
decode: function(type, resource) {return resource.rarity;},
|
||||||
tblResult.append($('<tr><th colspan="2">Passcode accepted!</th></tr>'));
|
format: function(acquired, rarity) {
|
||||||
|
var prefix = '<span style="color: ' + (window.COLORS_MOD[rarity] || 'white') + ';">';
|
||||||
if(data.result.apAward)
|
var suffix = '</span>';
|
||||||
tblResult.append($('<tr><td>+</td><td>' + data.result.apAward + 'AP</td></tr>'));
|
var abbreviation = rarity.split('_').map(function (i) {return i[0];}).join('');
|
||||||
if(data.result.xmAward)
|
return {
|
||||||
tblResult.append($('<tr><td>+</td><td>' + data.result.xmAward + 'XM</td></tr>'));
|
table: '<td>' + prefix + abbreviation + suffix + '</td><td>' + acquired.name.long + ' [' + acquired.count + ']</td>',
|
||||||
|
html: acquired.count + '×' + prefix + abbreviation + suffix,
|
||||||
var resonators = {};
|
plain: acquired.count + '@' + abbreviation
|
||||||
var bursts = {};
|
};
|
||||||
var shields = {};
|
}
|
||||||
var cubes = {};
|
},
|
||||||
|
'default' : {
|
||||||
for(var i in data.result.inventoryAward) {
|
decode: function(type, resource) {return 'UNKNOWN';},
|
||||||
var acquired = data.result.inventoryAward[i][2];
|
format: function(acquired, group) {
|
||||||
if(acquired.modResource) {
|
return {
|
||||||
if(acquired.modResource.resourceType === 'RES_SHIELD') {
|
table: '<td>+</td><td>' + acquired.name.long + ' [' + acquired.count + ']</td>',
|
||||||
var rarity = acquired.modResource.rarity.split('_').map(function (i) {return i[0]}).join('');
|
html: acquired.count + '×' + acquired.name.short,
|
||||||
if(!shields[rarity]) shields[rarity] = 0;
|
plain: acquired.count + '@' + acquired.name.short
|
||||||
shields[rarity] += 1;
|
};
|
||||||
}
|
|
||||||
} else if(acquired.resourceWithLevels) {
|
|
||||||
if(acquired.resourceWithLevels.resourceType === 'EMITTER_A') {
|
|
||||||
var level = acquired.resourceWithLevels.level
|
|
||||||
if(!resonators[level]) resonators[level] = 0;
|
|
||||||
resonators[level] += 1;
|
|
||||||
} else if(acquired.resourceWithLevels.resourceType === 'EMP_BURSTER') {
|
|
||||||
var level = acquired.resourceWithLevels.level
|
|
||||||
if(!bursts[level]) bursts[level] = 0;
|
|
||||||
bursts[level] += 1;
|
|
||||||
} else if(acquired.resourceWithLevels.resourceType === 'POWER_CUBE') {
|
|
||||||
var level = acquired.resourceWithLevels.level
|
|
||||||
if(!cubes[level]) cubes[level] = 0;
|
|
||||||
cubes[level] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$.each(resonators, function(lvl, count) {
|
|
||||||
var text = 'Resonator';
|
|
||||||
if(count >= 2) text += ' ('+count+')';
|
|
||||||
tblResult.append($('<tr ><td style="color: ' +window.COLORS_LVL[lvl]+ ';">L' +lvl+ '</td><td>' + text + '</td></tr>'));
|
|
||||||
});
|
|
||||||
$.each(bursts, function(lvl, count) {
|
|
||||||
var text = 'Xmp Burster';
|
|
||||||
if(count >= 2) text += ' ('+count+')';
|
|
||||||
tblResult.append($('<tr ><td style="color: ' +window.COLORS_LVL[lvl]+ ';">L' +lvl+ '</td><td>' + text + '</td></tr>'));
|
|
||||||
});
|
|
||||||
$.each(cubes, function(lvl, count) {
|
|
||||||
var text = 'Power Cube';
|
|
||||||
if(count >= 2) text += ' ('+count+')';
|
|
||||||
tblResult.append($('<tr ><td style="color: ' +window.COLORS_LVL[lvl]+ ';">L' +lvl+ '</td><td>' + text + '</td></tr>'));
|
|
||||||
});
|
|
||||||
$.each(shields, function(lvl, count) {
|
|
||||||
var text = 'Portal Shield';
|
|
||||||
if(count >= 2) text += ' ('+count+')';
|
|
||||||
tblResult.append($('<tr><td>'+lvl+'</td><td>'+text+'</td></tr>'));
|
|
||||||
});
|
|
||||||
alert(tblResult, true);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Redemption "hints" hint at what an unknown resource might be from its object properties.
|
||||||
|
*/
|
||||||
|
window.REDEEM_HINTS = {
|
||||||
|
level: 'resourceWithLevels',
|
||||||
|
rarity: 'modResource'
|
||||||
|
};
|
||||||
|
|
||||||
|
window.handleRedeemResponse = function(data, textStatus, jqXHR) {
|
||||||
|
var passcode = this.passcode, to_alert, to_log;
|
||||||
|
|
||||||
|
if(data.error) {
|
||||||
|
to_alert = '<strong>' + data.error + '</strong><br />' + (window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?');
|
||||||
|
to_log = '[ERROR] ' + data.error;
|
||||||
|
} else if(data.result) {
|
||||||
|
var encouragement = window.REDEEM_ENCOURAGEMENT[Math.floor(Math.random() * window.REDEEM_ENCOURAGEMENT.length)];
|
||||||
|
var payload = {};
|
||||||
|
var inferred = [];
|
||||||
|
var results = {
|
||||||
|
'table' : ['<th colspan="2" style="text-align: left;"><strong>' + encouragement + '</strong></th>'],
|
||||||
|
'html' : [],
|
||||||
|
'plain' : []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track frequencies and levels of items
|
||||||
|
$.each(data.result.inventoryAward, function (award_idx, award) {
|
||||||
|
var acquired = award[2], handler, type, key, name;
|
||||||
|
|
||||||
|
// The "what the heck is this item" heuristic
|
||||||
|
$.each(acquired, function (taxonomy, resource) {
|
||||||
|
if('resourceType' in resource) {
|
||||||
|
if(taxonomy in window.REDEEM_HANDLERS) {
|
||||||
|
// Cool. We know how to directly handle this item.
|
||||||
|
handler = {
|
||||||
|
functions: window.REDEEM_HANDLERS[taxonomy],
|
||||||
|
taxonomy: taxonomy,
|
||||||
|
processed_as: taxonomy
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Let's see if we can get a hint for how we should handle this.
|
||||||
|
$.each(resource, function (resource_key, resource_value) {
|
||||||
|
if(resource_key in window.REDEEM_HINTS) {
|
||||||
|
// We're not sure what this item is, but we can process it like another item
|
||||||
|
handler = {
|
||||||
|
functions: (window.REDEEM_HANDLERS[window.REDEEM_HINTS[resource_key]] || window.REDEEM_HANDLERS['default']),
|
||||||
|
taxonomy: taxonomy,
|
||||||
|
processed_as: window.REDEEM_HINTS[resource_key]
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fall back to the default handler if necessary
|
||||||
|
handler = handler || {
|
||||||
|
functions: window.REDEEM_HANDLERS['default'],
|
||||||
|
taxonomy: taxonomy,
|
||||||
|
processed_as: 'default'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the data that we know
|
||||||
|
type = resource.resourceType;
|
||||||
|
key = handler.functions.decode(type, resource);
|
||||||
|
name = window.REDEEM_RESOURCES[type] || {long: type, short: type[0]};
|
||||||
|
|
||||||
|
// Decide if we inferred this resource
|
||||||
|
if(!(type in window.REDEEM_RESOURCES) || handler.taxonomy !== handler.processed_as) {
|
||||||
|
name.long += '*';
|
||||||
|
name.short += '*';
|
||||||
|
inferred.push({type: type, key: key, handler: handler});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update frequencies
|
||||||
|
payload[type] = payload[type] || {};
|
||||||
|
payload[type][key] = payload[type][key] || {};
|
||||||
|
payload[type][key].handler = payload[type][key].handler || handler;
|
||||||
|
payload[type][key].type = payload[type][key].type || type;
|
||||||
|
payload[type][key].name = payload[type][key].name || name;
|
||||||
|
payload[type][key].count = payload[type][key].count || 0;
|
||||||
|
payload[type][key].count += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get AP and XM.
|
||||||
|
$.each([{label: 'AP', award: parseInt(data.result.apAward)}, {label: 'XM', award: parseInt(data.result.xmAward)}], function(idx, val) {
|
||||||
|
if(val.award > 0) {
|
||||||
|
results.table.push('<td>+</td><td>' + digits(val.award) + ' ' + val.label + '</td>');
|
||||||
|
results.html.push(val.award + ' ' + val.label);
|
||||||
|
results.plain.push(val.award + ' ' + val.label);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build the formatted results alphabetically
|
||||||
|
$.each(Object.keys(payload).sort(), function(type_idx, type) {
|
||||||
|
$.each(Object.keys(payload[type]).sort(), function(key_idx, key) {
|
||||||
|
var acquired = payload[type][key];
|
||||||
|
$.each(acquired.handler.functions.format(acquired, key), function(format, string) {
|
||||||
|
results[format].push(string);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Let the user know if we had to guess
|
||||||
|
if (inferred.length > 0) {
|
||||||
|
results.table.push('<td style="font-family: monospace;">*</td><td style="font-family: monospace;">Guessed (check console)</td>');
|
||||||
|
$.each(inferred, function (idx, val) {
|
||||||
|
console.log(passcode +
|
||||||
|
' => [INFERRED] ' + val.type + ':' + val.key + ' :: ' +
|
||||||
|
val.handler.taxonomy + ' =~ ' + val.handler.processed_as);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add table footers
|
||||||
|
results.table.push('<td style="font-family: monospace;">></td><td><a href="javascript:alert(\'' +
|
||||||
|
escape('<span style="font-family: monospace;"><strong>' + encouragement + '</strong><br />' + results.html.join('/') + '</span>') +
|
||||||
|
'\', true);" style="font-family: monospace;">[plaintext]</a>');
|
||||||
|
|
||||||
|
// Display formatted versions in a table, plaintext, and the console log
|
||||||
|
to_alert = '<table class="redeem-result">' + results.table.map(function(a) {return '<tr>' + a + '</tr>';}).join("\n") + '</table>';
|
||||||
|
to_log = '[SUCCESS] ' + results.plain.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(to_alert, true);
|
||||||
|
console.log(passcode + ' => ' + to_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.setupRedeem = function() {
|
window.setupRedeem = function() {
|
||||||
$("#redeem").keypress(function(e) {
|
$("#redeem").keypress(function(e) {
|
||||||
if((e.keyCode ? e.keyCode : e.which) != 13) return;
|
if((e.keyCode ? e.keyCode : e.which) !== 13) return;
|
||||||
var data = {passcode: $(this).val()};
|
var data = {passcode: $(this).val()};
|
||||||
|
|
||||||
window.postAjax('redeemReward', data, window.handleRedeemResponse,
|
window.postAjax('redeemReward', data, window.handleRedeemResponse,
|
||||||
function(response) {
|
function(response) {
|
||||||
var extra = '';
|
var extra = '';
|
||||||
if(response && response.status) {
|
if(response.status) {
|
||||||
if(response.status === 429) {
|
extra = (window.REDEEM_STATUSES[response.status] || 'The server indicated an error.') + ' (HTTP ' + response.status + ')';
|
||||||
extra = 'You have been rate-limited by the server. Wait a bit and try again.';
|
|
||||||
} else {
|
|
||||||
extra = 'The server indicated an error.';
|
|
||||||
}
|
|
||||||
extra += '\nResponse: HTTP <a href="http://httpstatus.es/' + response.status + '" alt="HTTP ' + response.status + '">' + response.status + '</a>.';
|
|
||||||
} else {
|
} else {
|
||||||
extra = 'No status code was returned.';
|
extra = 'No status code was returned.';
|
||||||
}
|
}
|
||||||
|
@ -82,13 +82,14 @@ window.digits = function(d) {
|
|||||||
// able arguments: http://api.jquery.com/jQuery.ajax/
|
// able arguments: http://api.jquery.com/jQuery.ajax/
|
||||||
// error: see above. Additionally it is logged if the request failed.
|
// error: see above. Additionally it is logged if the request failed.
|
||||||
window.postAjax = function(action, data, success, error) {
|
window.postAjax = function(action, data, success, error) {
|
||||||
data = JSON.stringify($.extend({method: 'dashboard.'+action}, data));
|
var post_data = JSON.stringify($.extend({method: 'dashboard.'+action}, data));
|
||||||
var remove = function(data, textStatus, jqXHR) { window.requests.remove(jqXHR); };
|
var remove = function(data, textStatus, jqXHR) { window.requests.remove(jqXHR); };
|
||||||
var errCnt = function(jqXHR) { window.failedRequestCount++; window.requests.remove(jqXHR); };
|
var errCnt = function(jqXHR) { window.failedRequestCount++; window.requests.remove(jqXHR); };
|
||||||
var result = $.ajax({
|
var result = $.ajax({
|
||||||
url: '/rpc/dashboard.'+action,
|
url: '/rpc/dashboard.'+action,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: data,
|
data: post_data,
|
||||||
|
context: data,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: [remove, success],
|
success: [remove, success],
|
||||||
error: error ? [errCnt, error] : errCnt,
|
error: error ? [errCnt, error] : errCnt,
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user