diff --git a/Images/uniqueTile.png b/Images/uniqueTile.png new file mode 100644 index 0000000..acef8de Binary files /dev/null and b/Images/uniqueTile.png differ diff --git a/LootCache.cs b/LootCache.cs index 712a816..067463a 100644 --- a/LootCache.cs +++ b/LootCache.cs @@ -311,7 +311,7 @@ internal static void Setup(Mod recipeBrowserMod) for (int i = 1; i < NPCLoader.NPCCount; i++) // for every npc... { npc.SetDefaults(i); - npc.value = 0; + npc.value = 0; // Causes some drops to be missed, why is this here? string currentMod = npc.modNPC?.mod.Name ?? "Terraria"; if (!modsThatNeedRecalculate.Contains(currentMod)) continue; diff --git a/RecipeBrowser.cs b/RecipeBrowser.cs index a90908b..2462e58 100644 --- a/RecipeBrowser.cs +++ b/RecipeBrowser.cs @@ -7,6 +7,13 @@ namespace RecipeBrowser { + // Magic storage: item checklist support? + // Any iron bar not working for starred + // TODO: Autostar items needed for starred recipes. + // TODO: Save starred recipes. Also, crafting check off starred last time, look into it. + // TODO: Hide Items, items not interested in crafting. Only show if query item is that item (so you can still know how to craft if needed in craft chain.) + // TODO: Star Loot + // TODO: Invesitgate Update placement. (Tiles purple flash) internal class RecipeBrowser : Mod { internal static RecipeBrowser instance; diff --git a/RecipeBrowserUI.cs b/RecipeBrowserUI.cs index e826964..8f6b6d3 100644 --- a/RecipeBrowserUI.cs +++ b/RecipeBrowserUI.cs @@ -200,6 +200,7 @@ public override void OnInitialize() modFilterButton.Top.Set(-0, 0f); modFilterButton.OnClick += ModFilterButton_OnClick; modFilterButton.OnRightClick += ModFilterButton_OnRightClick; + modFilterButton.OnMiddleClick += ModFilterButton_OnMiddleClick; button.Append(modFilterButton); Texture2D texture = RecipeBrowser.instance.GetTexture("UIElements/closeButton"); @@ -254,6 +255,15 @@ private void ModFilterButton_OnRightClick(UIMouseEvent evt, UIElement listeningE AllUpdateNeeded(); } + private void ModFilterButton_OnMiddleClick(UIMouseEvent evt, UIElement listeningElement) + { + UIHoverImageButtonMod button = (evt.Target as UIHoverImageButtonMod); + modIndex = mods.Length - 1; + button.hoverText = "Mod Filter: All"; + UpdateModHoverImage(button); + AllUpdateNeeded(); + } + private void UpdateModHoverImage(UIHoverImageButtonMod button) { button.texture = null; diff --git a/RecipeCatalogueUI.cs b/RecipeCatalogueUI.cs index 6804d9b..ef493e9 100644 --- a/RecipeCatalogueUI.cs +++ b/RecipeCatalogueUI.cs @@ -19,6 +19,25 @@ internal class RecipeCatalogueUI internal UICheckbox TileLookupRadioButton; internal Item queryLootItem; + + private int tile = -1; + internal int Tile + { + get { return tile; } + set + { + if (tile != value) + updateNeeded = true; + tile = value; + foreach (var tileSlot in tileSlots) + { + tileSlot.selected = false; + if (tileSlot.tile == value) + tileSlot.selected = true; + } + } + } + // internal UICheckbox inventoryFilter; internal NewUITextBox itemNameFilter; @@ -26,9 +45,13 @@ internal class RecipeCatalogueUI // internal UIHoverImageButton clearNameFilterButton; internal NewUITextBox itemDescriptionFilter; + internal UIPanel mainPanel; internal UIPanel recipeGridPanel; internal UIGrid recipeGrid; internal UIGrid lootSourceGrid; + internal UIPanel tileChooserPanel; + internal UICycleImage uniqueCheckbox; + internal UIGrid tileChooserGrid; internal UIRecipeInfo recipeInfo; internal UIRadioButton NearbyIngredientsRadioBitton; @@ -38,6 +61,7 @@ internal class RecipeCatalogueUI internal int selectedIndex = -1; internal int newestItem = 0; internal List recipeSlots; + internal List tileSlots; internal bool updateNeeded; @@ -48,7 +72,7 @@ public RecipeCatalogueUI() internal UIElement CreateRecipeCataloguePanel() { - UIPanel mainPanel = new UIPanel(); + mainPanel = new UIPanel(); mainPanel.SetPadding(6); // mainPanel.Left.Set(400f, 0f); // mainPanel.Top.Set(400f, 0f); @@ -68,15 +92,14 @@ internal UIElement CreateRecipeCataloguePanel() queryItem = new UIRecipeCatalogueQueryItemSlot(new Item()); queryItem.Top.Set(2, 0f); queryItem.Left.Set(2, 0f); - queryItem.OnItemChanged += () => { TileLookupRadioButton.SetDisabled(queryItem.item.createTile <= -1); }; + //queryItem.OnItemChanged += () => { Main.NewText("Item changed?"); TileLookupRadioButton.SetDisabled(queryItem.item.createTile <= -1); }; mainPanel.Append(queryItem); TileLookupRadioButton = new UICheckbox("Tile", ""); TileLookupRadioButton.Top.Set(42, 0f); TileLookupRadioButton.Left.Set(0, 0f); TileLookupRadioButton.SetText(" Tile"); - TileLookupRadioButton.OnSelectedChanged += (s, e) => { updateNeeded = true; }; - TileLookupRadioButton.SetDisabled(true); + TileLookupRadioButton.OnSelectedChanged += (s, e) => { ToggleTileChooser(!mainPanel.HasChild(tileChooserPanel)); updateNeeded = true; }; mainPanel.Append(TileLookupRadioButton); RadioButtonGroup = new UIRadioButtonGroup(); @@ -172,13 +195,79 @@ internal UIElement CreateRecipeCataloguePanel() lootSourcePanel.Append(lootSourceScrollbar); lootSourceGrid.SetScrollbar(lootSourceScrollbar); + // Tile Chooser + tileChooserPanel = new UIPanel(); + tileChooserPanel.SetPadding(6); + tileChooserPanel.Top.Pixels = 60; + tileChooserPanel.Width.Set(50, 0f); + tileChooserPanel.Height.Set(-60 - 121, 1f); + tileChooserPanel.BackgroundColor = Color.CornflowerBlue; + + uniqueCheckbox = new UICycleImage(RecipeBrowser.instance.GetTexture("Images/uniqueTile") /* Thanks MiningdiamondsVIII */, 2, new string[] { "Show inherited recipes", "Show unique recipes" }, 36, 20); + uniqueCheckbox.Top.Set(0, 0f); + uniqueCheckbox.Left.Set(1, 0f); + uniqueCheckbox.CurrentState = 1; + uniqueCheckbox.OnStateChanged += (s, e) => { updateNeeded = true; }; + tileChooserPanel.Append(uniqueCheckbox); + + tileChooserGrid = new UIGrid(); + tileChooserGrid.Width.Set(0, 1f); + tileChooserGrid.Height.Set(-24, 1f); + tileChooserGrid.Top.Set(24, 0f); + tileChooserGrid.ListPadding = 2f; + tileChooserPanel.Append(tileChooserGrid); + + var tileChooserScrollbar = new InvisibleFixedUIScrollbar(RecipeBrowserUI.instance.userInterface); + tileChooserScrollbar.SetView(100f, 1000f); + tileChooserScrollbar.Height.Set(0, 1f); + tileChooserScrollbar.Left.Set(-20, 1f); + tileChooserPanel.Append(tileChooserScrollbar); + tileChooserGrid.SetScrollbar(tileChooserScrollbar); + recipeSlots = new List(); + tileSlots = new List(); updateNeeded = true; return mainPanel; } + internal void ToggleTileChooser(bool show = true) + { + if (show) + { + recipeGridPanel.Width.Set(-113, 1f); + recipeGridPanel.Left.Set(53, 0f); + mainPanel.Append(tileChooserPanel); + } + else + { + recipeGridPanel.Width.Set(-60, 1f); + recipeGridPanel.Left.Set(0, 0f); + mainPanel.RemoveChild(tileChooserPanel); + Tile = -1; + } + recipeGridPanel.Recalculate(); + } + + internal void ShowCraftInterface() + { + // make smaller? bigger? + //throw new NotImplementedException(); + Main.NewText("ShowCraftInterface"); + if (Main.rand.NextBool(2)) + { + recipeGridPanel.Width.Set(-120, 1f); + recipeGridPanel.Left.Set(60, 0f); + } + else + { + recipeGridPanel.Width.Set(-60, 1f); + recipeGridPanel.Left.Set(0, 0f); + } + recipeGridPanel.Recalculate(); + } + internal void CloseButtonClicked() { // we should have a way for the button itself to be unclicked and notify parent. @@ -231,13 +320,35 @@ private void UpdateGrid() { recipeSlots.Add(new UIRecipeSlot(i)); } + + tileChooserGrid.Clear(); + var tileUsageCounts = new Dictionary(); + int currentCount; + for (int i = 0; i < Recipe.numRecipes; i++) + { + for (int j = 0; j < 15; j++) + { + if (Main.recipe[i].requiredTile[j] == -1) + break; + tileUsageCounts.TryGetValue(Main.recipe[i].requiredTile[j], out currentCount); + tileUsageCounts[Main.recipe[i].requiredTile[j]] = currentCount + 1; + } + } + // sort + var sorted = tileUsageCounts.OrderBy(kvp => kvp.Value); + foreach (var tileUsage in sorted) + { + var tileSlot = new UITileSlot(tileUsage.Key, tileUsage.Value); + tileChooserGrid.Add(tileSlot); + tileSlots.Add(tileSlot); + } } if (!updateNeeded) { return; } updateNeeded = false; List groups = new List(); - if (queryItem.item.stack > 0 && !TileLookupRadioButton.Selected) + if (queryItem.item.stack > 0) { int type = queryItem.item.type; @@ -358,31 +469,58 @@ private bool PassRecipeFilters(Recipe recipe, List groups) Main.NewText("How is this happening??"); } } - //if (inventoryFilter.Selected) - //{ - //} - if (!queryItem.item.IsAir) + + // Filter out recipes that don't use selected Tile + if (Tile > -1) { - if (TileLookupRadioButton.Selected) + List adjTiles = new List(); + adjTiles.Add(Tile); + if (uniqueCheckbox.CurrentState == 0) { - int type = queryItem.item.createTile; - if (!recipe.requiredTile.Any(ing => ing == type)) + Terraria.ModLoader.ModTile modTile = Terraria.ModLoader.TileLoader.GetTile(Tile); + if (modTile != null) { - return false; + adjTiles.AddRange(modTile.adjTiles); + } + if (Tile == 302) + adjTiles.Add(17); + if (Tile == 77) + adjTiles.Add(17); + if (Tile == 133) + { + adjTiles.Add(17); + adjTiles.Add(77); } + if (Tile == 134) + adjTiles.Add(16); + if (Tile == 354) + adjTiles.Add(14); + if (Tile == 469) + adjTiles.Add(14); + if (Tile == 355) + { + adjTiles.Add(13); + adjTiles.Add(14); + } + // TODO: GlobalTile.AdjTiles support (no player object, reflection needed since private) } - else + if (!recipe.requiredTile.Any(t => adjTiles.Contains(t))) { - int type = queryItem.item.type; - bool inGroup = recipe.acceptedGroups.Intersect(groups).Any(); + return false; + } + } - inGroup |= recipe.useWood(type, type) || recipe.useSand(type, type) || recipe.useFragment(type, type) || recipe.useIronBar(type, type) || recipe.usePressurePlate(type, type); - if (!inGroup) + if (!queryItem.item.IsAir) + { + int type = queryItem.item.type; + bool inGroup = recipe.acceptedGroups.Intersect(groups).Any(); + + inGroup |= recipe.useWood(type, type) || recipe.useSand(type, type) || recipe.useFragment(type, type) || recipe.useIronBar(type, type) || recipe.usePressurePlate(type, type); + if (!inGroup) + { + if (!(recipe.createItem.type == type || recipe.requiredItem.Any(ing => ing.type == type))) { - if (!(recipe.createItem.type == type || recipe.requiredItem.Any(ing => ing.type == type))) - { - return false; ; - } + return false; ; } } } diff --git a/UIElements/UICycleImage.cs b/UIElements/UICycleImage.cs new file mode 100644 index 0000000..e10c290 --- /dev/null +++ b/UIElements/UICycleImage.cs @@ -0,0 +1,74 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using Terraria; +using Terraria.UI; + +namespace RecipeBrowser.UIElements +{ + internal class UICycleImage : UIElement + { + private Texture2D texture; + private int _drawWidth; + private int _drawHeight; + private int padding; + private int textureOffsetX; + private int textureOffsetY; + private int states; + internal string[] hoverTexts; + + public event EventHandler OnStateChanged; + + private int currentState = 0; + public int CurrentState + { + get { return currentState; } + set + { + if (value != currentState) + { + currentState = value; + OnStateChanged?.Invoke(this, EventArgs.Empty); + } + } + } + + public UICycleImage(Texture2D texture, int states, string[] hoverTexts, int width, int height, int textureOffsetX = 0, int textureOffsetY = 0, int padding = 2) + { + this.texture = texture; + this._drawWidth = width; + this._drawHeight = height; + this.textureOffsetX = textureOffsetX; + this.textureOffsetY = textureOffsetY; + this.Width.Set((float)width, 0f); + this.Height.Set((float)height, 0f); + this.states = states; + this.padding = padding; + this.hoverTexts = hoverTexts; + } + + protected override void DrawSelf(SpriteBatch spriteBatch) + { + CalculatedStyle dimensions = base.GetDimensions(); + Point point = new Point(textureOffsetX, textureOffsetY + ((padding + _drawHeight) * currentState)); + Color color = base.IsMouseHovering ? Color.White : Color.Silver; + spriteBatch.Draw(texture, new Rectangle((int)dimensions.X, (int)dimensions.Y, this._drawWidth, this._drawHeight), new Rectangle?(new Rectangle(point.X, point.Y, this._drawWidth, this._drawHeight)), color); + if (IsMouseHovering) + { + Main.hoverItemName = hoverTexts[CurrentState]; + } + } + + public override void Click(UIMouseEvent evt) + { + CurrentState = (currentState + 1) % states; + base.Click(evt); + } + + public override void RightClick(UIMouseEvent evt) + { + CurrentState = (currentState + states - 1) % states; + base.RightClick(evt); + } + } +} diff --git a/UIElements/UIRecipeCatalogueQueryItemSlot.cs b/UIElements/UIRecipeCatalogueQueryItemSlot.cs index 1485512..bb9ba50 100644 --- a/UIElements/UIRecipeCatalogueQueryItemSlot.cs +++ b/UIElements/UIRecipeCatalogueQueryItemSlot.cs @@ -21,6 +21,8 @@ internal override void ReplaceWithFake(int type) base.ReplaceWithFake(type); RecipeCatalogueUI.instance.queryLootItem = item; RecipeCatalogueUI.instance.updateNeeded = true; + RecipeCatalogueUI.instance.Tile = -1; + RecipeCatalogueUI.instance.TileLookupRadioButton.Selected = false; } } } \ No newline at end of file diff --git a/UIElements/UITileSlot.cs b/UIElements/UITileSlot.cs new file mode 100644 index 0000000..91bc127 --- /dev/null +++ b/UIElements/UITileSlot.cs @@ -0,0 +1,144 @@ +using Microsoft.Xna.Framework.Graphics; +using System.Linq; +using Terraria; +using Terraria.UI; +using Microsoft.Xna.Framework; +using System; +using Terraria.ObjectData; +using Terraria.Map; +using Terraria.ID; + +namespace RecipeBrowser.UIElements +{ + class UITileSlot : UIElement + { + public Texture2D backgroundTexture => Main.inventoryBack9Texture; + public Texture2D selectedTexture => UIRecipeSlot.selectedBackgroundTexture; + internal float scale = .75f; + public int order; // usage count + public int tile; + public bool selected; + Texture2D texture; + + public UITileSlot(int tile, int order, float scale = 0.75f) + { + this.scale = scale; + this.order = order; + this.tile = tile; + this.Width.Set(backgroundTexture.Width * scale, 0f); + this.Height.Set(backgroundTexture.Height * scale, 0f); + } + + public override void Click(UIMouseEvent evt) + { + //RecipeCatalogueUI.instance.ToggleTileChooser(false); + //RecipeCatalogueUI.instance.queryItem.ReplaceWithFake(item.type); + //RecipeCatalogueUI.instance.TileLookupRadioButton.SetDisabled(false); + //RecipeCatalogueUI.instance.TileLookupRadioButton.Selected = true; + if (selected) + RecipeCatalogueUI.instance.Tile = -1; + else + RecipeCatalogueUI.instance.Tile = tile; + } + + public override void DoubleClick(UIMouseEvent evt) + { + //RecipeCatalogueUI.instance.queryItem.ReplaceWithFake(0); + //RecipeCatalogueUI.instance.Tile = tile; + //RecipeCatalogueUI.instance.ToggleTileChooser(false); + } + + public override int CompareTo(object obj) + { + UITileSlot other = obj as UITileSlot; + return -order.CompareTo(other.order); + } + + // TODO, move this to a real update to prevent purple flash + public override void Update(GameTime gameTime) + { + //texture = null; + if (texture == null) + { + Main.instance.LoadTiles(tile); + + var tileObjectData = TileObjectData.GetTileData(tile, 0, 0); + if (tileObjectData == null) + { + texture = Main.magicPixel; + return; + } + + int width = tileObjectData.Width; + int height = tileObjectData.Height; + int padding = tileObjectData.CoordinatePadding; + + Main.spriteBatch.End(); + RenderTarget2D renderTarget = new RenderTarget2D(Main.graphics.GraphicsDevice, width * 16, height * 16); + Main.instance.GraphicsDevice.SetRenderTarget(renderTarget); + Main.instance.GraphicsDevice.Clear(Color.Transparent); + Main.spriteBatch.Begin(); + + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + Main.spriteBatch.Draw(Main.tileTexture[tile], new Vector2(i * 16, j * 16), new Rectangle(i * 16 + i * padding, j * 16 + j * padding, 16, 16), Color.White, 0f, Vector2.Zero, 1, SpriteEffects.None, 0f); + } + } + + Main.spriteBatch.End(); + Main.instance.GraphicsDevice.SetRenderTarget(null); + Main.spriteBatch.Begin(); + texture = renderTarget; + } + } + + protected override void DrawSelf(SpriteBatch spriteBatch) + { + if (texture == null) + return; + + CalculatedStyle dimensions = base.GetInnerDimensions(); + Rectangle rectangle = dimensions.ToRectangle(); + spriteBatch.Draw(backgroundTexture, dimensions.Position(), null, Color.White, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f); + + if (selected) + spriteBatch.Draw(selectedTexture, dimensions.Position(), null, Color.White, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f); + + int height = texture.Height; + int width = texture.Width; + float drawScale = 1f; // larger, uncomment below + float availableWidth = (float)backgroundTexture.Width * scale; + if (width /** drawScale*/ > availableWidth || height /** drawScale*/ > availableWidth) + { + if (width > height) + { + drawScale = availableWidth / width; + } + else + { + drawScale = availableWidth / height; + } + } + drawScale *= scale; + Vector2 vector = backgroundTexture.Size() * scale; + Vector2 position2 = dimensions.Position() + vector / 2f - texture.Size() * drawScale / 2f; + //Vector2 origin = texture.Size() * (1f / 2f - 0.5f); + spriteBatch.Draw(texture, position2, null, Color.White, 0f, Vector2.Zero, drawScale, SpriteEffects.None, 0f); + + if (IsMouseHovering) + { + string tileName = Lang.GetMapObjectName(MapHelper.TileToLookup(tile, 0)); + if (tileName == "") + { + if (tile < TileID.Count) + tileName = $"Tile {tile}"; + else + tileName = Terraria.ModLoader.TileLoader.GetTile(tile).Name + " (err no entry)"; + } + Main.hoverItemName = tileName; + } + } + } +} diff --git a/build.txt b/build.txt index b3d644d..2d6af69 100644 --- a/build.txt +++ b/build.txt @@ -1,5 +1,5 @@ author = jopojelly -version = 0.5 +version = 0.5.1 displayName = Recipe Browser homepage = https://forums.terraria.org/index.php?threads/recipe-browser.62462/ buildIgnore = .vs\*, Properties\*, *.csproj, *.user, obj\*, bin\*, *.config, unused\*, .git\*, diff --git a/icon.png b/icon.png index 5b75553..cda24fb 100644 Binary files a/icon.png and b/icon.png differ