From 1b2baf5a8809cbc0229cf90b5ea4e58017db9f44 Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Sat, 25 May 2013 00:02:15 -0600 Subject: [PATCH] = Redemption tweaks, round 3 * Pass the entire acquired item into resource decoders instead of the attribute in question. Generic 'resource' objects have things we need to examine. * Formatted support for the `resource' taxonomy (portal keys and JARVIS/ADA). Remove standard 'default' taxonomy in favor of this. * Let individual items override their own primary key (for example, JARVIS/ADA are both a FLIP_CARD, but we don't want to smash them together) * Let the server-side item name override the "hard-coded" name in IITC * Assuming that passcodes can eventually work like portal hacks, add support for the MEDIA and PORTAL_LINK_KEY item types. * Change plaintext and HTML display for modResources in anticipation of link amplifiers * Log entire JSON response to the console if we have to guess about items. * Add more encouragement. * Add Jarvis/ADA JSON responses, fake media items, and fake portal keys to json_examples. --- code/redeeming.js | 237 +++++++++++++++++++++++++------------ json_examples/redeeming.js | 88 ++++++++++++-- 2 files changed, 237 insertions(+), 88 deletions(-) diff --git a/code/redeeming.js b/code/redeeming.js index e5efa643..11c66105 100644 --- a/code/redeeming.js +++ b/code/redeeming.js @@ -2,14 +2,136 @@ // Heuristic passcode redemption that tries to guess unknown items / //////////////////////////////////////////////////////////////////// +/* Abbreviates redemption items. + * Example: VERY_RARE => VR + */ +window.REDEEM_ABBREVIATE = function(tag) {return tag.split('_').map(function (i) {return i[0];}).join('');}; + /* Resource type names mapped to actual names and abbreviations. * Add more here if necessary. + * Sometimes, items will have more information than just a level. Allow for specialization. */ 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'} + RES_SHIELD: { + /* modResource */ + format: function(acquired) { + return {long: 'Portal Shield', short: 'S'}; + } + }, + EMITTER_A: { + /* resourceWithLevels */ + format: function(acquired) { + return {long: 'Resonator', short: 'R'}; + } + }, + EMP_BURSTER: { + /* resourceWithLevels */ + format: function(acquired) { + return {long: 'XMP Burster', short: 'X'}; + } + }, + POWER_CUBE: { + /* resourceWithLevels */ + format: function(acquired) { + return {long: 'Power Cube', short: 'C'}; + } + }, + MEDIA: { + /* resourceWithLevels */ + format: function(acquired) { + return { + long: 'Media: ' + (acquired.storyItem.shortDescription || 'UNKNOWN') + '', + short: 'M' + }; + } + }, + FLIP_CARD: { + decode: function(type, acquired, key) { + /* ADA or JARVIS */ + return acquired.flipCard.flipCardType; + }, + format: function(acquired) { + var type = acquired.flipCard.flipCardType; + return { + long: type, + short: ({ADA: 'AR', JARVIS: 'JV'}[type] || 'FC'), + prefix: '', + suffix: '' + }; + } + }, + PORTAL_LINK_KEY: { + decode: function(type, acquired, key) { + /* A unique identifier for this portal. */ + return acquired.portalCoupler.portalGuid; + }, + format: function(acquired) { + return { + long: 'Portal Key: ' + acquired.portalCoupler.portalTitle || 'Unknown Portal', + short: 'K' + }; + } + } +}; + +/* 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 = { + resource: { + decode: function(type, acquired, key) {return 'RESOURCE';}, + format: function(acquired, group) { + var prefix = acquired.str.prefix || ''; + var suffix = acquired.str.suffix || ''; + return { + table: '+' + prefix + acquired.str.long + suffix + ' [' + acquired.count + ']', + html: acquired.count + '×' + prefix + acquired.str.short + suffix, + plain: acquired.count + '@' + acquired.str.short + }; + } + }, + + // A leveled resource, such as XMPs, resonators, or power cubes. + resourceWithLevels: { + decode: function(type, acquired, key) {return acquired[key].level;}, + format: function(acquired, level) { + var prefix = ''; + var suffix = ''; + return { + table: '' + prefix + 'L' + level + suffix + '' + acquired.str.long + ' [' + acquired.count + ']', + html: acquired.count + '×' + acquired.str.short + prefix + level + suffix, + plain: acquired.count + '@' + acquired.str.short + level + }; + } + }, + + // A mod, such as portal shields or link amplifiers. + modResource: { + decode: function(type, acquired, key) {return acquired[key].rarity;}, + format: function(acquired, rarity) { + var prefix = ''; + var suffix = ''; + var abbreviation = window.REDEEM_ABBREVIATE(rarity); + return { + table: '' + prefix + abbreviation + suffix + '' + acquired.str.long + ' [' + acquired.count + ']', + html: acquired.count + '×' + acquired.str.short + ':' + prefix + abbreviation + suffix, + plain: acquired.count + '@' + acquired.str.short + ':' + abbreviation + }; + } + } +}; + +/* Redemption "hints" hint at what an unknown resource might be from its object properties. + */ +window.REDEEM_HINTS = { + level: 'resourceWithLevels', + rarity: 'modResource' }; /* Redemption errors. Very self-explanatory. @@ -34,71 +156,19 @@ window.REDEEM_STATUSES = { window.REDEEM_ENCOURAGEMENT = [ "Passcode accepted!", "Access granted.", + "Resources acquired.", + "Power up!", "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_dialog, to_log, dialog_title, dialog_buttons; if(data.error) { // What to display - to_dialog = '' + data.error + '
' + (window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?'); + to_dialog = '' + data.error + '
' +(window.REDEEM_ERRORS[data.error] || 'There was a problem redeeming the passcode. Try again?'); to_log = '[ERROR] ' + data.error; // Dialog options @@ -116,11 +186,11 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { // Track frequencies and levels of items $.each(data.result.inventoryAward, function (award_idx, award) { - var acquired = award[2], handler, type, key, name; + var acquired = award[2], handler, type, resource, key, str; // The "what the heck is this item" heuristic - $.each(acquired, function (taxonomy, resource) { - if('resourceType' in resource) { + $.each(acquired, function (taxonomy, attribute) { + if('resourceType' in attribute) { if(taxonomy in window.REDEEM_HANDLERS) { // Cool. We know how to directly handle this item. handler = { @@ -130,13 +200,13 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { }; } 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) { + $.each(attribute, function (attribute_key, attribute_value) { + if(attribute_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']), + functions: (window.REDEEM_HANDLERS[window.REDEEM_HINTS[attribute_key]] || window.REDEEM_HANDLERS.resource), taxonomy: taxonomy, - processed_as: window.REDEEM_HINTS[resource_key] + processed_as: window.REDEEM_HINTS[attribute_key] }; return false; } @@ -144,21 +214,31 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { // Fall back to the default handler if necessary handler = handler || { - functions: window.REDEEM_HANDLERS['default'], + functions: window.REDEEM_HANDLERS.resource, taxonomy: taxonomy, - processed_as: 'default' + processed_as: 'resource' }; } - // 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]}; + // Grab the type + type = attribute.resourceType; + + // Prefer the resource's native format, falling back to a generic version that applies to an entire taxonomy + resource = $.extend({format: function(acquired) {return {long: type, short: type[0]};}}, window.REDEEM_RESOURCES[type] || {}); + + // Get strings pertaining to this item, using server overrides for the item name if possible + str = $.extend(resource.format(acquired), + acquired.displayName && acquired.displayName.displayName ? { + long: attribute.displayName || acquired.displayName.displayName + } : {}); + + // Get the primary key. Once again, prefer the resource's native format, but use the generic version if we don't have one. + key = (resource.decode || handler.functions.decode)(type, acquired, handler.taxonomy); // Decide if we inferred this resource if(!(type in window.REDEEM_RESOURCES) || handler.taxonomy !== handler.processed_as) { - name.long += '*'; - name.short += '*'; + str.long += '*'; + str.short += '*'; inferred.push({type: type, key: key, handler: handler}); } return false; @@ -170,7 +250,7 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { 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].str = payload[type][key].str || str; payload[type][key].count = payload[type][key].count || 0; payload[type][key].count += 1; }); @@ -195,13 +275,14 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { }); // Let the user know if we had to guess - if (inferred.length > 0) { + if(inferred.length > 0) { results.table.push('*Guessed (check console)'); $.each(inferred, function (idx, val) { console.log(passcode + ' => [INFERRED] ' + val.type + ':' + val.key + ' :: ' + val.handler.taxonomy + ' =~ ' + val.handler.processed_as); }); + console.log(passcode + ' => [RESPONSE] ' + JSON.stringify(data)); } // Display formatted versions in a table, plaintext, and the console log @@ -228,7 +309,7 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { html: to_dialog }); console.log(passcode + ' => ' + to_log); -} +}; window.setupRedeem = function() { $("#redeem").keypress(function(e) { @@ -249,4 +330,4 @@ window.setupRedeem = function() { }); }); }); -} +}; diff --git a/json_examples/redeeming.js b/json_examples/redeeming.js index 4982b052..f42ce914 100644 --- a/json_examples/redeeming.js +++ b/json_examples/redeeming.js @@ -1878,12 +1878,13 @@ }, "requiredLevel": 6 }, - "doesNotExist": { + "resourceWithLevels": { "resourceType": "MEDIA", "level": 8 }, - "powerCube": { - "energy": 6000 + "storyItem": { + "shortDescription": "INGRESS_REPORT_42", + "primaryUrl": "http://www.nianticproject.com" } } ], @@ -1895,13 +1896,38 @@ "playerId": "00000000000000000000000000000000.c", "acquisitionTimestampMs": "1365600000000" }, - "modResource": { - "resourceType": "PORTAL_TURRET", - "stats": { - "MITIGATION": "6" + "accessLevel": { + "failure": { + "isAllowed": false, + "requiredLevel": 6 }, - "displayName": "Portal Turret", - "rarity": "VERY_RARE" + "requiredLevel": 6 + }, + "resourceWithLevels": { + "resourceType": "MEDIA", + "level": 8 + }, + "storyItem": { + "shortDescription": "INGRESS_REPORT_42", + "primaryUrl": "http://www.nianticproject.com" + } + } + ], + [ + "00000000000000000000000000000000.5", + 1365600000000, + { + "inInventory": { + "playerId": "00000000000000000000000000000000.c", + "acquisitionTimestampMs": "1365600000000" + }, + "resource": { + "resourceType": "PORTAL_LINK_KEY", + "displayName": "Portal Key" + }, + "portalCoupler": { + "portalGuid": "00000000000000000000000000000000.7", + "portalTitle": "Portal Name" } } ], @@ -1924,7 +1950,49 @@ "resourceType": "SUPER_SECRET" } } + ], + [ + "00000000000000000000000000000000.5", + 1365600000000, + { + "inInventory": { + "playerId": "00000000000000000000000000000000.c", + "acquisitionTimestampMs": "1365600000000" + }, + "resource": { + "resourceType": "FLIP_CARD", + "resourceRarity": "VERY_RARE" + }, + "displayName": { + "displayDescription": "The ADA Refactor can be used to reverse the alignment of an Enlightened Portal.", + "displayName": "ADA Refactor" + }, + "flipCard": { + "flipCardType": "ADA" + } + } + ], + [ + "00000000000000000000000000000000.5", + 1365600000000, + { + "inInventory": { + "playerId": "00000000000000000000000000000000.c", + "acquisitionTimestampMs": "1365600000000" + }, + "resource": { + "resourceType": "FLIP_CARD", + "resourceRarity": "VERY_RARE" + }, + "displayName": { + "displayDescription": "The JARVIS Virus can be used to reverse the alignment of a Resistance Portal.", + "displayName": "JARVIS Virus" + }, + "flipCard": { + "flipCardType": "JARVIS" + } + } ] ] } -} \ No newline at end of file +}