message: Better handle missing items in loadouts
diff: @@ -205,6 +205,9 @@ function LoadoutService($q, $rootScope, $translate, uuid2, dimItemService, dimSt
  // subclasses have unique IDs, and emblems/shaders/etc are interchangeable.
  function getLoadoutItem(pseudoItem, store) {
  var item = dimItemService.getItem(pseudoItem);
  + if (!item) {
  + return null;
  + }
  if (_.contains(['Class', 'Shader', 'Emblem', 'Emote', 'Ship', 'Horn'], item.type)) {
  item = _.find(store.items, {
  hash: pseudoItem.hash
  @@ -263,20 +266,21 @@ function LoadoutService($q, $rootScope, $translate, uuid2, dimItemService, dimSt
  // Only select stuff that needs to change state
  var totalItems = items.length;
  - items = _.filter(items, function(pseudoItem) {
  - var item = getLoadoutItem(pseudoItem, store);
  - var invalid = !item || !item.equipment;
  - var alreadyThere = item.owner !== store.id ||
  - // Needs to be equipped. Stuff not marked "equip" doesn't
  - // necessarily mean to de-equip it.
  - (pseudoItem.equipped && !item.equipped);
  -
  + items = items.filter(function(pseudoItem) {
  + const item = getLoadoutItem(pseudoItem, store);
  + const invalid = !item || !item.equipment;
  // provide a more accurate count of total items
  if (invalid) {
  totalItems--;
  + return false;
  }
  - return invalid || alreadyThere;
  + const notAlreadyThere = item.owner !== store.id ||
  + // Needs to be equipped. Stuff not marked "equip" doesn't
  + // necessarily mean to de-equip it.
  + (pseudoItem.equipped && !item.equipped);
  +
  + return notAlreadyThere;
  });
  // only try to equip subclasses that are equippable, since we allow multiple in a loadout
  