Example #1
0
 /// <summary>Construct an instance.</summary>
 /// <param name="blueprint">The building blueprint.</param>
 /// <param name="building">A sample building constructed by the blueprint.</param>
 public RecipeModel(BluePrint blueprint, Building building)
     : this(
         key: blueprint.name,
         type: RecipeType.BuildingBlueprint,
         displayType : I18n.Building_Construction(),
         ingredients : blueprint.itemsRequired
         .Select(ingredient => new RecipeIngredientModel(ingredient.Key, ingredient.Value))
         .ToArray(),
         item : _ => null,
         isKnown : () => true,
         machineParentSheetIndex : null,
         isForMachine : _ => false
         )
 {
     this.SpecialOutput = new RecipeItemEntry(
         new SpriteInfo(building.texture.Value, building.getSourceRectForMenu()),
         building.buildingType.Value
         );
 }
Example #2
0
        /// <inheritdoc />
        public override Vector2?DrawValue(SpriteBatch spriteBatch, SpriteFont font, Vector2 position, float wrapWidth)
        {
            // get margins
            const int groupVerticalMargin   = 6;
            const int groupLeftMargin       = 0;
            const int firstRecipeTopMargin  = 5;
            const int firstRecipeLeftMargin = 14;
            const int otherRecipeTopMargin  = 2;
            float     inputDividerWidth     = font.MeasureString("+").X;
            float     itemSpacer            = inputDividerWidth;

            // current drawing position
            Vector2 curPos            = position;
            float   absoluteWrapWidth = position.X + wrapWidth;

            // icon size and line height
            float lineHeight  = this.LineHeight;
            var   iconSize    = new Vector2(this.IconSize);
            float joinerWidth = inputDividerWidth + (itemSpacer * 2);

            // draw recipes
            curPos.Y += groupVerticalMargin;
            foreach (RecipeByTypeGroup group in this.Recipes)
            {
                // check if we can align columns
                bool alignColumns = wrapWidth >= (group.TotalColumnWidth + itemSpacer + ((group.ColumnWidths.Length - 1) * joinerWidth)); // columns + space between output/input + space between each input

                // draw group label
                curPos.X = position.X + groupLeftMargin;
                curPos  += this.DrawIconText(spriteBatch, font, curPos, absoluteWrapWidth, $"{group.Type}:", Color.Black);

                // draw recipe lines
                foreach (RecipeEntry entry in group.Recipes)
                {
                    // fade recipes which aren't known
                    Color iconColor = entry.IsKnown ? Color.White : Color.White * .5f;
                    Color textColor = entry.IsKnown ? Color.Black : Color.Gray;

                    // reset position for recipe output
                    curPos = new Vector2(
                        position.X + firstRecipeLeftMargin,
                        curPos.Y + firstRecipeTopMargin
                        );

                    // draw output item (icon + name + count + chance)
                    float inputLeft;
                    {
                        var   outputSize  = this.DrawIconText(spriteBatch, font, curPos, absoluteWrapWidth, entry.Output.DisplayText, textColor, entry.Output.Sprite, iconSize, iconColor);
                        float outputWidth = alignColumns
                            ? group.ColumnWidths[0]
                            : outputSize.X;

                        inputLeft = curPos.X + outputWidth + itemSpacer;
                        curPos.X  = inputLeft;
                    }

                    // draw input items
                    for (int i = 0, last = entry.Inputs.Length - 1; i <= last; i++)
                    {
                        RecipeItemEntry input = entry.Inputs[i];

                        // move the draw position down to a new line if the next item would be drawn off the right edge
                        Vector2 inputSize = this.DrawIconText(spriteBatch, font, curPos, absoluteWrapWidth, input.DisplayText, textColor, input.Sprite, iconSize, iconColor, probe: true);
                        if (alignColumns)
                        {
                            inputSize.X = group.ColumnWidths[i + 1];
                        }

                        if (curPos.X + inputSize.X > absoluteWrapWidth)
                        {
                            curPos = new Vector2(
                                x: inputLeft,
                                y: curPos.Y + lineHeight + otherRecipeTopMargin
                                );
                        }

                        // draw input item (icon + name + count)
                        this.DrawIconText(spriteBatch, font, curPos, absoluteWrapWidth, input.DisplayText, textColor, input.Sprite, iconSize, iconColor);
                        curPos = new Vector2(
                            x: curPos.X + inputSize.X,
                            y: curPos.Y
                            );

                        // draw input item joiner
                        if (i != last)
                        {
                            // move draw position to next line if needed
                            if (curPos.X + joinerWidth > absoluteWrapWidth)
                            {
                                curPos = new Vector2(
                                    x: inputLeft,
                                    y: curPos.Y + lineHeight + otherRecipeTopMargin
                                    );
                            }
                            else
                            {
                                curPos.X += itemSpacer;
                            }

                            // draw the input item joiner
                            var joinerSize = this.DrawIconText(spriteBatch, font, curPos, absoluteWrapWidth, "+", textColor);
                            curPos.X += joinerSize.X + itemSpacer;
                        }
                    }

                    curPos.Y += lineHeight;
                }

                curPos.Y += lineHeight; // blank line between groups
            }

            // vertical spacer at the bottom of the recipes
            curPos.Y += groupVerticalMargin;

            // get drawn dimensions
            return(new Vector2(wrapWidth, curPos.Y - position.Y - lineHeight));
        }
Example #3
0
        /*********
        ** Private methods
        *********/
        /// <summary>Build an optimized representation of the recipes to display.</summary>
        /// <param name="ingredient">The ingredient item.</param>
        /// <param name="rawRecipes">The raw recipes to list.</param>
        private IEnumerable <RecipeByTypeGroup> BuildRecipeGroups(Item ingredient, RecipeModel[] rawRecipes)
        {
            /****
            ** build models for matching recipes
            ****/
            Dictionary <string, RecipeEntry[]> rawGroups = rawRecipes
                                                           // split into specific recipes that match the item
                                                           // (e.g. a recipe with several possible inputs => several recipes with one possible input)
                                                           .SelectMany(recipe =>
            {
                Item outputItem        = recipe.CreateItem(ingredient);
                RecipeItemEntry output = this.CreateItemEntry(
                    name: recipe.SpecialOutput?.DisplayText ?? outputItem?.DisplayName,
                    item: outputItem,
                    sprite: recipe.SpecialOutput?.Sprite,
                    minCount: recipe.MinOutput,
                    maxCount: recipe.MaxOutput,
                    chance: recipe.OutputChance,
                    isOutput: true
                    );

                return(this.GetCartesianInputs(recipe)
                       .Select(inputIds =>
                {
                    // get ingredient models
                    IEnumerable <RecipeItemEntry> inputs = inputIds
                                                           .Select((inputId, index) => this.TryCreateItemEntry(inputId, recipe.Ingredients[index]))
                                                           .Where(p => p != null);
                    if (recipe.Type != RecipeType.TailorInput)         // tailoring is always two ingredients with cloth first
                    {
                        inputs = inputs.OrderBy(entry => entry.DisplayText);
                    }

                    // build recipe
                    return new RecipeEntry(
                        name: recipe.Key,
                        type: recipe.DisplayType,
                        isKnown: recipe.IsKnown(),
                        inputs: inputs.ToArray(),
                        output: output
                        );
                }));
            })

                                                           // filter to unique recipe
                                                           // (e.g. two recipe matches => one recipe)
                                                           .GroupBy(recipe => recipe.UniqueKey)
                                                           .Select(item => item.First())

                                                           // sort
                                                           .OrderBy(recipe => recipe.Type)
                                                           .ThenBy(recipe => recipe.Output.DisplayText)

                                                           // group by type
                                                           .GroupBy(p => p.Type)
                                                           .ToDictionary(p => p.Key, p => p.ToArray());

            /****
            ** build recipe groups with column widths
            ****/
            foreach (var rawGroup in rawGroups)
            {
                // build column width list
                var columnWidths = new List <float>();
                void TrackWidth(int index, string text, SpriteInfo icon)
                {
                    while (columnWidths.Count < index + 1)
                    {
                        columnWidths.Add(0);
                    }

                    float width = Game1.smallFont.MeasureString(text).X;

                    if (icon != null)
                    {
                        width += this.IconSize + this.IconMargin;
                    }

                    columnWidths[index] = Math.Max(columnWidths[index], width);
                }

                // get max width of each column in the group
                foreach (var recipe in rawGroup.Value)
                {
                    TrackWidth(0, $"{recipe.Output.DisplayText}:", recipe.Output.Sprite);

                    for (int i = 0; i < recipe.Inputs.Length; i++)
                    {
                        TrackWidth(i + 1, recipe.Inputs[i].DisplayText, recipe.Inputs[i].Sprite);
                    }
                }

                // save widths
                yield return(new RecipeByTypeGroup(
                                 type: rawGroup.Key,
                                 recipes: rawGroup.Value,
                                 columnWidths: columnWidths
                                 ));
            }
        }