= 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.
This commit is contained in:
		| @@ -2,14 +2,136 @@ | |||||||
| // Heuristic passcode redemption that tries to guess unknown items / | // 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. | /* Resource type names mapped to actual names and abbreviations. | ||||||
|  * Add more here if necessary. |  * Add more here if necessary. | ||||||
|  |  * Sometimes, items will have more information than just a level. Allow for specialization. | ||||||
|  */ |  */ | ||||||
| window.REDEEM_RESOURCES = { | window.REDEEM_RESOURCES = { | ||||||
|   RES_SHIELD:  {long: 'Portal Shield', short: 'SH'}, |   RES_SHIELD: { | ||||||
|   EMITTER_A:   {long: 'Resonator', short: 'R'}, |     /* modResource */ | ||||||
|   EMP_BURSTER: {long: 'XMP Burster', short: 'X'}, |     format: function(acquired) { | ||||||
|   POWER_CUBE:  {long: 'Power Cube', short: 'C'} |       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: <a href="' + (acquired.storyItem.primaryUrl || '#') + '" target="_blank">' + (acquired.storyItem.shortDescription || 'UNKNOWN') + '</a>', | ||||||
|  |         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: '<span title="' + (acquired.displayName ? (acquired.displayName.displayDescription || '') : '') + '" class="' + ({ADA: 'res', JARVIS: 'enl'}[type] || '') + '">', | ||||||
|  |         suffix: '</span>' | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   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: '<td>+</td><td>' + prefix + acquired.str.long + suffix + ' [' + acquired.count + ']</td>', | ||||||
|  |         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 = '<span style="color: ' + (window.COLORS_LVL[level] || 'white') + ';">'; | ||||||
|  |       var suffix = '</span>'; | ||||||
|  |       return { | ||||||
|  |         table: '<td>' + prefix + 'L' + level + suffix + '</td><td>' + acquired.str.long + ' [' + acquired.count + ']</td>', | ||||||
|  |         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 = '<span style="color: ' + (window.COLORS_MOD[rarity] || 'white') + ';">'; | ||||||
|  |       var suffix = '</span>'; | ||||||
|  |       var abbreviation = window.REDEEM_ABBREVIATE(rarity); | ||||||
|  |       return { | ||||||
|  |         table: '<td>' + prefix + abbreviation + suffix + '</td><td>' + acquired.str.long + ' [' + acquired.count + ']</td>', | ||||||
|  |         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. | /* Redemption errors. Very self-explanatory. | ||||||
| @@ -34,65 +156,13 @@ window.REDEEM_STATUSES = { | |||||||
| window.REDEEM_ENCOURAGEMENT = [ | window.REDEEM_ENCOURAGEMENT = [ | ||||||
|   "Passcode accepted!", |   "Passcode accepted!", | ||||||
|   "Access granted.", |   "Access granted.", | ||||||
|  |   "Resources acquired.", | ||||||
|  |   "Power up!", | ||||||
|   "Asset transfer in progress.", |   "Asset transfer in progress.", | ||||||
|   "Well done, Agent.", |   "Well done, Agent.", | ||||||
|   "Make the " + {'RESISTANCE' : 'Resistance', 'ALIENS' : 'Enlightened'}[PLAYER.team] + " proud!" |   "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_dialog, to_log, dialog_title, dialog_buttons; |   var passcode = this.passcode, to_dialog, to_log, dialog_title, dialog_buttons; | ||||||
|  |  | ||||||
| @@ -116,11 +186,11 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { | |||||||
|  |  | ||||||
|     // Track frequencies and levels of items |     // Track frequencies and levels of items | ||||||
|     $.each(data.result.inventoryAward, function (award_idx, award) { |     $.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 |       // The "what the heck is this item" heuristic | ||||||
|       $.each(acquired, function (taxonomy, resource) { |       $.each(acquired, function (taxonomy, attribute) { | ||||||
|         if('resourceType' in resource) { |         if('resourceType' in attribute) { | ||||||
|           if(taxonomy in window.REDEEM_HANDLERS) { |           if(taxonomy in window.REDEEM_HANDLERS) { | ||||||
|             // Cool. We know how to directly handle this item. |             // Cool. We know how to directly handle this item. | ||||||
|             handler = { |             handler = { | ||||||
| @@ -130,13 +200,13 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { | |||||||
|             }; |             }; | ||||||
|           } else { |           } else { | ||||||
|             // Let's see if we can get a hint for how we should handle this. |             // Let's see if we can get a hint for how we should handle this. | ||||||
|             $.each(resource, function (resource_key, resource_value) { |             $.each(attribute, function (attribute_key, attribute_value) { | ||||||
|               if(resource_key in window.REDEEM_HINTS) { |               if(attribute_key in window.REDEEM_HINTS) { | ||||||
|                 // We're not sure what this item is, but we can process it like another item |                 // We're not sure what this item is, but we can process it like another item | ||||||
|                 handler = { |                 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, |                   taxonomy: taxonomy, | ||||||
|                   processed_as: window.REDEEM_HINTS[resource_key] |                   processed_as: window.REDEEM_HINTS[attribute_key] | ||||||
|                 }; |                 }; | ||||||
|                 return false; |                 return false; | ||||||
|               } |               } | ||||||
| @@ -144,21 +214,31 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { | |||||||
|  |  | ||||||
|             // Fall back to the default handler if necessary |             // Fall back to the default handler if necessary | ||||||
|             handler = handler || { |             handler = handler || { | ||||||
|               functions: window.REDEEM_HANDLERS['default'], |               functions: window.REDEEM_HANDLERS.resource, | ||||||
|               taxonomy: taxonomy, |               taxonomy: taxonomy, | ||||||
|               processed_as: 'default' |               processed_as: 'resource' | ||||||
|             }; |             }; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           // Collect the data that we know |           // Grab the type | ||||||
|           type = resource.resourceType; |           type = attribute.resourceType; | ||||||
|           key  = handler.functions.decode(type, resource); |  | ||||||
|           name = window.REDEEM_RESOURCES[type] || {long: type, short: type[0]}; |           // 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 |           // Decide if we inferred this resource | ||||||
|           if(!(type in window.REDEEM_RESOURCES) || handler.taxonomy !== handler.processed_as) { |           if(!(type in window.REDEEM_RESOURCES) || handler.taxonomy !== handler.processed_as) { | ||||||
|             name.long  += '*'; |             str.long  += '*'; | ||||||
|             name.short += '*'; |             str.short += '*'; | ||||||
|             inferred.push({type: type, key: key, handler: handler}); |             inferred.push({type: type, key: key, handler: handler}); | ||||||
|           } |           } | ||||||
|           return false; |           return false; | ||||||
| @@ -170,7 +250,7 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { | |||||||
|       payload[type][key] = payload[type][key] || {}; |       payload[type][key] = payload[type][key] || {}; | ||||||
|       payload[type][key].handler = payload[type][key].handler || handler; |       payload[type][key].handler = payload[type][key].handler || handler; | ||||||
|       payload[type][key].type = payload[type][key].type || type; |       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 = payload[type][key].count || 0; | ||||||
|       payload[type][key].count += 1; |       payload[type][key].count += 1; | ||||||
|     }); |     }); | ||||||
| @@ -202,6 +282,7 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { | |||||||
|                     ' => [INFERRED] ' + val.type + ':' + val.key + ' :: ' + |                     ' => [INFERRED] ' + val.type + ':' + val.key + ' :: ' + | ||||||
|                     val.handler.taxonomy + ' =~ ' + val.handler.processed_as); |                     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 |     // Display formatted versions in a table, plaintext, and the console log | ||||||
| @@ -228,7 +309,7 @@ window.handleRedeemResponse = function(data, textStatus, jqXHR) { | |||||||
|     html: to_dialog |     html: to_dialog | ||||||
|   }); |   }); | ||||||
|   console.log(passcode + ' => ' + to_log); |   console.log(passcode + ' => ' + to_log); | ||||||
| } | }; | ||||||
|  |  | ||||||
| window.setupRedeem = function() { | window.setupRedeem = function() { | ||||||
|   $("#redeem").keypress(function(e) { |   $("#redeem").keypress(function(e) { | ||||||
| @@ -249,4 +330,4 @@ window.setupRedeem = function() { | |||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|   }); |   }); | ||||||
| } | }; | ||||||
|   | |||||||
| @@ -1878,12 +1878,13 @@ | |||||||
|                         }, |                         }, | ||||||
|                         "requiredLevel": 6 |                         "requiredLevel": 6 | ||||||
|                     }, |                     }, | ||||||
|                     "doesNotExist": { |                     "resourceWithLevels": { | ||||||
|                         "resourceType": "MEDIA", |                         "resourceType": "MEDIA", | ||||||
|                         "level": 8 |                         "level": 8 | ||||||
|                     }, |                     }, | ||||||
|                     "powerCube": { |                     "storyItem": { | ||||||
|                         "energy": 6000 |                         "shortDescription": "INGRESS_REPORT_42", | ||||||
|  |                         "primaryUrl": "http://www.nianticproject.com" | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             ], |             ], | ||||||
| @@ -1895,13 +1896,38 @@ | |||||||
|                         "playerId": "00000000000000000000000000000000.c", |                         "playerId": "00000000000000000000000000000000.c", | ||||||
|                         "acquisitionTimestampMs": "1365600000000" |                         "acquisitionTimestampMs": "1365600000000" | ||||||
|                     }, |                     }, | ||||||
|                     "modResource": { |                     "accessLevel": { | ||||||
|                         "resourceType": "PORTAL_TURRET", |                         "failure": { | ||||||
|                         "stats": { |                             "isAllowed": false, | ||||||
|                             "MITIGATION": "6" |                             "requiredLevel": 6 | ||||||
|                         }, |                         }, | ||||||
|                         "displayName": "Portal Turret", |                         "requiredLevel": 6 | ||||||
|                         "rarity": "VERY_RARE" |                     }, | ||||||
|  |                     "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,6 +1950,48 @@ | |||||||
|                         "resourceType": "SUPER_SECRET" |                         "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" | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             ] |             ] | ||||||
|         ] |         ] | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user