farming-weight
Version:
Tools for calculating farming weight and fortune in Hypixel Skyblock
194 lines • 8.65 kB
JavaScript
import { CROP_INFO } from '../constants/crops.js';
import { EnchantTierProcurement, FARMING_ENCHANTS } from '../constants/enchants.js';
import { Stat } from '../constants/stats.js';
import { UpgradeAction, UpgradeCategory } from '../constants/upgrades.js';
import { buildEffectEnvironmentFromOptions } from '../effects/environment.js';
import { effectsToSummaries } from '../effects/summary.js';
import { enchantEffects } from '../items/sources/enchants.js';
import { getMaxStatFromEnchant, getOptimisticStatFromEnchant } from '../util/enchants.js';
import { getItemScopedConflictKey } from './upgradekeys.js';
const CROP_FORTUNE_STATS = new Set(Object.values(CROP_INFO).map((crop) => crop.fortuneType));
function getDisplayedIncrease(stats, stat) {
if (!stats)
return 0;
if (stat === Stat.FarmingFortune) {
return stats[Stat.FarmingFortune] ?? 0;
}
if (CROP_FORTUNE_STATS.has(stat)) {
return (stats[stat] ?? 0) + (stats[Stat.FarmingFortune] ?? 0);
}
return stats[stat] ?? 0;
}
function getEnchantEffectSummaries(upgradeable, enchantId, level, stat) {
const env = buildEffectEnvironmentFromOptions(upgradeable.options, upgradeable.crop);
const effects = enchantEffects(enchantId, level, env, upgradeable.options ?? {}, true);
return effectsToSummaries(effects, [stat]);
}
export function getUpgradeableEnchants(upgradeable, stat = Stat.FarmingFortune) {
if (!upgradeable.type)
return [];
const result = [];
for (const enchantId in FARMING_ENCHANTS) {
result.push(...getUpgradeableEnchant(upgradeable, enchantId, stat));
}
return result;
}
export function getUpgradeableEnchant(upgradeable, enchantId, stat = Stat.FarmingFortune, options) {
const enchant = FARMING_ENCHANTS[enchantId];
if (!upgradeable.type || !enchant)
return [];
const result = [];
// Skip if the enchantment doesn't apply to the item
if (!enchant || !enchant.appliesTo.includes(upgradeable.type))
return result;
// Skip if the enchantment is crop specific and the crop doesn't match
if (enchant.cropSpecific && enchant.cropSpecific !== upgradeable.crop)
return result;
const applied = upgradeable.item.enchantments?.[enchantId];
// If this enchant can never affect the selected stat, skip it entirely
// unless explicitly requested (used for progress-only enchants).
const maxForStat = getMaxStatFromEnchant(enchant, stat, upgradeable.options, upgradeable.crop);
const maxEffectSummaries = getEnchantEffectSummaries(upgradeable, enchantId, enchant.maxLevel, stat);
const includeWhenNoStatImpact = options?.includeWhenNoStatImpact === true;
if (maxForStat <= 0 && maxEffectSummaries.length === 0 && !includeWhenNoStatImpact)
return result;
const currentForStat = applied
? getOptimisticStatFromEnchant(applied, enchant, stat, upgradeable.options, upgradeable.crop)
: 0;
if (maxForStat > 0 && maxForStat <= currentForStat && !includeWhenNoStatImpact)
return result;
// If the enchantment is not applied, add an entry for applying it
if (!applied) {
const procurement = enchant.levels[enchant.minLevel]?.procurement;
const deltaStats = {};
for (const stat of Object.values(Stat)) {
const val = getOptimisticStatFromEnchant(enchant.minLevel, enchant, stat, upgradeable.options, upgradeable.crop);
if (val !== 0)
deltaStats[stat] = val;
}
// Some enchants (e.g. Dedication) are crop-computed and can evaluate to 0
// without player context. Preserve stat keys so stat-filtered views include them.
if (maxForStat > 0) {
if (stat !== Stat.FarmingFortune && deltaStats[stat] === undefined) {
deltaStats[stat] = 0;
}
if (deltaStats[Stat.FarmingFortune] === undefined) {
deltaStats[Stat.FarmingFortune] = 0;
}
}
const hasStats = Object.keys(deltaStats).length > 0;
const stats = hasStats ? deltaStats : undefined;
const increase = getDisplayedIncrease(stats, stat);
const effects = getEnchantEffectSummaries(upgradeable, enchantId, enchant.minLevel, stat);
result.push({
title: enchant.name + ' 1',
increase,
stats,
effects: effects.length > 0 ? effects : undefined,
wiki: enchant.wiki,
action: !procurement || procurement === EnchantTierProcurement.Normal
? UpgradeAction.Apply
: UpgradeAction.LevelUp,
category: UpgradeCategory.Enchant,
conflictKey: getItemScopedConflictKey(upgradeable, `enchant:${enchantId}:1`),
cost: {
items: {
[enchantNameToId(enchant) + '_1']: 1,
},
},
onto: {
name: upgradeable.item.name,
skyblockId: upgradeable.item.skyblockId,
},
meta: {
type: 'enchant',
key: enchantId,
value: 1,
itemUuid: upgradeable.item.uuid ?? undefined,
},
});
return result;
}
// If the enchantment is at max level already, we don't need to do anything
if (applied >= enchant.maxLevel)
return result;
// Add an entry for upgrading the enchantment
const deltaStats = {};
for (const stat of Object.values(Stat)) {
const before = getOptimisticStatFromEnchant(applied, enchant, stat, upgradeable.options, upgradeable.crop);
const after = getOptimisticStatFromEnchant(applied + 1, enchant, stat, upgradeable.options, upgradeable.crop);
const diff = after - before;
if (diff !== 0)
deltaStats[stat] = diff;
}
if (maxForStat > 0) {
if (stat !== Stat.FarmingFortune && deltaStats[stat] === undefined) {
deltaStats[stat] = 0;
}
if (deltaStats[Stat.FarmingFortune] === undefined) {
deltaStats[Stat.FarmingFortune] = 0;
}
}
const hasStats = Object.keys(deltaStats).length > 0;
const stats = hasStats ? deltaStats : undefined;
const increase = getDisplayedIncrease(stats, stat);
const effects = getEnchantEffectSummaries(upgradeable, enchantId, applied + 1, stat);
const nextEnchant = enchant.levels[applied + 1];
if (!nextEnchant)
return result;
const normalNext = !nextEnchant.procurement ||
nextEnchant.procurement === EnchantTierProcurement.Normal ||
nextEnchant.procurement === EnchantTierProcurement.Loot;
const items = nextEnchant.cost?.items ?? {};
switch (nextEnchant.procurement) {
case undefined:
case EnchantTierProcurement.Normal: {
// Get amount of level 1 enchantment items needed to craft the same applied level
// Ex: 4 level 1 items = 2 level 2 items = 1 level 3 item
// Count = Math.pow(2, applied - 1)
const count = Math.pow(2, applied - 1);
items[enchantNameToId(enchant) + '_1'] = count;
break;
}
case EnchantTierProcurement.Loot:
// The desired level needs to be applied directly
items[enchantNameToId(enchant) + '_' + (applied + 1)] = 1;
break;
case EnchantTierProcurement.SelfLeveling:
return result; // Self-leveling enchantments do not have a cost
default:
break;
}
const cost = {
...(nextEnchant.cost ?? {}),
items: Object.keys(items).length > 0 ? items : undefined,
};
result.push({
title: enchant.name + ' ' + (applied + 1),
increase,
stats,
effects: effects.length > 0 ? effects : undefined,
action: normalNext ? UpgradeAction.Apply : UpgradeAction.LevelUp,
category: UpgradeCategory.Enchant,
conflictKey: getItemScopedConflictKey(upgradeable, `enchant:${enchantId}:${applied + 1}`),
cost: cost,
wiki: enchant.wiki,
onto: {
name: upgradeable.item.name,
skyblockId: upgradeable.item.skyblockId,
},
meta: {
type: 'enchant',
key: enchantId,
value: applied + 1,
itemUuid: upgradeable.item.uuid ?? undefined,
},
});
return result;
}
function enchantNameToId(enchant) {
if (enchant.id)
return enchant.id;
return 'ENCHANTMENT_' + enchant.name.toLocaleUpperCase().replaceAll(' ', '_').replaceAll('-', '_');
}
//# sourceMappingURL=enchantupgrades.js.map