<commit_before>import { D1ManifestDefinitions } from 'app/destiny1/d1-definitions';
import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { bungieNetPath } from 'app/dim-ui/BungieImage';
import { t } from 'app/i18next-t';
import { DimBucketType } from 'app/inventory/inventory-buckets';
import { DimCharacterStat, DimStore } from 'app/inventory/store-types';
import { SocketOverrides } from 'app/inventory/store/override-sockets';
import { isPluggableItem } from 'app/inventory/store/sockets';
import { isModStatActive } from 'app/loadout-builder/process/mappers';
import { isLoadoutBuilderItem } from 'app/loadout/item-utils';
import { isInsertableArmor2Mod, sortMods } from 'app/loadout/mod-utils';
import { armorStats } from 'app/search/d2-known-values';
import { emptyArray } from 'app/utils/empty';
import { itemCanBeInLoadout } from 'app/utils/item-utils';
import { getFirstSocketByCategoryHash } from 'app/utils/socket-utils';
import { DestinyClass, DestinyStatDefinition } from 'bungie-api-ts/destiny2';
import { BucketHashes, SocketCategoryHashes } from 'data/d2/generated-enums';
import _ from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import { D2Categories } from '../destiny2/d2-bucket-categories';
import { DimItem, PluggableInventoryItemDefinition } from '../inventory/item-types';
import { DimLoadoutItem, Loadout, LoadoutItem } from './loadout-types';

// We don't want to prepopulate the loadout with D1 cosmetics
export const fromEquippedTypes: DimBucketType[] = [
  'Class',
  'KineticSlot',
  'Energy',
  'Power',
  'Primary',
  'Special',
  'Heavy',
  'Helmet',
  'Gauntlets',
  'Chest',
  'Leg',
  'ClassItem',
  'Artifact',
  'Ghost',
  'Ships',
  'Vehicle',
  'Emblems',
];

const excludeGearSlots = ['Class', 'SeasonalArtifacts'];
// order to display a list of all 8 gear slots
const gearSlotOrder: DimItem['type'][] = [
  ...D2Categories.Weapons.filter((t) => !excludeGearSlots.includes(t)),
  ...D2Categories.Armor,
];

/**
 * Creates a new loadout, with all of the items equipped and the items inserted mods saved.
 */
export function newLoadout(name: string, items: LoadoutItem[]): Loadout {
  return {
    id: uuidv4(),
    classType: DestinyClass.Unknown,
    name,
    items,
    clearSpace: false,
  };
}

/**
 * Create a socket overrides structure from the item's currently plugged sockets.
 */
function createSocketOverridesFromEquipped(item: DimItem) {
  const socketOverrides: SocketOverrides = {};
  for (const socket of item.sockets?.allSockets || []) {
    // If the socket is plugged and we plug isn't the initial plug we apply the overrides
    // to the loadout.
    if (
      socket.plugged &&
      socket.plugged.plugDef.hash !== socket.socketDefinition.singleInitialItemHash
    ) {
      socketOverrides[socket.socketIndex] = socket.plugged.plugDef.hash;
    }
  }
  return socketOverrides;
}

/**
 * Create a new loadout that includes all the equipped items and mods on the character.
 */
export function newLoadoutFromEquipped(name: string, dimStore: DimStore) {
  const items = dimStore.items.filter(
    (item) => item.equipped && itemCanBeInLoadout(item) && fromEquippedTypes.includes(item.type)
  );
  const loadout = newLoadout(
    name,
    items.map((i) => {
      const item = convertToLoadoutItem(i, true);
      if (i.bucket.hash === BucketHashes.Subclass) {
        item.socketOverrides = createSocketOverridesFromEquipped(i);
      }
      return item;
    })
  );
  const mods = items.flatMap((i) => extractArmorModHashes(i));
  if (mods.length) {
    loadout.parameters = {
      mods,
    };
  }
  loadout.classType = dimStore.classType;
  return loadout;
}

/*
 * Calculates the light level for a list of items, one per type of weapon and armor
 * or it won't be accurate. function properly supports guardians w/o artifacts
 * returns to tenth decimal place.
 */
export function getLight(store: DimStore, items: DimItem[]): number {
  // https://www.reddit.com/r/DestinyTheGame/comments/6yg4tw/how_overall_power_level_is_calculated/
  if (store.destinyVersion === 2) {
    const exactLight = _.sumBy(items, (i) => i.power) / items.length;
    return Math.floor(exactLight * 1000) / 1000;
  } else {
    const itemWeight = {
      Weapons: 6,
      Armor: 5,
      General: 4,
    };

    const itemWeightDenominator = items.reduce(
      (memo, item) =>
        memo + (itemWeight[item.type === 'ClassItem' ? 'General' : item.bucket.sort!] || 0),
      0
    );

    const exactLight =
      items.reduce(
        (memo, item) =>
          memo +
          item.power * (itemWeight[item.type === 'ClassItem' ? 'General' : item.bucket.sort!] || 1),
        0
      ) / itemWeightDenominator;

    return Math.floor(exactLight * 10) / 10;
  }
}

/** Returns a map of armor stat hashes to stats. There should be just one of each item */
export function getArmorStats(
  defs: D1ManifestDefinitions | D2ManifestDefinitions,
  items: DimItem[]
): { [hash: number]: DimCharacterStat } {
  const statDefs = armorStats.map((hash) => defs.Stat.get(hash) as DestinyStatDefinition);

  // Construct map of stat hash to DimCharacterStat
  const statsByArmorHash: { [hash: number]: DimCharacterStat } = {};
  statDefs.forEach(({ hash, displayProperties: { description, icon, name } }) => {
    statsByArmorHash[hash] = { hash, description, icon: bungieNetPath(icon), name, value: 0 };
  });

  // Sum the items stats into the statsByArmorHash
  items.forEach((item) => {
    const itemStats = _.groupBy(item.stats, (stat) => stat.statHash);
    Object.entries(statsByArmorHash).forEach(([hash, stat]) => {
      stat.value += itemStats[hash]?.[0].value ?? 0;
    });
  });

  return statsByArmorHash;
}

/**
 * This gets the loadout stats for all the equipped items and mods.
 *
 * It will add all stats from the mods whether they are equipped or not. If
 * you want to ensure it will be the same as the game stats, make sure to check
 * if all mods will fit on the items.
 */
export function getLoadoutStats(
  defs: D2ManifestDefinitions,
  classType: DestinyClass,
  subclass: LoadoutItem | undefined,
  armor: DimItem[],
  mods: PluggableInventoryItemDefinition[]
) {
  const statDefs = armorStats.map((hash) => defs.Stat.get(hash));

  // Construct map of stat hash to DimCharacterStat
  const stats: { [hash: number]: DimCharacterStat } = {};
  statDefs.forEach(({ hash, displayProperties: { description, icon, name } }) => {
    stats[hash] = { hash, description, icon: bungieNetPath(icon), name, value: 0 };
  });
<commit_msg>Remove old armor stats function.<commit_after>import { D1ManifestDefinitions } from 'app/destiny1/d1-definitions';
import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { bungieNetPath } from 'app/dim-ui/BungieImage';
import { t } from 'app/i18next-t';
import { DimBucketType } from 'app/inventory/inventory-buckets';
import { DimCharacterStat, DimStore } from 'app/inventory/store-types';
import { SocketOverrides } from 'app/inventory/store/override-sockets';
import { isPluggableItem } from 'app/inventory/store/sockets';
import { isModStatActive } from 'app/loadout-builder/process/mappers';
import { isLoadoutBuilderItem } from 'app/loadout/item-utils';
import { isInsertableArmor2Mod, sortMods } from 'app/loadout/mod-utils';
import { armorStats } from 'app/search/d2-known-values';
import { emptyArray } from 'app/utils/empty';
import { itemCanBeInLoadout } from 'app/utils/item-utils';
import { getFirstSocketByCategoryHash } from 'app/utils/socket-utils';
import { DestinyClass } from 'bungie-api-ts/destiny2';
import { BucketHashes, SocketCategoryHashes } from 'data/d2/generated-enums';
import _ from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import { D2Categories } from '../destiny2/d2-bucket-categories';
import { DimItem, PluggableInventoryItemDefinition } from '../inventory/item-types';
import { DimLoadoutItem, Loadout, LoadoutItem } from './loadout-types';

// We don't want to prepopulate the loadout with D1 cosmetics
export const fromEquippedTypes: DimBucketType[] = [
  'Class',
  'KineticSlot',
  'Energy',
  'Power',
  'Primary',
  'Special',
  'Heavy',
  'Helmet',
  'Gauntlets',
  'Chest',
  'Leg',
  'ClassItem',
  'Artifact',
  'Ghost',
  'Ships',
  'Vehicle',
  'Emblems',
];

const excludeGearSlots = ['Class', 'SeasonalArtifacts'];
// order to display a list of all 8 gear slots
const gearSlotOrder: DimItem['type'][] = [
  ...D2Categories.Weapons.filter((t) => !excludeGearSlots.includes(t)),
  ...D2Categories.Armor,
];

/**
 * Creates a new loadout, with all of the items equipped and the items inserted mods saved.
 */
export function newLoadout(name: string, items: LoadoutItem[]): Loadout {
  return {
    id: uuidv4(),
    classType: DestinyClass.Unknown,
    name,
    items,
    clearSpace: false,
  };
}

/**
 * Create a socket overrides structure from the item's currently plugged sockets.
 */
function createSocketOverridesFromEquipped(item: DimItem) {
  const socketOverrides: SocketOverrides = {};
  for (const socket of item.sockets?.allSockets || []) {
    // If the socket is plugged and we plug isn't the initial plug we apply the overrides
    // to the loadout.
    if (
      socket.plugged &&
      socket.plugged.plugDef.hash !== socket.socketDefinition.singleInitialItemHash
    ) {
      socketOverrides[socket.socketIndex] = socket.plugged.plugDef.hash;
    }
  }
  return socketOverrides;
}

/**
 * Create a new loadout that includes all the equipped items and mods on the character.
 */
export function newLoadoutFromEquipped(name: string, dimStore: DimStore) {
  const items = dimStore.items.filter(
    (item) => item.equipped && itemCanBeInLoadout(item) && fromEquippedTypes.includes(item.type)
  );
  const loadout = newLoadout(
    name,
    items.map((i) => {
      const item = convertToLoadoutItem(i, true);
      if (i.bucket.hash === BucketHashes.Subclass) {
        item.socketOverrides = createSocketOverridesFromEquipped(i);
      }
      return item;
    })
  );
  const mods = items.flatMap((i) => extractArmorModHashes(i));
  if (mods.length) {
    loadout.parameters = {
      mods,
    };
  }
  loadout.classType = dimStore.classType;
  return loadout;
}

/*
 * Calculates the light level for a list of items, one per type of weapon and armor
 * or it won't be accurate. function properly supports guardians w/o artifacts
 * returns to tenth decimal place.
 */
export function getLight(store: DimStore, items: DimItem[]): number {
  // https://www.reddit.com/r/DestinyTheGame/comments/6yg4tw/how_overall_power_level_is_calculated/
  if (store.destinyVersion === 2) {
    const exactLight = _.sumBy(items, (i) => i.power) / items.length;
    return Math.floor(exactLight * 1000) / 1000;
  } else {
    const itemWeight = {
      Weapons: 6,
      Armor: 5,
      General: 4,
    };

    const itemWeightDenominator = items.reduce(
      (memo, item) =>
        memo + (itemWeight[item.type === 'ClassItem' ? 'General' : item.bucket.sort!] || 0),
      0
    );

    const exactLight =
      items.reduce(
        (memo, item) =>
          memo +
          item.power * (itemWeight[item.type === 'ClassItem' ? 'General' : item.bucket.sort!] || 1),
        0
      ) / itemWeightDenominator;

    return Math.floor(exactLight * 10) / 10;
  }
}

/**
 * This gets the loadout stats for all the equipped items and mods.
 *
 * It will add all stats from the mods whether they are equipped or not. If
 * you want to ensure it will be the same as the game stats, make sure to check
 * if all mods will fit on the items.
 */
export function getLoadoutStats(
  defs: D2ManifestDefinitions,
  classType: DestinyClass,
  subclass: LoadoutItem | undefined,
  armor: DimItem[],
  mods: PluggableInventoryItemDefinition[]
) {
  const statDefs = armorStats.map((hash) => defs.Stat.get(hash));

  // Construct map of stat hash to DimCharacterStat
  const stats: { [hash: number]: DimCharacterStat } = {};
  statDefs.forEach(({ hash, displayProperties: { description, icon, name } }) => {
    stats[hash] = { hash, description, icon: bungieNetPath(icon), name, value: 0 };
  });

{'fraction_non_alphanumeric': 0.12871451038821288, 'fraction_numerical': 0.008029136660872445, 'mean_word_length': 3.7380392156862747, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 26, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3939765', 'n_tokens_mistral': 3963, 'n_tokens_neox': 3887, 'n_words': 1027}