bdo-shopping-cart-package
Version:
For use with my bdo-crafting-profit projects
453 lines • 20.7 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ItemManager = exports.Recipe = exports.Item = exports.RecipeData = exports.MarketPrice = exports.getShoppingCartDataForItem = exports.getMarketPriceForItem = void 0;
var ShoppingCart_1 = require("./ShoppingCart");
var Action_1 = require("../optimizers/Action");
var PPSOptimizer_1 = require("./../optimizers/PPSOptimizer");
exports.getMarketPriceForItem = function (item) {
return (item.overrideMarketPrice ||
(item.marketData && item.marketData["Market Price"]) ||
0);
};
exports.getShoppingCartDataForItem = function (item, recipePath) {
return item.shoppingCartData[recipePath];
};
var MarketPrice = /** @class */ (function () {
function MarketPrice() {
}
return MarketPrice;
}());
exports.MarketPrice = MarketPrice;
;
var RecipeData = /** @class */ (function () {
function RecipeData() {
}
return RecipeData;
}());
exports.RecipeData = RecipeData;
;
var Item = /** @class */ (function () {
/**
* Create a new Item object
* @param initialItemData e.g {Action: string, Ingredients: [], Market Data: {}, Name: string, Quantity Produced: float, Recipe: [], Time to Produce: float}
*/
function Item(initialItemData) {
var _this = this;
this.resetUses = function (recipePath) {
if (recipePath == null) {
_this.usedInRecipes = {};
}
else {
delete _this.usedInRecipes[recipePath];
delete _this.shoppingCartData[recipePath];
}
// console.log("Reset Use: ", this.name, 'Recipe Path:', recipePath, 'Used in Recipes:', JSON.stringify(this.usedInRecipes, null, 4), JSON.stringify(this.shoppingCartData, null, 4))
if (Object.keys(_this.usedInRecipes).length == 0)
_this.activeRecipeId = "";
};
// console.log("Initial Item Data", initialItemData)
this.name = initialItemData["Name"];
this.marketData = initialItemData["Market Data"];
this.shoppingCartData = {};
this.recipes = {};
this.usedInRecipes = {};
this.activeRecipeId = "";
this.depth = initialItemData["depth"] || -1;
this.overrideMarketPrice = null;
this.isSymbolic = false; // A symbolic recipe is not meant to be bought
this.image = initialItemData["Image"];
this.addRecipe(initialItemData["_id"], initialItemData["Name"], initialItemData["Recipe"], initialItemData["Quantity Produced"], initialItemData["Time to Produce"], initialItemData["Action"], initialItemData["Image"]);
}
/**
*
* @param _id
* @param productName
* @param recipe
* @param quantityProduced
* @param timeToProduce
* @param action
* @param image
*/
Item.prototype.addRecipe = function (_id, productName, recipe, quantityProduced, timeToProduce, action, image) {
if (this.recipes[_id] == null) {
this.recipes[_id] = new Recipe(productName, recipe, quantityProduced, timeToProduce, action, image);
if (action == "Symbolic") {
this.isSymbolic = true;
}
}
// this.printRecipes()
};
/**
*
* @param actionTaken // Craft or Buy
* @param parentName // What recipe is this item used in?
* @param parentRecipeId // What recipe is this item used in?
* @param activeRecipeId // One of the recipes to craft this item
* @param recipePath // One of the recipes to craft this item
*/
Item.prototype.addUse = function (actionTaken, parentName, parentRecipeId, activeRecipeId, recipePath) {
this.usedInRecipes[recipePath] = {
actionTaken: actionTaken,
parentName: parentName,
parentRecipeId: parentRecipeId,
};
if (this.activeRecipeId == "")
this.selectRecipe(activeRecipeId);
else if (activeRecipeId != this.activeRecipeId) {
console.log("POSSIBLE ERROR: Recursive recipe OR recipe needs to be bought/crafted at the same time... or crafted using multiple recipes. \nItem name:", this.name, "\nActive recipe:", this.activeRecipeId, "\nTarget recipe:", activeRecipeId);
}
};
/**
*
* @param recipePath
* @param data
*/
Item.prototype.addShoppingCartData = function (recipePath, data) {
this.shoppingCartData[recipePath] = data;
};
Item.prototype.selectRecipe = function (recipeId) {
if (this.recipes[recipeId] != null) {
this.activeRecipeId = recipeId;
}
else if (recipeId == "" || recipeId == null) {
this.activeRecipeId = "";
}
};
Item.prototype.setDepth = function (depth) {
if (this.depth == -1 || depth < this.depth) {
this.depth = depth;
}
};
Item.prototype.applyBuffs = function (buffs) {
for (var _i = 0, _a = Object.values(this.recipes); _i < _a.length; _i++) {
var recipe = _a[_i];
recipe.applyBuffs(buffs);
}
};
Item.prototype.printRecipes = function () {
console.log("Recipes:", this.recipes);
};
return Item;
}());
exports.Item = Item;
var Recipe = /** @class */ (function () {
/**
*
* @param productName
* @param ingredients
* @param quantityProduced
* @param timeToProduce
* @param action
*/
function Recipe(productName, ingredients, quantityProduced, timeToProduce, action, image) {
this.productName = productName;
this.ingredients = ingredients;
this.quantityProduced = quantityProduced;
this.timeToProduce = timeToProduce;
this.originalTimeToProduce = timeToProduce;
this.action = action;
this.image = image;
}
Recipe.prototype.printIngredients = function () {
console.log("Ingredients:", this.ingredients);
};
Recipe.prototype.applyBuffs = function (buffs) {
if (this.action in buffs) {
this.timeToProduce = Math.max(this.originalTimeToProduce - buffs[this.action].timeReduction, 0);
}
};
return Recipe;
}());
exports.Recipe = Recipe;
var ItemManager = /** @class */ (function () {
function ItemManager() {
var _this = this;
this.officialProductName = "";
/**
* Starting at the recipe path, remove all references pointing to that item.
* In other words, all paths branching off that recipePath provided will have their 'usedInRecipes' dictionary reset
* @param itemName // Name of the item
* @param recipePath // Path to reach the item (ingredient)
*/
this.resetRecipePath = function (itemName, recipePath) {
// Parse path variables
var pathArr = recipePath.split("/");
var parentName = pathArr.length >= 2 ? pathArr[pathArr.length - 2] : null;
if (parentName == "")
parentName = null;
var parentPath = pathArr.slice(0, -1).join("/");
if (parentPath == "")
parentPath = null;
// console.log('Reset', pathArr, parentName, parentPath)
// Reset items that were dependent on the previous recipe
_this.startRecursiveReset(_this.items[itemName], _this.items, parentPath);
};
/**
* Callback function for RecipesTable.onRecipeClick
* Updates the 'this.items' object in this component's state, which updates the RecipesTable(s)
* @param itemName The name of the item
* @param recipeId The id of the recipe selected
*/
this.selectRecipe = function (itemName, recipeId, recipePath) {
var items = _this.items;
// Step 1: Parse recipe Path and get the parent Recipe Id for later
var _a = _this.splitPath(recipePath), parentName = _a.parentName, parentPath = _a.parentPath;
var parentRecipeId = _this.items[parentName] != null
? _this.items[parentName].activeRecipeId
: "";
// Step 2: Select the recipe
// console.log("ITEMS", items);
// console.log("ITEM NAME", itemName);
if (items[itemName] == null)
return;
items[itemName].selectRecipe(recipeId);
// console.log('recipesDashboard.jsx | items after recursive reset', items)
// Step 2: Find the best way to make money using the new decision
_this.shoppingCart.optimizer.setItems(items, _this.officialProductName);
var optimalActions = _this.shoppingCart.optimizer.startCalculatingOptimalActions(itemName, recipeId);
// console.log("optimalActions", optimalActions);
var chosenAction = recipeId == "" ? Action_1.ActionTaken.Buy : Action_1.ActionTaken.Craft;
// Step 3: Using the new optimal actions calculated, update the items object so that the corresponding tables are displayed
_this.cascadeActiveRecipeWithOptimalActions(optimalActions, itemName, chosenAction, items, {
parentRecipeId: parentRecipeId,
parentName: parentName,
parentPath: parentPath,
});
// Step 4: Recalculate all the costs!
_this.recalculate();
};
/**
*
* @param overrides
* {
* craftCount: int
* }
*/
this.recalculate = function (overrides) {
if (overrides === void 0) { overrides = null; }
// Step 1: Calculate the costs associated with the desired action tree. (TODO: Move to separate function?)
// console.log("THIS PROPS PRODUCT = ", this.officialProductName, this.items);
if (overrides != null) {
_this.shoppingCart.calculateCosts(_this.officialProductName, overrides.craftCount, _this.items);
}
else {
_this.shoppingCart.calculateCosts(_this.officialProductName, _this.defaultCraftCount, _this.items);
}
};
this.items = {};
this.alreadyResetPath = {}; // To keep track of paths reset when calling 'startRecursiveReset'
this.shoppingCart = new ShoppingCart_1.ShoppingCart(new PPSOptimizer_1.PPSOptimizer());
this.defaultCraftCount = 100;
}
/**
* Parses the information retrieved from the backend to produce the state object 'items'
* @param {} recipes
*/
ItemManager.prototype.parseRecipes = function (recipes) {
// console.log("Original Recipes Data: ", recipes);
this.items = {};
if (recipes == null)
return this.items;
// Get the official name of the item being produced
this.officialProductName = "";
if (recipes.length > 0)
this.officialProductName = recipes[0]["Name"];
// Parse Recipe and prep for the display in table format
for (var _i = 0, recipes_1 = recipes; _i < recipes_1.length; _i++) {
var recipe = recipes_1[_i];
this.addItem(this.items, recipe);
for (var _a = 0, _b = recipe.Ingredients; _a < _b.length; _a++) {
var ingredient = _b[_a];
this.addItem(this.items, ingredient);
}
}
return this.items;
};
ItemManager.prototype.applyBuffs = function (buffs) {
if (!buffs)
return;
for (var _i = 0, _a = Object.values(this.items); _i < _a.length; _i++) {
var item = _a[_i];
item.applyBuffs(buffs);
}
};
/**
*
* @param items {key: item name, value: Item object}
* @param item
*/
ItemManager.prototype.addItem = function (items, item) {
var _a, _b;
if (item == null || items == null)
return;
if (items[item["Name"]] == null) {
items[item["Name"]] = new Item(item);
}
else {
items[item["Name"]].addRecipe(item["_id"], item["Name"], item["Recipe"], item["Quantity Produced"] || 0, item["Time to Produce"] || 0, item["Action"], (_a = item["Image"]) !== null && _a !== void 0 ? _a : "");
if (item["Name"] != this.officialProductName)
items[item["Name"]].setDepth((_b = item["depth"]) !== null && _b !== void 0 ? _b : -1);
}
// console.log('item name', item.Name)
};
/**
*
* @param item Instance of the Item object
* @param items Dictionary of Item objects. This is used to referenced Items used in the recipe
*/
ItemManager.prototype.startRecursiveReset = function (item, items, parentPath) {
this.alreadyResetPath = {};
// console.log("recipesDashboard.jsx | Starting recursive reset:", parentPath, item);
this.recursivelyResetItemUses(item, items, parentPath);
};
/**
*
* @param item Instance of the Item object
* @param items Dictionary of Item objects. This is used to referenced Items used in the recipe
*/
ItemManager.prototype.recursivelyResetItemUses = function (item, items, parentPath) {
if (parentPath === void 0) { parentPath = null; }
if (item == null || items == null)
return;
var recipeId = item.activeRecipeId;
var currentPath = (parentPath || "") + "/" + item.name;
if (this.alreadyResetPath[currentPath]) {
console.log("Already reset", item.name);
return;
}
if (item.usedInRecipes[currentPath] == null) {
// console.log('No recipe data available for item/path', item.name, currentPath, JSON.stringify(item.usedInRecipes,null, 4));
return;
}
this.alreadyResetPath[currentPath] = true;
if (recipeId != "") {
for (var _i = 0, _a = item.recipes[recipeId].ingredients; _i < _a.length; _i++) {
var ingredient = _a[_i];
var ingredientName = ingredient["Item Name"];
// console.log(
// "Ingredient reset:",
// ingredient["Item Name"], parentPath
// );
// const newPath = `${currentPath || ''}/${ingredientName}`
this.recursivelyResetItemUses(items[ingredientName], items, currentPath);
}
}
// items[ingredientName].resetUses(newPath);
item.resetUses(currentPath);
items[item.name] = item;
};
/**
* Splits it into /parentPath/parentName
* @param path
*/
ItemManager.prototype.splitPath = function (path) {
var pathArr = path.split("/");
var currentItemName = pathArr.length >= 2 ? pathArr[pathArr.length - 1] : "";
var parentName = pathArr.length >= 2 ? pathArr[pathArr.length - 2] : "";
var parentPath = pathArr.slice(0, -1).join("/");
// console.log('Select', pathArr, parentName, parentPath)
return { parentPath: parentPath, parentName: parentName, currentItemName: currentItemName };
};
ItemManager.prototype.resetToOptimal = function () {
if (this.officialProductName == null) {
return this.items;
}
for (var _i = 0, _a = Object.values(this.items); _i < _a.length; _i++) {
var item = _a[_i];
item.resetUses();
item.selectRecipe("");
}
// Get optimal action for each recipe of the root product
this.shoppingCart.optimizer.setItems(this.items, this.officialProductName);
var bestRecipeActions = this.shoppingCart.optimizer.findOptimalActionSets();
var product = this.officialProductName;
// Choose most optimal recipe and the optimal actions for profit
var bestActionSet = null;
for (var actionSetIdx in bestRecipeActions) {
var actionSet = bestRecipeActions[actionSetIdx];
if (bestActionSet == null) {
bestActionSet = actionSet;
continue;
}
var oldCraftAction = bestActionSet.optimalActions[product].Craft;
var newCraftAction = actionSet.optimalActions[product].Craft;
var marketPrice = exports.getMarketPriceForItem(this.items[product]);
if (oldCraftAction != null &&
newCraftAction != null &&
oldCraftAction.calculateProfit(marketPrice) <
newCraftAction.calculateProfit(marketPrice))
bestActionSet = actionSet;
}
if (bestActionSet != null) {
var craftAction = bestActionSet["optimalActions"][product].Craft;
if (craftAction != null) {
this.selectRecipe(product, craftAction.recipe_id, "/" + product);
}
else if (bestActionSet.optimalActions[product].Buy != null) {
this.selectRecipe(product, bestActionSet.optimalActions[product].Buy.recipe_id, "/" + product);
}
}
return this.items;
};
/**
* Selects the RecipeTables that should be active by modifying the 'this.items' object
* @param optimalActions The set of actions determined by the user
* @param currentItem Name of the item
* @param actionTaken 'Buy' or 'Craft'
* @param items this.items
*/
ItemManager.prototype.cascadeActiveRecipeWithOptimalActions = function (optimalActions, currentItemName, actionTaken, items, parent) {
if (items === void 0) { items = __assign({}, this.items); }
var parentRecipeId = parent.parentRecipeId, parentName = parent.parentName, parentPath = parent.parentPath;
// console.log("Optimal Actions", JSON.stringify(optimalActions, null, 4));
// console.log("Active Item:", currentItemName);
// console.log("Action Taken:", actionTaken);
// console.log("Parent Path:", parentPath);
if (items[currentItemName].isSymbolic)
actionTaken = Action_1.ActionTaken.Craft;
var action = optimalActions[currentItemName][actionTaken];
if (action == null)
return items; // Base case. Return when there is no valid action
var currentPath = (parentPath || "") + "/" + currentItemName;
var recipePathArr = currentPath.split("/");
var containsLoop = new Set(recipePathArr).size !== recipePathArr.length;
if (containsLoop) {
actionTaken = Action_1.ActionTaken.Buy;
}
if (items[currentItemName] != null) {
items[currentItemName].addUse(actionTaken, parentName, parentRecipeId, action.recipe_id, currentPath); // e.g. Item.addUse('Craft', parentRecipeId, action's recipe Id which may be null if Buying)
}
else {
console.log("ERROR: '" + currentItemName + "' does not have a recipe entry in the database. Request a FIX by sending a DM on discord to @Kitsune#1040");
}
if (actionTaken === Action_1.ActionTaken.Buy)
return items;
// Recursively update activeRecipes dictionary using more calls to cascadeActiveRecipeWithOptimalActions
for (var ingredientIdx in action.recipe.ingredients) {
var ingredient = action.recipe.ingredients[ingredientIdx];
var name_1 = ingredient["Item Name"];
var ingredientAction = action.actionSequence[ingredientIdx];
var newParent = {
parentRecipeId: action.recipe_id,
parentName: currentItemName,
parentPath: currentPath,
};
this.cascadeActiveRecipeWithOptimalActions(optimalActions, name_1, ingredientAction, items, newParent);
}
return items;
};
return ItemManager;
}());
exports.ItemManager = ItemManager;
//# sourceMappingURL=ShoppingCartCore.js.map