Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The "Easy Giants' Foundry" plugin is designed to optimize your performance in th
* **Cyan:** Click tool again for bonus progress
- **Alloy Quality and Crucible Content**
- Overlays **current crucible contents** and the **quality of the alloy** being forged.
- **Bank-Aware Foundry Planner**
- Suggests the best Giants' Foundry alloy from your banked bars and supported metal equipment.
- Shows banked sword count and metal score from the Info Panel.
- **Best Mould Guidance**
- Highlights the **best moulds to use** for your current task, guiding your selection process.
- **Progress and Actions Tracking**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,18 @@ default boolean drawAllMetals()
return false;
}

@ConfigItem(
position = 102,
keyName = "drawPlanner",
name = "Foundry planner",
description = "Show the best Giants' Foundry combo from your banked bars and equipment.",
section = infoPanelList
)
default boolean drawPlanner()
{
return false;
}

@ConfigItem(
position = 110,
keyName = "countOre",
Expand Down
57 changes: 57 additions & 0 deletions src/main/java/com/toofifty/easygiantsfoundry/FoundryOverlay2D.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
import javax.inject.Singleton;

import net.runelite.api.Client;
import net.runelite.api.Skill;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.overlay.OverlayPanel;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;

import java.util.Optional;

@Singleton
public class FoundryOverlay2D extends OverlayPanel
{
Expand All @@ -25,6 +28,7 @@ public class FoundryOverlay2D extends OverlayPanel
private final EasyGiantsFoundryPlugin plugin;
private final EasyGiantsFoundryState state;
private final MetalBarCounter metalBarCounter;
private final FoundryRecommendationPlanner recommendationPlanner;
private final EasyGiantsFoundryConfig config;

@Inject
Expand All @@ -33,12 +37,14 @@ private FoundryOverlay2D(
EasyGiantsFoundryPlugin plugin,
EasyGiantsFoundryState state,
MetalBarCounter metalBarCounter,
FoundryRecommendationPlanner recommendationPlanner,
EasyGiantsFoundryConfig config)
{
this.client = client;
this.plugin = plugin;
this.state = state;
this.metalBarCounter = metalBarCounter;
this.recommendationPlanner = recommendationPlanner;
this.config = config;
this.setPosition(OverlayPosition.BOTTOM_LEFT);
}
Expand Down Expand Up @@ -87,6 +93,11 @@ public Dimension render(Graphics2D graphics)
drawMetals(graphics);
}

if (config.drawPlanner())
{
drawPlanner();
}

if (swordPickedUp)
{
Heat heat = state.getCurrentHeat();
Expand Down Expand Up @@ -171,4 +182,50 @@ private void drawMetalCount(Graphics2D graphics2D, String displayName, int count
);
}
}

private void drawPlanner()
{
if (!metalBarCounter.isSeenBank())
{
panelComponent.getChildren().add(
LineComponent.builder()
.left("Planner: open bank")
.leftColor(Color.RED)
.build()
);
return;
}

Optional<FoundryRecommendation> recommendation = recommendationPlanner.recommend(client.getRealSkillLevel(Skill.SMITHING));
if (recommendation.isEmpty())
{
panelComponent.getChildren().add(
LineComponent.builder()
.left("Planner")
.right("no 28-metal combo")
.build()
);
return;
}

FoundryRecommendation best = recommendation.get();
panelComponent.getChildren().add(
LineComponent.builder()
.left("Best foundry")
.right(best.getRecipeText())
.build()
);
panelComponent.getChildren().add(
LineComponent.builder()
.left("Banked")
.right(best.getSwordsText())
.build()
);
panelComponent.getChildren().add(
LineComponent.builder()
.left("Score")
.right(Integer.toString(best.getMetalScore()))
.build()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.toofifty.easygiantsfoundry;

import com.toofifty.easygiantsfoundry.enums.MetalBarType;
import lombok.Value;

@Value
public class FoundryRecommendation
{
MetalBarType primary;
int primaryCount;
MetalBarType secondary;
int secondaryCount;
int swords;
int metalScore;

public String getRecipeText()
{
if (secondary == null)
{
return primaryCount + " " + displayName(primary);
}

return primaryCount + " " + displayName(primary) + " + " + secondaryCount + " " + displayName(secondary);
}

public String getSwordsText()
{
return swords + " sword" + (swords == 1 ? "" : "s");
}

private static String displayName(MetalBarType type)
{
switch (type)
{
case BRONZE:
return "Bronze";
case IRON:
return "Iron";
case STEEL:
return "Steel";
case MITHRIL:
return "Mithril";
case ADAMANT:
return "Adamant";
case RUNITE:
return "Rune";
default:
throw new IllegalArgumentException("Unknown metal type: " + type);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.toofifty.easygiantsfoundry;

import com.toofifty.easygiantsfoundry.enums.MetalBarType;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Optional;

import static com.toofifty.easygiantsfoundry.MathUtil.max1;

@Singleton
public class FoundryRecommendationPlanner
{
private static final int SWORD_METAL_COUNT = 28;

@Inject
private MetalBarCounter metalBarCounter;

public Optional<FoundryRecommendation> recommend(int smithingLevel)
{
Optional<FoundryRecommendation> best = Optional.empty();

for (MetalBarType type : MetalBarType.values())
{
best = chooseBest(best, evaluate(smithingLevel, type, SWORD_METAL_COUNT, null, 0));
}

MetalBarType[] types = MetalBarType.values();
for (int i = 0; i < types.length - 1; i++)
{
MetalBarType lower = types[i];
MetalBarType higher = types[i + 1];
for (int lowerCount = 1; lowerCount < SWORD_METAL_COUNT; lowerCount++)
{
best = chooseBest(best, evaluate(smithingLevel, lower, lowerCount, higher, SWORD_METAL_COUNT - lowerCount));
}
}

return best;
}

private Optional<FoundryRecommendation> chooseBest(
Optional<FoundryRecommendation> current,
Optional<FoundryRecommendation> candidate)
{
if (candidate.isEmpty())
{
return current;
}

if (current.isEmpty())
{
return candidate;
}

return compare(candidate.get(), current.get()) > 0 ? candidate : current;
}

private int compare(FoundryRecommendation left, FoundryRecommendation right)
{
return Comparator
.comparingInt((FoundryRecommendation recommendation) -> recommendation.getMetalScore() * recommendation.getSwords())
.thenComparingInt(FoundryRecommendation::getMetalScore)
.thenComparingInt(FoundryRecommendation::getSwords)
.compare(left, right);
}

private Optional<FoundryRecommendation> evaluate(
int smithingLevel,
MetalBarType primary,
int primaryCount,
MetalBarType secondary,
int secondaryCount)
{
if (smithingLevel < levelRequirement(primary) || (secondary != null && smithingLevel < levelRequirement(secondary)))
{
return Optional.empty();
}

int swords = metalBarCounter.getFoundryInput(primary) / primaryCount;
if (secondary != null)
{
swords = Math.min(swords, metalBarCounter.getFoundryInput(secondary) / secondaryCount);
}

if (swords == 0)
{
return Optional.empty();
}

int metalScore = getMetalScore(primary, primaryCount, secondary, secondaryCount);

return Optional.of(new FoundryRecommendation(
primary,
primaryCount,
secondary,
secondaryCount,
swords,
metalScore));
}

static int getMetalScore(MetalBarType primary, int primaryCount, MetalBarType secondary, int secondaryCount)
{
Double[] metals = new Double[] {
getWeightedValue(primary, primaryCount),
secondary == null ? 0d : getWeightedValue(secondary, secondaryCount)
};

Arrays.sort(metals, Collections.reverseOrder());

return (int) ((10 * metals[0] + 10 * metals[1] + max1(metals[0]) * max1(metals[1])) / 10.0);
}

private static double getWeightedValue(MetalBarType type, int count)
{
return (10 * metalTier(type) * count) / (double) SWORD_METAL_COUNT;
}

private static int metalTier(MetalBarType type)
{
switch (type)
{
case BRONZE:
return 1;
case IRON:
return 2;
case STEEL:
return 3;
case MITHRIL:
return 4;
case ADAMANT:
return 5;
case RUNITE:
return 6;
default:
throw new IllegalArgumentException("Unknown metal type: " + type);
}
}

private static int levelRequirement(MetalBarType type)
{
switch (type)
{
case BRONZE:
case IRON:
return 15;
case STEEL:
return 30;
case MITHRIL:
return 50;
case ADAMANT:
return 70;
case RUNITE:
return 85;
default:
throw new IllegalArgumentException("Unknown metal type: " + type);
}
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/toofifty/easygiantsfoundry/MetalBarCounter.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ public int get(MetalBarType type)
return count;
}

public int getFoundryInput(MetalBarType type)
{
int count = 0;
for (Map<MetalBarType, CountsBySource> counts : index.values())
{
count += counts.get(type).sumFoundryInput(config);
}

return count;
}

public void clear()
{
index.clear();
Expand Down Expand Up @@ -144,5 +155,12 @@ public int sum(EasyGiantsFoundryConfig config)
sum += config.countEquipment() ? equipment : 0;
return sum;
}

public int sumFoundryInput(EasyGiantsFoundryConfig config)
{
int sum = config.countBars() ? bars : 0;
sum += config.countEquipment() ? equipment : 0;
return sum;
}
}
}
Loading