Skip to content

Instantly share code, notes, and snippets.

@wiiaboo
Last active February 10, 2024 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wiiaboo/8d41c241b6fa58439007db11a5302b62 to your computer and use it in GitHub Desktop.
Save wiiaboo/8d41c241b6fa58439007db11a5302b62 to your computer and use it in GitHub Desktop.
const MAX_LEVEL = 90;
const craftables = [
{
level: 21,
name: 'lv20 grade 4 ishgard',
xp: 67771,
maximumLevel: 41,
},
{
level: 41,
name: 'lv40 grade 4 ishgard 2nd tier',
xp: 177985,
maximumLevel: 53,
},
{
level: 53,
name: 'lv40 grade 4 ishgard max tier',
xp: 359521,
maximumLevel: 63,
},
{
level: 63,
name: 'lv63 ishgard',
xp: 464875,
maximumLevel: 71,
},
{
level: 71,
name: 'lv71 ishgard',
xp: 755426,
maximumLevel: 80,
},
{
level: 80,
name: 'lv80 levequests',
xp: 1449240,
maximumLevel: 81,
},
{
level: 81,
name: 'lv81 collectables',
xp: 1055241,
},
{
level: 85,
name: 'lv85 collectables',
xp: 1417077,
},
// {
// level: 87,
// name: 'lv87 collectables',
// xp: 1629469,
// },
// {
// level: 89,
// name: 'lv89 collectables',
// xp: 1853298,
// }
];
const getXpTable = async () => {
const XIVAPI = require('@xivapi/js')
const xiv = new XIVAPI()
let res = await xiv.data.list('ParamGrow', {
limit: MAX_LEVEL,
columns: 'ID,ExpToNext'
});
return res.Results.reduce((t, level) => {
t[level.ID+1] = {
fromLevel: level.ID,
toLevel: level.ID+1,
perLevel: level.ExpToNext,
total: level.ID == 1 ? level.ExpToNext : t[level.ID].total + level.ExpToNext
}
return t;
}, {});
}
const craftsNeeded = async (startingLevel='21', finalLevel=MAX_LEVEL, {extraXP='0', xpPerCraft}) => {
const xpTable = await getXpTable();
extraXP = Number(extraXP);
const crafts = [];
let level = Number(startingLevel), currentXP = xpTable[Number(startingLevel)].total + extraXP;
while (level < Number(finalLevel)) {
let craft = { level, currentXP, extraXP };
craft.recipe = craftables.findLast(recipe => recipe.level <= level);
craft.nextRecipeMinimumLevel = craftables[craftables.indexOf(craft.recipe) + 1]?.level ?? MAX_LEVEL;
craft.resultLevel = Math.min(finalLevel, craft.nextRecipeMinimumLevel);
craft.resultLevelXP = xpTable[craft.resultLevel].total;
craft.craft = xpPerCraft ? {name: 'user-provided craft', xp: Number(xpPerCraft)} : craft.recipe;
craft.resultCrafts = Math.ceil((craft.resultLevelXP - craft.currentXP) / craft.craft.xp);
craft.resultXP = craft.currentXP + craft.resultCrafts * craft.craft.xp;
craft.resultExtraXP = craft.resultXP % craft.resultLevelXP;
crafts.push(craft);
level = craft.resultLevel;
currentXP = craft.resultXP;
extraXP = craft.resultExtraXP;
if (craft.nextRecipeMinimumLevel != MAX_LEVEL) continue;
let optimizedRecipe = craftables
.find(recipe => (!recipe.maximumLevel || recipe.maximumLevel >= craft.resultLevel) && craft.resultExtraXP - recipe.xp > 0);
if (optimizedRecipe) {
craft.resultCrafts--;
craft.resultXP -= craft.craft.xp;
craft.resultLevel--;
craft.resultExtraXP = craft.resultXP % xpTable[craft.resultLevel].total
let extraCraft = {
level: craft.resultLevel,
resultLevel: craft.resultLevel + 1,
currentXP: craft.resultXP,
extraXP: craft.resultExtraXP,
recipe: optimizedRecipe,
craft: optimizedRecipe,
resultCrafts: 1,
resultXP: craft.resultXP + optimizedRecipe.xp,
resultExtraXP: (craft.resultXP + optimizedRecipe.xp) % craft.resultLevelXP
}
crafts.push(extraCraft);
level = extraCraft.resultLevel;
currentXP = extraCraft.resultXP;
extraXP = extraCraft.resultExtraXP;
}
// crafts.push(`${craft.level} (+${craft.extraXP} XP) to ${craft.craftUpToLevel} (+${craft.resultExtraXP} XP)\n\t= ${Math.ceil(craft.resultCrafts)} × ${craftables[craft.recipeMinimumLevel] ? craftables[craft.recipeMinimumLevel].name : ''} (${craft.xp} XP craft)`);
// console.log(`xp per craft: ${craft.xp}, ${craft.resultCrafts} crafts, starting lvl/xp: ${level}/${craft.currentXP}, ending lvl/xp: ${craft.craftUpToLevel}/${craft.resultXP} + ${craft.resultExtraXP}`)
}
// crafts.forEach(craft => console.log(craft));
crafts.forEach(craft => console.log(`${craft.level} (+${craft.extraXP} XP) to ${craft.resultLevel}${craft.resultLevel < MAX_LEVEL ? ` (+${craft.resultExtraXP} XP)` : ''}
= ${Math.ceil(craft.resultCrafts)} × ${craft.craft.name} (${craft.craft.xp} XP craft)`));
// console.log(`lv${startingLevel} (+${currentXP} XP) --> lv${finalLevel}
// = ${result} × ${xpPerCraft} XP`.trim());
return craftsNeeded;
};
(() => {
const [ fromLevel, toLevel, extraXP, xpPerCraft ] = process.argv.slice(2);
craftsNeeded(fromLevel, toLevel, {extraXP, xpPerCraft});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment