internal override void UnConsumeResources(CraftPath path)
            {
                UnConsumeItems(path);

                base.UnConsumeResources(path);
            }
        // Multithreading to prevent lag?
        private static void FindCraftPaths(List <CraftPath> paths, CraftPath inProgress, CancellationToken token)
        {
            //if(token.CanBeCanceled && watch.ElapsedMilliseconds > 1000)
            //{
            //	Main.NewText("timed out" + watch.ElapsedMilliseconds);
            //	return;
            //}
            if (token.IsCancellationRequested)
            {
                return;
            }
            // Some notion of limiting depth of tree might help.
            //int count = inProgress.root.GetAllChildrenPreOrder().Count(); //.OfType<CraftPath.RecipeNode>()
            //if (count > 20)
            //{
            //	return;
            //}

            // Current will always be an unfulfilled
            CraftPath.UnfulfilledNode current = inProgress.GetCurrent();

            if (current == null)
            {
                paths.Add(inProgress.Clone());                 // Clone probably.
                return;
            }

            // Assume current is UnfulfulledItem class...could change later. Kill boss, other conditions maybe? think more about this.
            var ViableIngredients = current.item;
            var neededStack       = current.stack;

            // reduce VialbleIngredients to only items not seen above.

            current.CheckParentsForRecipeLoopViaIngredients(ViableIngredients);

            if (ViableIngredients.Count == 0)
            {
                //Console.WriteLine();
                return;
            }

            // if(VialbleIngredients.length == 1) optimization?
            //List<Recipe> recipes;
            //if (!recipeDictionary.TryGetValue(needItem.Key, out recipes))
            //	recipes = new List<Recipe>();

            // Find all Recipes that can fulfill current, which might be a RecipeGroup
            var recipeOptions = recipeDictionary.Where(x => ViableIngredients.Contains(x.Key)).SelectMany(x => x.Value).ToList();             // inefficient maybe?

            foreach (var recipe in recipeOptions)
            {
                // Better to do this before.
                //if (current.CheckParentsForRecipeLoop(recipe))
                //	continue;

                //if(inProgress.root.recipe.)

                if (inProgress.root is CraftPath.RecipeNode)
                {
                    var rootRecipe = (inProgress.root as CraftPath.RecipeNode).recipe;
                    if (recipe.requiredItem.Any(x => x.type == rootRecipe.createItem.type && x.stack >= rootRecipe.createItem.stack))
                    {
                        continue;                         // Prevents Wood->Platform->Wood loops. Other code checks similar loops but didn't catch these. TODO: Think about RecipeGroups.
                    }
                }

                // RecipeAvailable here.
                // Allow missing tiles?
                // If createItem/ingredients exists in Tree?

                int craftMultiple = (neededStack - 1) / recipe.createItem.stack + 1;

                // Push recipe consumes Items while populating Children with Unfulfilled or Fulfilled.
                var recipeNode = inProgress.Push(current, recipe, craftMultiple);

                if (RecipePathTester.print)
                {
                    inProgress.Print();
                }
                int pathsBefore = paths.Count;
                FindCraftPaths(paths, inProgress, token);                 // Handles everything from here recursively.
                int pathsAfter = paths.Count;
                // Pop restores Unfulfilled and restores consumed Items.
                inProgress.Pop(current, recipeNode);

                // 3 Iron Ore 6 Lead Ore problem...if paths same size, try with craftMultiple of 1 maybe?
                if (pathsBefore == pathsAfter)                 // and craftMultiple < some number for performance.
                {
                    // And 1 is possible?
                }
            }

            if (allowPurchasable)
            {
                // TODO recipe groups.
                var buyAble = ViableIngredients.Intersect(purchasable.Keys);
                //if (ViableIngredients.Any(x => purchasable.Contains(x)))
                if (buyAble.Count() > 0)
                {
                    // TODO: If Can afford.
                    // TODO: Take into account incomplete already owned items?
                    // UnfulfilledNode should probably be nested under HaveItemNode, or merged together.

                    CraftPath.BuyItemNode buyItemNode = new CraftPath.BuyItemNode(buyAble.First(), neededStack, current.ChildNumber, current.parent, current.craftPath);
                    inProgress.Push(current, buyItemNode);
                    if (RecipePathTester.print)
                    {
                        inProgress.Print();
                    }
                    FindCraftPaths(paths, inProgress, token);
                    inProgress.Pop(current, buyItemNode);
                }
            }

            if (allowLoots)
            {
                //if (VialbleIngredients.Intersect(loots).Any())
                var lootable = ViableIngredients.Intersect(loots.Keys);                 // TODO recipe groups. --> For loop??
                if (lootable.Count() > 0)
                {
                    bool encountered = false;

                    int npc = loots[lootable.First()].First();                     // Only checks 1 item in Group, and first NPC that drops it. Fix later.

                    int bannerID = Item.NPCtoBanner(npc);
                    if (bannerID > 0)
                    {
                        if (NPC.killCount[bannerID] > 0)
                        {
                            encountered = true;
                        }
                    }

                    if (encountered)
                    {
                        CraftPath.LootItemNode lootItemNode = new CraftPath.LootItemNode(lootable.First(), neededStack, current.ChildNumber, current.parent, current.craftPath);
                        inProgress.Push(current, lootItemNode);
                        if (RecipePathTester.print)
                        {
                            inProgress.Print();
                        }
                        FindCraftPaths(paths, inProgress, token);
                        inProgress.Pop(current, lootItemNode);
                    }
                }
            }

            // Other sources?
            // Fishing?
            // Mining/Harvesting?
            // Extractinator
            // Chests
            // Statue
            // Traveling Shop
            // ItemChecklist? If not any of the others, I must have had it once before.

            // returning will result in Popping.
        }
 public HaveItemNode(int itemid, int stack, int ChildNumber, CraftPathNode parent, CraftPath craftPath) : base(ChildNumber, parent, craftPath)
 {
     this.itemid = itemid;
     this.stack  = stack;
 }
Exemple #4
0
            public UnfulfilledNode(RecipeGroup recipeGroup, int stack, int ChildNumber, CraftPathNode parent, CraftPath craftPath) : base(ChildNumber, parent, craftPath)
            {
                this.recipeGroup = recipeGroup;
                this.item        = recipeGroup.ValidItems;
                this.stack       = stack;

                // recipeGroup.ContainsItem probably faster than iterating over item?
            }
        // TODO: GetCraftPaths but without a Recipe? Just an item? Buy/Loot
        // Bestiary Option? All New Items you can craft if you farm this npc?
        internal static List <CraftPath> GetCraftPaths(Recipe recipe, CancellationToken token)
        {
            //Main.NewText("GetCraftPaths");

            //if(harvestable == null)
            //{
            //	// prevent network spam?
            //	harvestable = new HashSet<int>();
            //	for (int i = 0; i < Main.maxTilesX; i++)
            //	{
            //		for (int j = 0; j < Main.maxTilesY; j++)
            //		{

            //		}
            //	}
            //}

            //if (RecipePath.isCraftableOptimization)
            //{
            //	//if (ItemCatalogueUI.instance.craftResults == null)
            //	throw new Exception();
            //}


            // TODO: Track money? or just use inventory items. Bank money easily query-able.
            // Special Currencies?

            var haveItems              = CalculateHaveItems();
            List <CraftPath> paths     = new List <CraftPath>();
            CraftPath        craftPath = new CraftPath(recipe, haveItems);      // Push. Can't pop. // not calling ConsumeResources(recipeNode);

            if (RecipePathTester.print)
            {
                craftPath.Print();
            }
            FindCraftPaths(paths, craftPath, token);

            //watch.Stop();
            //var elapsedMs = watch.ElapsedMilliseconds;
            //if (elapsedMs > 1000)
            //{
            //	StringBuilder sb = new StringBuilder();
            //	foreach (var item in recipe.requiredItem)
            //	{
            //		if (!item.IsAir)
            //			sb.Append(ItemTagHandler.GenerateTag(item));
            //	}
            //	sb.Append("-->");
            //	sb.Append(ItemTagHandler.GenerateTag(recipe.createItem));

            //	Main.NewText(elapsedMs + ": " + sb.ToString());
            //}

            if (!allowMissingStations)
            {
                for (int i = paths.Count - 1; i >= 0; i--)
                {
                    // imkSushisMod caused thousands of paths to be culled here. Cull earlier or limit paths count. Investigate more.
                    if (paths[i].root.GetAllChildrenPreOrder().OfType <CraftPath.RecipeNode>().Any(x => x.recipe.requiredTile.Any(tile => tile > -1 && !RecipeBrowserPlayer.seenTiles[tile])))
                    {
                        paths.RemoveAt(i);
                    }
                }
            }
            return(paths);
        }
Exemple #6
0
 internal void UnConsumeMoney(CraftPath path)
 {
     //path.haveItems.Adjust(itemid, stack);
 }
Exemple #7
0
 public UnfulfilledNode(int item, int stack, int ChildNumber, CraftPathNode parent, CraftPath craftPath) : base(ChildNumber, parent, craftPath)
 {
     this.stack = stack;
     this.item  = new List <int>()
     {
         item
     };
 }
Exemple #8
0
 internal void ConsumeMoney(CraftPath path)
 {
     // TODO. For now assume infinite money.
     //path.haveItems.Adjust(itemid, -stack);
 }
Exemple #9
0
            internal override void ConsumeResources(CraftPath path)
            {
                ConsumeMoney(path);

                base.ConsumeResources(path);
            }
Exemple #10
0
 public HaveItemsNode(RecipeGroup recipeGroup, List <Tuple <int, int> > listOfItems, int ChildNumber, CraftPathNode parent, CraftPath craftPath) : base(ChildNumber, parent, craftPath)
 {
     this.recipeGroup = recipeGroup;
     this.listOfItems = listOfItems;
 }
Exemple #11
0
 internal void ConsumeItems(CraftPath path)
 {
     path.haveItems.Adjust(itemid, -stack);
 }
Exemple #12
0
            public RecipeNode(Recipe recipe, int multiplier, int ChildNumber, CraftPathNode parent, CraftPath craftPath) : base(ChildNumber, parent, craftPath)
            {
                this.recipe     = recipe;
                this.multiplier = multiplier;
                children        = new CraftPathNode[recipe.requiredItem.Count(x => !x.IsAir)];

                List <int> groups = RecipePath.GetAcceptedGroups(recipe);

                for (int i = 0; i < children.Length; i++)                 // For Each Ingredient.
                {
                    bool itemIsRecipeGroupItem = false;
                    foreach (var groupid in groups)
                    {
                        // 6 wood, 4 shadewood works for 10 any wood.

                        // multiplier assumes all same Item in ItemGroup used for all Recipes.
                        if (recipe.requiredItem[i].type == RecipeGroup.recipeGroups[groupid].ValidItems[RecipeGroup.recipeGroups[groupid].IconicItemIndex])
                        {
                            bool foundValidItem   = false;
                            bool foundPartialItem = false;
                            foreach (var validItemID in RecipeGroup.recipeGroups[groupid].ValidItems)
                            {
                                if (craftPath.haveItems.ContainsKey(validItemID) && craftPath.haveItems[validItemID] >= recipe.requiredItem[i].stack * multiplier)
                                {
                                    // Any Wood on left, Wood on Right problem. Wood could be consumed before Wood node, when ShadeWood would be better option.
                                    children[i]    = new HaveItemNode(validItemID, recipe.requiredItem[i].stack * multiplier, i, this, craftPath);
                                    foundValidItem = true;
                                    break;
                                }
                                else if (craftPath.haveItems.ContainsKey(validItemID))
                                {
                                    foundPartialItem = true;
                                }
                            }
                            if (!foundValidItem && foundPartialItem)
                            {
                                List <Tuple <int, int> > listOfItems = new List <Tuple <int, int> >();
                                int remaining = recipe.requiredItem[i].stack * multiplier;
                                foreach (var validItemID in RecipeGroup.recipeGroups[groupid].ValidItems)
                                {
                                    if (remaining > 0 && craftPath.haveItems.ContainsKey(validItemID))
                                    {
                                        int taken = Math.Min(remaining, craftPath.haveItems[validItemID]);
                                        listOfItems.Add(new Tuple <int, int>(validItemID, taken));
                                        remaining -= taken;
                                    }
                                }
                                children[i] = new HaveItemsNode(RecipeGroup.recipeGroups[groupid], listOfItems, i, this, craftPath);
                                if (remaining > 0)
                                {
                                    children[i].children    = new CraftPathNode[1];
                                    children[i].children[0] = new UnfulfilledNode(RecipeGroup.recipeGroups[groupid], remaining, 0, children[i], craftPath);
                                }
                            }
                            else if (!foundValidItem)
                            {
                                children[i] = new UnfulfilledNode(RecipeGroup.recipeGroups[groupid], recipe.requiredItem[i].stack * multiplier, i, this, craftPath);
                            }
                            itemIsRecipeGroupItem = true;
                            break;
                        }
                    }
                    // Does it make more sense to nest these, or add more children slots? Hm, Children match up to recipe ingredient index.... Make a BranchNode?
                    if (!itemIsRecipeGroupItem)
                    {
                        // Recipe Groups can have stacks-size different inputs if needed. Ignore for now and handle: 10 wood needed, 9 wood held and 2 platforms held.

                        if (craftPath.haveItems.ContainsKey(recipe.requiredItem[i].type) && craftPath.haveItems[recipe.requiredItem[i].type] >= recipe.requiredItem[i].stack * multiplier)
                        {
                            // Potential problem: Recipe with multiple of same item. Or Item and ItemGroup that share.
                            // Could implement consumed flag and attempt to consume immediately.
                            children[i] = new HaveItemNode(recipe.requiredItem[i].type, recipe.requiredItem[i].stack * multiplier, i, this, craftPath);
                        }
                        else
                        {
                            if (craftPath.haveItems.ContainsKey(recipe.requiredItem[i].type))
                            {
                                int remainder = recipe.requiredItem[i].stack * multiplier - craftPath.haveItems[recipe.requiredItem[i].type];
                                children[i]             = new HaveItemNode(recipe.requiredItem[i].type, craftPath.haveItems[recipe.requiredItem[i].type], i, this, craftPath);
                                children[i].children    = new CraftPathNode[1];
                                children[i].children[0] = new UnfulfilledNode(recipe.requiredItem[i].type, remainder, 0, children[i], craftPath);
                            }
                            else
                            {
                                children[i] = new UnfulfilledNode(recipe.requiredItem[i].type, recipe.requiredItem[i].stack * multiplier, i, this, craftPath);                                 // assign current?
                            }
                        }
                    }
                    // TODO: Assign CraftPath.Current to 1st or last unfulfilled
                    // TODO: If Loot and Shop and Missing disabled, check
                    // if (RecipePath.isCraftableOptimization && !ItemCatalogueUI.instance.craftResults[item.Key])
                }
            }