= IITC Passcode Redemption overhaul, part 2
* Display random words of encouragement for successfully redeemed passcodes * Refactor redeeming into smaller functions, using more jQuery logic * Add redeem "handlers" - combinations of "decoders" and "formatters" for each taxonomy of item * Add heuristic item guessing. If IITC can't figure out directly what an item is, try to guess what taxonomy of item it would be closest to. (For instance, if an item is truly unknown, but has a "rarity" attribute, it's going to be close to a modResource. Likewise, if an unknown item has a "level" attribute, it's going to be close to a resourceWithLevels. ** Tailor formatting and decoding based upon that guess. Worst case: the item is truly a mystery. Don't discard it: display it in a default format. ** Let the user know if we tried to heuristically guess an item. This won't happen unless something changes on the Ingress backend.
This commit is contained in:
parent
a80617e6e7
commit
2d662f9e79
@ -1,101 +1,231 @@
|
|||||||
|
|
||||||
// REDEEMING /////////////////////////////////////////////////////////
|
// REDEEMING /////////////////////////////////////////////////////////
|
||||||
|
|
||||||
window.REDEEM_RES_LONG = {'RES_SHIELD' : 'Portal Shield',
|
/* Resource type names mapped to actual names and abbreviations.
|
||||||
'EMITTER_A' : 'Resonator',
|
* Add more here if necessary.
|
||||||
'EMP_BURSTER' : 'XMP Burster',
|
*/
|
||||||
'POWER_CUBE' : 'Power Cube'};
|
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',
|
/* Redemption errors. Very self-explanatory.
|
||||||
'EMITTER_A' : 'R',
|
*/
|
||||||
'EMP_BURSTER' : 'X',
|
window.REDEEM_ERRORS = {
|
||||||
'POWER_CUBE' : 'C'};
|
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.',
|
/* These are HTTP status codes returned by the redemption API.
|
||||||
'ALREADY_REDEEMED_BY_PLAYER' : 'You have already redeemed this passcode.',
|
* TODO: Move to another file? Use more generally across IITC?
|
||||||
'INVALID_PASSCODE' : 'This passcode is invalid.'};
|
*/
|
||||||
|
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 = '<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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'modResource' : {
|
||||||
|
decode: function(type, resource) {return resource.rarity;},
|
||||||
|
format: function(acquired, rarity) {
|
||||||
|
var prefix = '<span style="color: ' + (window.COLORS_MOD[rarity] || 'white') + ';">';
|
||||||
|
var suffix = '</span>';
|
||||||
|
var abbreviation = rarity.split('_').map(function (i) {return i[0];}).join('');
|
||||||
|
return {
|
||||||
|
table: '<td>' + prefix + abbreviation + suffix + '</td><td>' + acquired.name.long + ' [' + acquired.count + ']</td>',
|
||||||
|
html: acquired.count + '@' + prefix + abbreviation + suffix,
|
||||||
|
plain: acquired.count + '@' + abbreviation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'default' : {
|
||||||
|
decode: function(type, resource) {return 'UNKNOWN';},
|
||||||
|
format: function(acquired, group) {
|
||||||
|
return {
|
||||||
|
table: '<td>+</td><td>' + acquired.name.long + ' [' + acquired.count + ']</td>',
|
||||||
|
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) {
|
window.handleRedeemResponse = function(data, textStatus, jqXHR) {
|
||||||
|
var passcode = this.passcode, to_alert, to_log;
|
||||||
|
|
||||||
if(data.error) {
|
if(data.error) {
|
||||||
// Errors are now in window.REDEEM_ERRORS.
|
to_alert = '<strong>' + data.error + '</strong><br />' + (window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?');
|
||||||
var error = window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?';
|
to_log = '[ERROR] ' + data.error;
|
||||||
|
|
||||||
// Show an alert and add a console log
|
|
||||||
alert('<strong>' + data.error + '</strong>\n' + error);
|
|
||||||
console.log(this.passcode + ' => [ERROR] ' + data.error);
|
|
||||||
} else if(data.result) {
|
} else if(data.result) {
|
||||||
// Successful redemption
|
var payload = {};
|
||||||
var payload = {};
|
var encouragement = window.REDEEM_ENCOURAGEMENT[Math.floor(Math.random() * window.REDEEM_ENCOURAGEMENT.length)];
|
||||||
var table_result = ['<th colspan="2"><strong>Passcode accepted!</strong></th>'], plain_result = [];
|
var inferred = [];
|
||||||
var table = '', plain = '';
|
var results = {
|
||||||
|
'table' : ['<th colspan="2" style="text-align: left;"><strong>' + encouragement + '</strong></th>'],
|
||||||
// Get AP, XM, and other static quantities
|
'html' : [],
|
||||||
var scores = [[parseInt(data.result.apAward), 'AP'], [parseInt(data.result.xmAward), 'XM']];
|
'plain' : []
|
||||||
for(var i in scores) {
|
};
|
||||||
if(scores[i][0] > 0) {
|
|
||||||
table_result.push('<td>+</td><td>' + scores[i][0] + ' ' + scores[i][1] + '</td>');
|
|
||||||
plain_result.push(scores[i][0] + ' ' + scores[i][1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track frequencies and levels of items
|
// Track frequencies and levels of items
|
||||||
for(var i in data.result.inventoryAward) {
|
$.each(data.result.inventoryAward, function (award_idx, award) {
|
||||||
var acquired = data.result.inventoryAward[i][2], primary, secondary, type;
|
var acquired = award[2], handler, type, key, name;
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
||||||
payload[primary] = payload[primary] || {};
|
// The "what the heck is this item" heuristic
|
||||||
payload[primary][secondary] = payload[primary][secondary] || {};
|
$.each(acquired, function (taxonomy, resource) {
|
||||||
payload[primary][secondary].type = payload[primary][secondary].type || type;
|
if('resourceType' in resource) {
|
||||||
payload[primary][secondary].count = payload[primary][secondary].count || 0;
|
if(taxonomy in window.REDEEM_HANDLERS) {
|
||||||
payload[primary][secondary].count += 1;
|
// 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('<td>+</td><td>' + formatted + '</td>');
|
||||||
|
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('<td style="font-family: monospace;">**<td style="font-family: monospace;"><strong>IITC had to guess!</strong></td>');
|
||||||
|
results.table.push('<td style="font-family: monospace;">**<td style="font-family: monospace;"><strong>Submit a log including:</strong></td>');
|
||||||
|
$.each(inferred, function (idx, val) {
|
||||||
|
var type = val.type + ':' + val.key, taxonomy = val.handler.taxonomy + ' =~ ' + val.handler.processed_as;
|
||||||
|
results.table.push('<td style="font-family: monospace;">!</td><td style="font-family: monospace;"><em>' + type + '</em></td>');
|
||||||
|
results.table.push('<td style="font-family: monospace;">!</td><td style="font-family: monospace;">' + taxonomy + '</td>');
|
||||||
|
console.log(passcode + ' => [INFERRED] ' + type + ' :: ' + taxonomy);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the table and plaintext arrays
|
// Add table footers
|
||||||
var keys = Object.keys(payload).sort();
|
results.table.push('<td style="font-family: monospace;">>></td><td><a href="javascript:alert(\'' +
|
||||||
for(var k in keys) {
|
escape('<span style="font-family: monospace;"><strong>' + encouragement + '</strong><br />' + results.html.join('/') + '</span>') +
|
||||||
var primary = payload[keys[k]], long_name = window.REDEEM_RES_LONG[keys[k]] || keys[k], short_name = window.REDEEM_RES_SHORT[keys[k]] || '?';
|
'\', true);" style="font-family: monospace;">[plaintext]</a>');
|
||||||
var table_array = [], plain_array = [];
|
|
||||||
for(var secondary in primary) {
|
|
||||||
var acquired = primary[secondary];
|
|
||||||
var span_prefix = acquired.type === 'leveled' ? '<span style="color: ' + window.COLORS_LVL[secondary] + ';">' : '<span style="color: ' + window.COLORS_MOD[secondary] + ';">';
|
|
||||||
var span_infix = acquired.type === 'leveled' ? secondary : secondary.split('_').map(function (i) {return i[0];}).join('');
|
|
||||||
var span_suffix = '</span>'
|
|
||||||
table_array.push('<td>' + span_prefix + (acquired.type === 'leveled' ? 'L' : '') + span_infix + span_suffix + '</td><td>' + long_name + ' [' + primary[secondary].count + ']</td>');
|
|
||||||
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 = '<span style="font-family: monospace;">' + plain_result.join('/') + '</span>';
|
|
||||||
table_result.push('<td style="font-family: monospace;">>></td><td><a href="javascript:alert(\'' + escape(plain) + '\', true);" style="font-family: monospace;">[plaintext]</a>');
|
|
||||||
table = '<table class="redeem-result">' + table_result.map(function(a) {return '<tr>' + a + '</tr>';}).join("\n") + '</table>';
|
|
||||||
|
|
||||||
// Display formatted versions in a table, plaintext, and the console log
|
// Display formatted versions in a table, plaintext, and the console log
|
||||||
alert(table, true);
|
to_alert = '<table class="redeem-result">' + results.table.map(function(a) {return '<tr>' + a + '</tr>';}).join("\n") + '</table>';
|
||||||
console.log(this.passcode + ' => ' + $(plain).text());
|
to_log = 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.status) {
|
if(response.status) {
|
||||||
extra = (window.REDEEM_STATUSES[response.status] || 'The server indicated an error.') + ' (HTTP ' + response.status + ')';
|
extra = (window.REDEEM_STATUSES[response.status] || 'The server indicated an error.') + ' (HTTP ' + response.status + ')';
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user