<commit_before>define([
	'ash',
	'game/GameGlobals',
	'game/constants/ItemConstants',
	'game/constants/UpgradeConstants',
	'game/constants/PlayerActionConstants',
	'game/constants/WorldConstants',
], function (
	Ash,
	GameGlobals,
	ItemConstants,
	UpgradeConstants,
	PlayerActionConstants,
	WorldConstants,
) {
	var ItemsHelper = Ash.Class.extend({
		
		MAX_SCA_RARITY_DEFAULT_CLOTHING: 1,

		constructor: function () {},
		
		isAvailable: function (item, campOrdinal, step, includeCraftable, includeNonCraftable, maxScavengeRarity) {
			maxScavengeRarity = maxScavengeRarity || 100;
			var adjustCampOrdinal = step == WorldConstants.CAMP_STEP_START || step == WorldConstants.CAMP_STEP_PREVIOUS;
			var adjustedCampOrdinal = adjustCampOrdinal ? campOrdinal - 1 : campOrdinal;
			var adjustedStep = adjustCampOrdinal ? WorldConstants.CAMP_STEP_END : step - 1;
			
			let result = false;
			
			// craftable items: by craftable camp ordinal
			if (item.craftable && includeCraftable) {
				var req = GameGlobals.itemsHelper.getRequiredCampAndStepToCraft(item);
				result = (req.campOrdinal < adjustedCampOrdinal || (req.campOrdinal == adjustedCampOrdinal && req.step <= adjustedStep));
			}

			// non-craftable items: by item defintiion camp ordinal
			// TODO don't check for scavenge rarity, it's not a common way to find items, trade rarity instead? + take into account levels with no trade
			if (!item.craftable && includeNonCraftable) {
				result = true;
				result = result && item.requiredCampOrdinal <= adjustedCampOrdinal;
				result = result && item.scavengeRarity <= maxScavengeRarity;
				result = result && (item.maximumCampOrdinal <= 0 || item.maximumCampOrdinal >= adjustedCampOrdinal);
			}
			
			return result;
		},
		
		getDefaultEquipment: function (campOrdinal, step, itemBonusType, isHardLevel) {
			let result = {}
			
			let clothing = this.getDefaultClothing(campOrdinal, step, itemBonusType, isHardLevel);
			var itemsByType = {};
			for (let j = 0; j < clothing.length; j++) {
				var item = clothing[j];
				itemsByType[item.type] = item;
			}
			let slotTypes = Object.keys(ItemConstants.itemTypesEquipment);
			for (let j = 0; j < slotTypes.length; j++) {
				let slotType = slotTypes[j];
				let existing = itemsByType[slotType];
				if (existing) {
					result[slotType] = existing;
				} else {
					result[slotType] = this.getBestAvailableItem(campOrdinal, step, slotType, itemBonusType);
				}
			}
			
			return Object.values(result);
		},
		
		getDefaultWeapon: function (campOrdinal, step) {
			return this.getBestAvailableItem(campOrdinal, step, ItemConstants.itemTypes.weapon, ItemConstants.itemBonusTypes.fight_att);
		},
		
		getDefaultClothing: function (campOrdinal, step, itemBonusType, isHardLevel) {
			return this.getBestClothing(campOrdinal, step, itemBonusType, this.MAX_SCA_RARITY_DEFAULT_CLOTHING, isHardLevel);
		},
		
		getBestClothing: function (campOrdinal, step, itemBonusType, maxScavengeRarity, isHardLevel) {
			return this.getAvailableClothingList(campOrdinal, step, true, true, false, itemBonusType, maxScavengeRarity, isHardLevel);
		},
		
		getScavengeRewardClothing: function (campOrdinal, step) {
			return this.getAvailableClothingList(campOrdinal, step, true, true, true);
		},
		
		getScavengeNecessityClothing: function (campOrdinal, step) {
			return this.getAvailableClothingList(campOrdinal, step, false, true, false, null, this.MAX_SCA_RARITY_DEFAULT_CLOTHING);
		},
		
		getAvailableClothingList: function (campOrdinal, step, includeCraftable, includeNonCraftable, includeMultiplePerType, preferredItemBonus, maxScavengeRarity, includeSpecialEquipment) {
			step = step || WorldConstants.CAMP_STEP_POI_2;
			let result = [];
			var clothingLists = [
				ItemConstants.itemDefinitions.clothing_over,
				ItemConstants.itemDefinitions.clothing_upper,
				ItemConstants.itemDefinitions.clothing_lower,
				ItemConstants.itemDefinitions.clothing_hands,
				ItemConstants.itemDefinitions.clothing_head
			];
			
			var bestAvailableItem;
			var bestAvailableItemBonus;
			var clothingList;
			var clothingItem;
			var isAvailable;
			
			for (let i = 0; i < clothingLists.length; i++) {
				bestAvailableItem = null;
				bestAvailableItemBonus = 0;
				clothingList = clothingLists[i];
				for (let j = 0; j < clothingList.length; j++) {
					clothingItem = clothingList[j];
					
					if (clothingItem.isSpecialEquipment && !includeSpecialEquipment) continue;
					
					isAvailable = this.isAvailable(clothingItem, campOrdinal, step, includeCraftable, includeNonCraftable, maxScavengeRarity);
					var bonus = preferredItemBonus ? clothingItem.getBonus(preferredItemBonus) : clothingItem.getTotalBonus();
					
					if (isAvailable && bonus > 0 && (!bestAvailableItem || bestAvailableItemBonus < bonus)) {
						bestAvailableItem = clothingItem;
						bestAvailableItemBonus = bonus;
					}
					
					if (isAvailable && includeMultiplePerType) {
						result.push(clothingItem);
					}
				}

				if (!includeMultiplePerType && bestAvailableItem) {
					// var reqs = PlayerActionConstants.requirements["craft_" + clothingItem.id];
					// var reqTech = reqs ? Object.keys(reqs.upgrades) : "none";
					// log.i("-> camp ordinal " + campOrdinal + " best " + clothingList[0].type + ": " + bestAvailableItem.name + " " + bestAvailableItem.id + " | " + bestAvailableItem.craftable)
					result.push(bestAvailableItem);
				}
			}
			
			return result;
		},
		
		getNewEquipment: function (campOrdinal) {
			let result = [];
			var prevNecessityClothing = this.getScavengeNecessityClothing(campOrdinal - 1, 1);
			var necessityClothing = this.getScavengeNecessityClothing(campOrdinal, 1);
			for (let i = 0; i < necessityClothing.length; i++) {
				var notNew = false;
				for (let j = 0; j < prevNecessityClothing.length; j++) {
					if (necessityClothing[i].id === prevNecessityClothing[j].id) {
						notNew = true;
					}
				}
				if (notNew) continue;
				result.push(necessityClothing[i]);
			}
			var prevWeapon = this.getDefaultWeapon(campOrdinal - 1, WorldConstants.CAMP_STEP_END);
			var weapon = this.getDefaultWeapon(campOrdinal, WorldConstants.CAMP_STEP_END);
			if (weapon && (!prevWeapon || weapon.id !== prevWeapon.id)) result.push(weapon);
			return result;
		},
		
		getBestAvailableItem: function (campOrdinal, step, itemType, itemBonusType) {
			let bestItem = null;
			let bestBonus = 0;
			let bestTotal = 0;
			for (let i = 0; i < ItemConstants.itemDefinitions[itemType].length; i++) {
				let item = ItemConstants.itemDefinitions[itemType][i];
				if (!this.isAvailable(item, campOrdinal, WorldConstants.CAMP_STAGE_EARLY, true, true)) {
					continue;
				}
				let bonus = ItemConstants.getItemBonusComparisonValue(item, itemBonusType);
				let total = item.getTotalBonus();
				if (!bestItem || bonus > bestBonus || (bonus == bestBonus && total > bestTotal)) {
					bestItem = item;
					bestBonus = bonus;
					bestTotal = total;
				}
			}
			return bestItem;
		},
		
		getMaxHazardRadiationForLevel: function (campOrdinal, step, isHardLevel) {
			var defaultClothing = this.getDefaultClothing(campOrdinal, step, ItemConstants.itemBonusTypes.res_radiation, isHardLevel);
			var radiationProtection = 0;
			for (let i = 0; i < defaultClothing.length; i++) {<commit_msg>[balancing] fix default player weapon used to calculate enemy difficulty including non-craftable weapons like Sharp Knife<commit_after>define([
	'ash',
	'game/GameGlobals',
	'game/constants/ItemConstants',
	'game/constants/UpgradeConstants',
	'game/constants/PlayerActionConstants',
	'game/constants/WorldConstants',
], function (
	Ash,
	GameGlobals,
	ItemConstants,
	UpgradeConstants,
	PlayerActionConstants,
	WorldConstants,
) {
	var ItemsHelper = Ash.Class.extend({
		
		MAX_SCA_RARITY_DEFAULT_CLOTHING: 1,

		constructor: function () {},
		
		isAvailable: function (item, campOrdinal, step, includeCraftable, includeNonCraftable, maxScavengeRarity) {
			maxScavengeRarity = maxScavengeRarity || 100;
			var adjustCampOrdinal = step == WorldConstants.CAMP_STEP_START || step == WorldConstants.CAMP_STEP_PREVIOUS;
			var adjustedCampOrdinal = adjustCampOrdinal ? campOrdinal - 1 : campOrdinal;
			var adjustedStep = adjustCampOrdinal ? WorldConstants.CAMP_STEP_END : step - 1;
			
			let result = false;
			
			// craftable items: by craftable camp ordinal
			if (item.craftable && includeCraftable) {
				var req = GameGlobals.itemsHelper.getRequiredCampAndStepToCraft(item);
				result = (req.campOrdinal < adjustedCampOrdinal || (req.campOrdinal == adjustedCampOrdinal && req.step <= adjustedStep));
			}

			// non-craftable items: by item definition camp ordinal
			// TODO don't check for scavenge rarity, it's not a common way to find items, trade rarity instead? + take into account levels with no trade
			if (!item.craftable && includeNonCraftable) {
				result = true;
				result = result && item.requiredCampOrdinal <= adjustedCampOrdinal;
				result = result && item.scavengeRarity <= maxScavengeRarity;
				result = result && (item.maximumCampOrdinal <= 0 || item.maximumCampOrdinal >= adjustedCampOrdinal);
			}
			
			return result;
		},
		
		getDefaultEquipment: function (campOrdinal, step, itemBonusType, isHardLevel) {
			let result = {}
			
			let clothing = this.getDefaultClothing(campOrdinal, step, itemBonusType, isHardLevel);
			var itemsByType = {};
			for (let j = 0; j < clothing.length; j++) {
				var item = clothing[j];
				itemsByType[item.type] = item;
			}
			let slotTypes = Object.keys(ItemConstants.itemTypesEquipment);
			for (let j = 0; j < slotTypes.length; j++) {
				let slotType = slotTypes[j];
				let existing = itemsByType[slotType];
				if (existing) {
					result[slotType] = existing;
				} else {
					result[slotType] = this.getBestAvailableItem(campOrdinal, step, slotType, itemBonusType);
				}
			}
			
			return Object.values(result);
		},
		
		getDefaultWeapon: function (campOrdinal, step) {
			return this.getBestAvailableItem(campOrdinal, step, ItemConstants.itemTypes.weapon, ItemConstants.itemBonusTypes.fight_att, false);
		},
		
		getDefaultClothing: function (campOrdinal, step, itemBonusType, isHardLevel) {
			return this.getBestClothing(campOrdinal, step, itemBonusType, this.MAX_SCA_RARITY_DEFAULT_CLOTHING, isHardLevel);
		},
		
		getBestClothing: function (campOrdinal, step, itemBonusType, maxScavengeRarity, isHardLevel) {
			return this.getAvailableClothingList(campOrdinal, step, true, true, false, itemBonusType, maxScavengeRarity, isHardLevel);
		},
		
		getScavengeRewardClothing: function (campOrdinal, step) {
			return this.getAvailableClothingList(campOrdinal, step, true, true, true);
		},
		
		getScavengeNecessityClothing: function (campOrdinal, step) {
			return this.getAvailableClothingList(campOrdinal, step, false, true, false, null, this.MAX_SCA_RARITY_DEFAULT_CLOTHING);
		},
		
		getAvailableClothingList: function (campOrdinal, step, includeCraftable, includeNonCraftable, includeMultiplePerType, preferredItemBonus, maxScavengeRarity, includeSpecialEquipment) {
			step = step || WorldConstants.CAMP_STEP_POI_2;
			let result = [];
			var clothingLists = [
				ItemConstants.itemDefinitions.clothing_over,
				ItemConstants.itemDefinitions.clothing_upper,
				ItemConstants.itemDefinitions.clothing_lower,
				ItemConstants.itemDefinitions.clothing_hands,
				ItemConstants.itemDefinitions.clothing_head
			];
			
			var bestAvailableItem;
			var bestAvailableItemBonus;
			var clothingList;
			var clothingItem;
			var isAvailable;
			
			for (let i = 0; i < clothingLists.length; i++) {
				bestAvailableItem = null;
				bestAvailableItemBonus = 0;
				clothingList = clothingLists[i];
				for (let j = 0; j < clothingList.length; j++) {
					clothingItem = clothingList[j];
					
					if (clothingItem.isSpecialEquipment && !includeSpecialEquipment) continue;
					
					isAvailable = this.isAvailable(clothingItem, campOrdinal, step, includeCraftable, includeNonCraftable, maxScavengeRarity);
					var bonus = preferredItemBonus ? clothingItem.getBonus(preferredItemBonus) : clothingItem.getTotalBonus();
					
					if (isAvailable && bonus > 0 && (!bestAvailableItem || bestAvailableItemBonus < bonus)) {
						bestAvailableItem = clothingItem;
						bestAvailableItemBonus = bonus;
					}
					
					if (isAvailable && includeMultiplePerType) {
						result.push(clothingItem);
					}
				}

				if (!includeMultiplePerType && bestAvailableItem) {
					// var reqs = PlayerActionConstants.requirements["craft_" + clothingItem.id];
					// var reqTech = reqs ? Object.keys(reqs.upgrades) : "none";
					// log.i("-> camp ordinal " + campOrdinal + " best " + clothingList[0].type + ": " + bestAvailableItem.name + " " + bestAvailableItem.id + " | " + bestAvailableItem.craftable)
					result.push(bestAvailableItem);
				}
			}
			
			return result;
		},
		
		getNewEquipment: function (campOrdinal) {
			let result = [];
			var prevNecessityClothing = this.getScavengeNecessityClothing(campOrdinal - 1, 1);
			var necessityClothing = this.getScavengeNecessityClothing(campOrdinal, 1);
			for (let i = 0; i < necessityClothing.length; i++) {
				var notNew = false;
				for (let j = 0; j < prevNecessityClothing.length; j++) {
					if (necessityClothing[i].id === prevNecessityClothing[j].id) {
						notNew = true;
					}
				}
				if (notNew) continue;
				result.push(necessityClothing[i]);
			}
			var prevWeapon = this.getDefaultWeapon(campOrdinal - 1, WorldConstants.CAMP_STEP_END);
			var weapon = this.getDefaultWeapon(campOrdinal, WorldConstants.CAMP_STEP_END);
			if (weapon && (!prevWeapon || weapon.id !== prevWeapon.id)) result.push(weapon);
			return result;
		},
		
		getBestAvailableItem: function (campOrdinal, step, itemType, itemBonusType, includeNonCraftable) {
			let bestItem = null;
			let bestBonus = 0;
			let bestTotal = 0;
			for (let i = 0; i < ItemConstants.itemDefinitions[itemType].length; i++) {
				let item = ItemConstants.itemDefinitions[itemType][i];
				let isAvailable = this.isAvailable(item, campOrdinal, WorldConstants.CAMP_STAGE_EARLY, true, includeNonCraftable);
				if (!isAvailable) continue;
				let bonus = ItemConstants.getItemBonusComparisonValue(item, itemBonusType);
				let total = item.getTotalBonus();
				if (!bestItem || bonus > bestBonus || (bonus == bestBonus && total > bestTotal)) {
					bestItem = item;
					bestBonus = bonus;
					bestTotal = total;
				}
			}
			return bestItem;
		},
		
		getMaxHazardRadiationForLevel: function (campOrdinal, step, isHardLevel) {
			var defaultClothing = this.getDefaultClothing(campOrdinal, step, ItemConstants.itemBonusTypes.res_radiation, isHardLevel);
			var radiationProtection = 0;
			for (let i = 0; i < defaultClothing.length; i++) {
{'fraction_non_alphanumeric': 0.10694132843837106, 'fraction_numerical': 0.0035352505268882997, 'mean_word_length': 4.201555869872702, 'pattern_counts': {'":': 2, '<': 31, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '2246425', 'n_tokens_mistral': 5528, 'n_tokens_neox': 4569, 'n_words': 982}