diff --git a/code/redeeming.js b/code/redeeming.js
index 5a6cc4b9..4b9e6a2b 100644
--- a/code/redeeming.js
+++ b/code/redeeming.js
@@ -1,101 +1,231 @@
// REDEEMING /////////////////////////////////////////////////////////
-window.REDEEM_RES_LONG = {'RES_SHIELD' : 'Portal Shield',
- 'EMITTER_A' : 'Resonator',
- 'EMP_BURSTER' : 'XMP Burster',
- 'POWER_CUBE' : 'Power Cube'};
+/* Resource type names mapped to actual names and abbreviations.
+ * Add more here if necessary.
+ */
+window.REDEEM_RESOURCES = {
+ RES_SHIELD: {long: 'Portal Shield', short: 'SH'},
+ EMITTER_A: {long: 'Resonator', short: 'R'},
+ EMP_BURSTER: {long: 'XMP Burster', short: 'X'},
+ POWER_CUBE: {long: 'Power Cube', short: 'C'}
+};
-window.REDEEM_RES_SHORT = {'RES_SHIELD' : 'S',
- 'EMITTER_A' : 'R',
- 'EMP_BURSTER' : 'X',
- 'POWER_CUBE' : 'C'};
+/* 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.'
+};
-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'
+};
-window.REDEEM_STATUSES = {429 : 'You have been rate-limited by the server. Wait a bit and try again.'};
+/* 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 = '';
+ var suffix = '';
+ return {
+ table: '
' + prefix + 'L' + level + suffix + ' | ' + acquired.name.long + ' [' + acquired.count + '] | ',
+ html: acquired.count + '@' + acquired.name.short + prefix + level + suffix,
+ plain: acquired.count + '@' + acquired.name.short + level
+ };
+ }
+ },
+ 'modResource' : {
+ decode: function(type, resource) {return resource.rarity;},
+ format: function(acquired, rarity) {
+ var prefix = '';
+ var suffix = '';
+ var abbreviation = rarity.split('_').map(function (i) {return i[0];}).join('');
+ return {
+ table: '' + prefix + abbreviation + suffix + ' | ' + acquired.name.long + ' [' + acquired.count + '] | ',
+ html: acquired.count + '@' + prefix + abbreviation + suffix,
+ plain: acquired.count + '@' + abbreviation
+ };
+ }
+ },
+ 'default' : {
+ decode: function(type, resource) {return 'UNKNOWN';},
+ format: function(acquired, group) {
+ return {
+ table: '+ | ' + acquired.name.long + ' [' + acquired.count + '] | ',
+ html: acquired.count + '@' + acquired.name.short,
+ plain: acquired.count + '@' + acquired.name.short
+ };
+ }
+ }
+};
+
+/* 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) {
- // Errors are now in window.REDEEM_ERRORS.
- var error = window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?';
-
- // Show an alert and add a console log
- alert('' + data.error + '\n' + error);
- console.log(this.passcode + ' => [ERROR] ' + data.error);
+ to_alert = '' + data.error + '
' + (window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?');
+ to_log = '[ERROR] ' + data.error;
} else if(data.result) {
- // Successful redemption
- var payload = {};
- var table_result = ['Passcode accepted! | '], plain_result = [];
- var table = '', plain = '';
-
- // Get AP, XM, and other static quantities
- var scores = [[parseInt(data.result.apAward), 'AP'], [parseInt(data.result.xmAward), 'XM']];
- for(var i in scores) {
- if(scores[i][0] > 0) {
- table_result.push('+ | ' + scores[i][0] + ' ' + scores[i][1] + ' | ');
- plain_result.push(scores[i][0] + ' ' + scores[i][1]);
- }
- }
+ var payload = {};
+ var encouragement = window.REDEEM_ENCOURAGEMENT[Math.floor(Math.random() * window.REDEEM_ENCOURAGEMENT.length)];
+ var inferred = [];
+ var results = {
+ 'table' : ['' + encouragement + ' | '],
+ 'html' : [],
+ 'plain' : []
+ };
// Track frequencies and levels of items
- for(var i in data.result.inventoryAward) {
- var acquired = data.result.inventoryAward[i][2], primary, secondary, type;
- if(acquired.modResource) {
- primary = acquired.modResource.resourceType;
- secondary = acquired.modResource.rarity;
- type = 'mod';
- } else if(acquired.resourceWithLevels) {
- primary = acquired.resourceWithLevels.resourceType;
- secondary = parseInt(acquired.resourceWithLevels.level);
- type = 'leveled';
- }
+ $.each(data.result.inventoryAward, function (award_idx, award) {
+ var acquired = award[2], handler, type, key, name;
- payload[primary] = payload[primary] || {};
- payload[primary][secondary] = payload[primary][secondary] || {};
- payload[primary][secondary].type = payload[primary][secondary].type || type;
- payload[primary][secondary].count = payload[primary][secondary].count || 0;
- payload[primary][secondary].count += 1;
+ // 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;
+ }
+ return true;
+ });
+
+ // 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) {
+ inferred.push({type: type, key: key, handler: handler});
+ }
+ return false;
+ }
+ return true;
+ });
+
+ // 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, XM, and other static quantities
+ $.each([{label: 'AP', award: parseInt(data.result.apAward)}, {label: 'XM', award: parseInt(data.result.xmAward)}], function(idx, val) {
+ if(val.award > 0) {
+ var formatted = val.award + ' ' + val.label;
+ results.table.push('+ | ' + formatted + ' | ');
+ results.html.push(formatted);
+ results.plain.push(formatted);
+ }
+ return true;
+ });
+
+ // 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);
+ return true;
+ });
+ return true;
+ });
+ return true;
+ });
+
+ if (inferred.length > 0) {
+ results.table.push('** | IITC had to guess! | ');
+ results.table.push('** | Submit a log including: | ');
+ $.each(inferred, function (idx, val) {
+ var type = val.type + ':' + val.key, taxonomy = val.handler.taxonomy + ' =~ ' + val.handler.processed_as;
+ results.table.push('! | ' + type + ' | ');
+ results.table.push('! | ' + taxonomy + ' | ');
+ console.log(passcode + ' => [INFERRED] ' + type + ' :: ' + taxonomy);
+ });
}
- // Build the table and plaintext arrays
- var keys = Object.keys(payload).sort();
- for(var k in keys) {
- var primary = payload[keys[k]], long_name = window.REDEEM_RES_LONG[keys[k]] || keys[k], short_name = window.REDEEM_RES_SHORT[keys[k]] || '?';
- var table_array = [], plain_array = [];
- for(var secondary in primary) {
- var acquired = primary[secondary];
- var span_prefix = acquired.type === 'leveled' ? '' : '';
- var span_infix = acquired.type === 'leveled' ? secondary : secondary.split('_').map(function (i) {return i[0];}).join('');
- var span_suffix = ''
- table_array.push('' + span_prefix + (acquired.type === 'leveled' ? 'L' : '') + span_infix + span_suffix + ' | ' + long_name + ' [' + primary[secondary].count + '] | ');
- plain_array.push(primary[secondary].count + '@' + (acquired.type === 'leveled' ? short_name : '') + span_prefix + span_infix + span_suffix);
- }
- table_result.push(table_array.join(''));
- plain_result.push(plain_array.join('/'));
- }
-
- // Add more HTML tags
- plain = '' + plain_result.join('/') + '';
- table_result.push('>> | [plaintext]');
- table = '' + table_result.map(function(a) {return '' + a + ' ';}).join("\n") + ' ';
+ // Add table footers
+ results.table.push(' | >> | ' + encouragement + ' ' + results.html.join('/') + '') +
+ '\', true);" style="font-family: monospace;">[plaintext]');
// Display formatted versions in a table, plaintext, and the console log
- alert(table, true);
- console.log(this.passcode + ' => ' + $(plain).text());
+ to_alert = '' + results.table.map(function(a) {return '' + a + ' ';}).join("\n") + ' ';
+ to_log = results.plain.join('/');
}
+
+ alert(to_alert, true);
+ console.log(passcode + ' => ' + to_log);
}
window.setupRedeem = function() {
$("#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()};
window.postAjax('redeemReward', data, window.handleRedeemResponse,
function(response) {
- var extra = ''
+ var extra = '';
if(response.status) {
extra = (window.REDEEM_STATUSES[response.status] || 'The server indicated an error.') + ' (HTTP ' + response.status + ')';
} else {
|