nice
This commit is contained in:
19
src/main/java/ee/futur/easygiantsfoundry/BonusWidget.java
Normal file
19
src/main/java/ee/futur/easygiantsfoundry/BonusWidget.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
|
||||
public class BonusWidget
|
||||
{
|
||||
private static final int BONUS_WIDGET = 49414148;
|
||||
private static final int BONUS_COLOR = 0xfcd703;
|
||||
|
||||
static boolean isActive(Client client)
|
||||
{
|
||||
Widget bonusWidget = client.getWidget(BONUS_WIDGET);
|
||||
return bonusWidget != null
|
||||
&& bonusWidget.getChildren() != null
|
||||
&& bonusWidget.getChildren().length != 0
|
||||
&& bonusWidget.getChild(0).getTextColor() == BONUS_COLOR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
public class EasyGiantsFoundryClientIDs
|
||||
{
|
||||
// heat and progress are from 0-1000
|
||||
protected static final int VARBIT_HEAT = 13948;
|
||||
protected static final int VARBIT_PROGRESS = 13949;
|
||||
|
||||
protected static final int VARBIT_BRONZE_COUNT = 13931;
|
||||
protected static final int VARBIT_IRON_COUNT = 13932;
|
||||
protected static final int VARBIT_STEEL_COUNT = 13933;
|
||||
protected static final int VARBIT_MITHRIL_COUNT = 13934;
|
||||
protected static final int VARBIT_ADAMANT_COUNT = 13935;
|
||||
protected static final int VARBIT_RUNE_COUNT = 13936;
|
||||
|
||||
protected static final int VARBIT_FORTE_SELECTED = 13910;
|
||||
protected static final int VARBIT_BLADE_SELECTED = 13911;
|
||||
protected static final int VARBIT_TIP_SELECTED = 13912;
|
||||
|
||||
// 0 - load bars
|
||||
// 1 - set mould
|
||||
// 2 - collect preform
|
||||
// 3 -
|
||||
protected static final int VARBIT_GAME_STAGE = 13914;
|
||||
|
||||
protected static final int VARBIT_PREFORM_STORED = 13947;
|
||||
|
||||
protected static final int WIDGET_HEAT_PARENT = 49414153;
|
||||
protected static final int WIDGET_LOW_HEAT_PARENT = 49414163;
|
||||
protected static final int WIDGET_MED_HEAT_PARENT = 49414164;
|
||||
protected static final int WIDGET_HIGH_HEAT_PARENT = 49414165;
|
||||
|
||||
protected static final int WIDGET_PROGRESS_PARENT = 49414219;
|
||||
// children with type 3 are stage boxes
|
||||
// every 11th child is a sprite
|
||||
|
||||
protected static final int SPRITE_ID_TRIP_HAMMER = 4442;
|
||||
protected static final int SPRITE_ID_GRINDSTONE = 4443;
|
||||
protected static final int SPRITE_ID_POLISHING_WHEEL = 4444;
|
||||
|
||||
protected static final int ANIMATION_HEATING = 827;
|
||||
protected static final int ANIMATION_COOLING = 832;
|
||||
}
|
||||
@@ -0,0 +1,712 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.FontType;
|
||||
import net.runelite.client.config.Alpha;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.ConfigSection;
|
||||
import net.runelite.client.config.Range;
|
||||
import net.runelite.client.config.Notification;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
|
||||
@ConfigGroup(EasyGiantsFoundryConfig.GROUP)
|
||||
public interface EasyGiantsFoundryConfig extends Config
|
||||
{
|
||||
String GROUP = "easygiantsfoundry";
|
||||
String SOUND_ID = "soundID";
|
||||
String POINTS_KEY = "easygiantsfoundrypoints";
|
||||
|
||||
@ConfigSection(
|
||||
name = "Notifications",
|
||||
description = "Notifications",
|
||||
position = 0
|
||||
)
|
||||
String notificationList = "notificationList";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "giantsFoundryStageNotification",
|
||||
name = "Notify stage changes",
|
||||
description = "Notifies just before completing a stage",
|
||||
position = 0,
|
||||
section = notificationList
|
||||
)
|
||||
default Notification stageNotification()
|
||||
{
|
||||
return Notification.ON;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "giantsFoundryHeatNotification",
|
||||
name = "Notify heat changes",
|
||||
description = "Notifies just before overheating/cooling when using tools",
|
||||
position = 1,
|
||||
section = notificationList
|
||||
)
|
||||
default Notification heatNotification()
|
||||
{
|
||||
return Notification.ON;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "giantsFoundryStageThreshold",
|
||||
name = "Stage threshold notification",
|
||||
description = "The number of actions left required for the notification.",
|
||||
position = 2,
|
||||
section = notificationList
|
||||
)
|
||||
default int StageNotificationsThreshold()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "giantsFoundryHeatThreshold",
|
||||
name = "Heat threshold notification",
|
||||
description = "The heat level left required for the notification.",
|
||||
position = 3,
|
||||
section = notificationList
|
||||
)
|
||||
default int HeatNotificationsThreshold()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "bonusNotification",
|
||||
name = "Notify bonus",
|
||||
description = "Notifies when bonus appears",
|
||||
position = 4,
|
||||
section = notificationList
|
||||
)
|
||||
default Notification bonusNotification()
|
||||
{
|
||||
return Notification.OFF;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "bonusSound",
|
||||
name = "Bonus sound",
|
||||
description = "Plays a sound when bonus appears",
|
||||
position = 5,
|
||||
section = notificationList
|
||||
)
|
||||
default boolean bonusSoundNotify()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = SOUND_ID,
|
||||
name = "Bonus sound ID",
|
||||
description = "Sound Effect ID to play when bonus appears",
|
||||
position = 6,
|
||||
section = notificationList
|
||||
)
|
||||
default int soundId()
|
||||
{
|
||||
return 4212;
|
||||
}
|
||||
|
||||
|
||||
@ConfigSection(
|
||||
name = "Highlights",
|
||||
description = "3D npc/object highlights",
|
||||
position = 1
|
||||
)
|
||||
String highlightList = "highlightList";
|
||||
|
||||
@ConfigItem(
|
||||
name = "Highlight Style",
|
||||
description = "The style of the highlight",
|
||||
position = 0,
|
||||
section = highlightList,
|
||||
keyName = "overlayOption")
|
||||
default HighlightStyle highlightStyle()
|
||||
{
|
||||
return HighlightStyle.HIGHLIGHT_CLICKBOX;
|
||||
}
|
||||
|
||||
@Range(
|
||||
min = 1,
|
||||
max = 4
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "borderThickness",
|
||||
name = "Border Thickness",
|
||||
description = "The thickness of the border",
|
||||
position = 1,
|
||||
section = highlightList
|
||||
)
|
||||
default int borderThickness()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Range(
|
||||
min = 0,
|
||||
max = 4
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "borderFeather",
|
||||
name = "Border Feather",
|
||||
description = "The feather of the border",
|
||||
position = 2,
|
||||
section = highlightList
|
||||
)
|
||||
default int borderFeather()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// alpha
|
||||
@Range(
|
||||
min = 0,
|
||||
max = 255
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "borderAlpha",
|
||||
name = "Border Alpha",
|
||||
description = "The alpha of the border highlight",
|
||||
position = 3,
|
||||
section = highlightList
|
||||
)
|
||||
default int borderAlpha()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "toolsHighlight",
|
||||
name = "Highlight Tools",
|
||||
description = "Highlights current tool with symbolic colors",
|
||||
position = 4,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean highlightTools()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Range(
|
||||
min = 100,
|
||||
max = 200
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "clickboxScale",
|
||||
name = "Clickbox scale %",
|
||||
description = "Scale clickbox highlights to make targets easier to click.",
|
||||
position = 4,
|
||||
section = highlightList
|
||||
)
|
||||
default int clickboxScale()
|
||||
{
|
||||
return 150;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "waterLavaHighlight",
|
||||
name = "Highlight Waterfall/Lava Pool",
|
||||
description = "Highlight Lava Pool / Waterfall when heat change required",
|
||||
position = 5,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean highlightWaterAndLava()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "mouldHighlight",
|
||||
name = "Highlight Mould",
|
||||
description = "Highlight Mould when it should be clicked",
|
||||
position = 6,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean highlightMould()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "crucibleHighlight",
|
||||
name = "Highlight Crucible",
|
||||
description = "Highlight Crucible when it should be filled/poured",
|
||||
position = 7,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean highlightCrucible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "kovacHighlight",
|
||||
name = "Highlight Kovac for hand in",
|
||||
description = "Highlight Kovac when sword can be handed in",
|
||||
position = 8,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean highlightKovac()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "crucibleContent",
|
||||
name = "Show Crucible content and quality",
|
||||
description = "Show the content and quality of the crucible",
|
||||
position = 9,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean showCrucibleContent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "storageHighlight",
|
||||
name = "Highlight Preform Storage",
|
||||
description = "Highlight Storage when it contains a preform.",
|
||||
position = 10,
|
||||
section = highlightList
|
||||
)
|
||||
default boolean highlightStorage()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigSection(
|
||||
name = "Info Panel",
|
||||
description = "Settings for the Info Panel overlay",
|
||||
position = 2
|
||||
)
|
||||
String infoPanelList = "infoPanelList";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "infoTitle",
|
||||
name = "Title",
|
||||
description = "Toggle for \"Easy Giants' Foundry\" text",
|
||||
position = 0,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawTitle()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatInfo",
|
||||
name = "Heat",
|
||||
description = "Toggle for Heat text",
|
||||
position = 1,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawHeatInfo()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "stageInfo",
|
||||
name = "Stage",
|
||||
description = "Toggle for Stage text",
|
||||
position = 2,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawStageInfo()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "actionLeft",
|
||||
name = "Actions Left",
|
||||
description = "Toggle for actions left text",
|
||||
position = 3,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawActionsLeft()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatLeft",
|
||||
name = "Heat Left",
|
||||
description = "Toggle for heat left text",
|
||||
position = 4,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawHeatLeft()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "bonusActions",
|
||||
name = "Bonus Actions",
|
||||
description = "Toggle for Bonus actions text",
|
||||
position = 5,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawBonusActions()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "shopPoints",
|
||||
name = "Reputation",
|
||||
description = "Toggle for reputation text",
|
||||
position = 6,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawShopPoints()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatDelta",
|
||||
name = "Heat change",
|
||||
description = "Show the heat delta and recommended location for the next adjust.",
|
||||
position = 7,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawHeatDelta()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatPlan",
|
||||
name = "Heat plan",
|
||||
description = "Show fast/slow heat action plans with predicted results.",
|
||||
position = 8,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawHeatPlan()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatActionTimer",
|
||||
name = "Heat action timer",
|
||||
description = "Show the active heat action countdown in the info panel.",
|
||||
position = 9,
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawHeatActionTimer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigSection(
|
||||
name = "Info Overlay",
|
||||
description = "Overlay Text Info On Objects",
|
||||
position = 3
|
||||
)
|
||||
String infoOverlay = "infoOverlay";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "actionLeftOverlay",
|
||||
name = "Actions Left Overlay",
|
||||
description = "Toggle for actions left overlay",
|
||||
position = 0,
|
||||
section = infoOverlay
|
||||
)
|
||||
default boolean drawActionLeftOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatLeftOverlay",
|
||||
name = "Heat Left Overlay",
|
||||
description = "Toggle for heat left overlay",
|
||||
position = 1,
|
||||
section = infoOverlay
|
||||
)
|
||||
default boolean drawHeatLeftOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "crucibleInfoOverlay",
|
||||
name = "Crucible Info Overlay",
|
||||
description = "Toggle for crucible info overlay",
|
||||
position = 2,
|
||||
section = infoOverlay
|
||||
)
|
||||
default boolean drawCrucibleInfoOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "mouldInfoOverlay",
|
||||
name = "Mould Info Overlay",
|
||||
description = "Toggle for mould info overlay",
|
||||
position = 3,
|
||||
section = infoOverlay
|
||||
)
|
||||
default boolean drawMouldInfoOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "LavaWaterInfoOverlay",
|
||||
name = "Lava/Waterfall Info Overlay",
|
||||
description = "Toggle for lava/waterfall info overlay",
|
||||
position = 4,
|
||||
section = infoOverlay
|
||||
)
|
||||
default boolean drawLavaWaterInfoOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigSection(
|
||||
name = "Colour",
|
||||
description = "Colours",
|
||||
position = 4
|
||||
)
|
||||
String colourList = "colourList";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "mouldText",
|
||||
name = "Mould Text",
|
||||
description = "Colour for optimal mould text",
|
||||
position = 0,
|
||||
section = colourList
|
||||
)
|
||||
default Color mouldTextColour()
|
||||
{
|
||||
return new Color(0xdc10d);
|
||||
}
|
||||
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "generalColour",
|
||||
name = "General",
|
||||
description = "Colour for highlighting objects/npcs in general",
|
||||
position = 1,
|
||||
section = colourList
|
||||
)
|
||||
default Color generalHighlight()
|
||||
{
|
||||
return Color.CYAN;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "lavaWaterColour",
|
||||
name = "Lava/Waterfall",
|
||||
description = "Colour for highlighting lava/waterfall",
|
||||
position = 2,
|
||||
section = colourList
|
||||
)
|
||||
default Color lavaWaterfallColour()
|
||||
{
|
||||
return ColorScheme.PROGRESS_COMPLETE_COLOR;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "toolGood",
|
||||
name = "Tool Good",
|
||||
description = "Colour for highlighting current tool when they are usable",
|
||||
position = 3,
|
||||
section = colourList
|
||||
)
|
||||
default Color toolGood()
|
||||
{
|
||||
return ColorScheme.PROGRESS_COMPLETE_COLOR;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "toolBad",
|
||||
name = "Tool Bad",
|
||||
description = "Colour for highlighting current tool when they are not usable",
|
||||
position = 4,
|
||||
section = colourList
|
||||
)
|
||||
default Color toolBad()
|
||||
{
|
||||
return ColorScheme.PROGRESS_ERROR_COLOR;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "toolCaution",
|
||||
name = "Tool Caution",
|
||||
description = "Colour for highlighting current tool when they are about to be not usable",
|
||||
position = 5,
|
||||
section = colourList
|
||||
)
|
||||
default Color toolCaution()
|
||||
{
|
||||
return ColorScheme.PROGRESS_INPROGRESS_COLOR;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "toolBonus",
|
||||
name = "Tool Bonus",
|
||||
description = "Colour for highlighting current tool when they have a bonus to click on",
|
||||
position = 6,
|
||||
section = colourList
|
||||
)
|
||||
default Color toolBonus()
|
||||
{
|
||||
return Color.CYAN;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "textBackground",
|
||||
name = "Text Background",
|
||||
description = "Set a color to draw a box behind text.",
|
||||
position = 7,
|
||||
section = colourList
|
||||
)
|
||||
@Alpha
|
||||
default Color textBackground()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "dynamicOverlayFont",
|
||||
name = "Dynamic Overlay Font",
|
||||
description = "Choose the font type for the info overlay.<br/>" +
|
||||
"Defaults to your setting from RuneLite -> Overlay settings -> Dynamic overlay font.",
|
||||
position = 10,
|
||||
section = colourList
|
||||
)
|
||||
default FontType dynamicOverlayFont()
|
||||
{
|
||||
return FontType.DEFAULT;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "textOutline",
|
||||
name = "Text Outline",
|
||||
description = "Use an outline around text instead of a shadow.",
|
||||
position = 11,
|
||||
section = colourList
|
||||
)
|
||||
default boolean textOutline()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = -100,
|
||||
keyName = "alwaysShowInfoPanel",
|
||||
name = "Always show",
|
||||
description = "Always show the info panel, even outside of Giants' Foundry.",
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean alwaysDrawInfoPanel()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 100,
|
||||
keyName = "drawMetals",
|
||||
name = "Metals",
|
||||
description = "Show total metals count in the info panel.",
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawMetals()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 101,
|
||||
keyName = "drawAllMetals",
|
||||
name = "Metals: show all",
|
||||
description = "Show rows for metals even if you don't have any of that type.",
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean drawAllMetals()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 110,
|
||||
keyName = "countOre",
|
||||
name = "Metals: count ore",
|
||||
description = "Include raw ores in the metals count.",
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean countOre()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 111,
|
||||
keyName = "countBars",
|
||||
name = "Metals: count bars",
|
||||
description = "Include smelted bars in the metals count.",
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean countBars()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 112,
|
||||
keyName = "countEquipment",
|
||||
name = "Metals: count equipment",
|
||||
description = "Include equipment in the metals count.",
|
||||
section = infoPanelList
|
||||
)
|
||||
default boolean countEquipment()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigSection(
|
||||
name = "Advanced",
|
||||
description = "Advanced Settings",
|
||||
position = 5
|
||||
)
|
||||
String advancedSettings = "generalSettings";
|
||||
|
||||
|
||||
@Range(
|
||||
max = 50
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "heatActionBuffer", // renamed to reset player's settings for previous bugged implementation
|
||||
name = "Padding Ticks",
|
||||
description = "Number of inefficient idle ticks between actions; calculations will pad more than optimal heat compensate for heat decay during idle/afk.",
|
||||
position = 0,
|
||||
section = advancedSettings
|
||||
)
|
||||
default int heatActionPadTicks()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "debugging",
|
||||
name = "Show Debugging",
|
||||
description = "Shows debugging visuals used for development",
|
||||
position = 0,
|
||||
section = advancedSettings,
|
||||
warning = "Only used for development."
|
||||
)
|
||||
default boolean debugging()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.awt.Color;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public final class EasyGiantsFoundryHelper
|
||||
{
|
||||
public static Color getHeatColor(int actions, int heat)
|
||||
{
|
||||
if (heat >= actions)
|
||||
{
|
||||
return ColorScheme.PROGRESS_COMPLETE_COLOR;
|
||||
}
|
||||
|
||||
if (heat > 0)
|
||||
{
|
||||
return ColorScheme.PROGRESS_INPROGRESS_COLOR;
|
||||
}
|
||||
|
||||
return ColorScheme.PROGRESS_ERROR_COLOR;
|
||||
}
|
||||
|
||||
public static void renderTextLocation(Graphics2D graphics, Point point, String text, Color fg, Color bg, boolean outline)
|
||||
{
|
||||
if (bg != null)
|
||||
{
|
||||
FontMetrics fm = graphics.getFontMetrics();
|
||||
graphics.setColor(bg);
|
||||
graphics.fillRect(point.getX(), point.getY() - fm.getHeight(), fm.stringWidth(text), fm.getHeight());
|
||||
}
|
||||
|
||||
graphics.setColor(Color.BLACK);
|
||||
if (outline)
|
||||
{
|
||||
graphics.drawString(text, point.getX(), point.getY() + 1);
|
||||
graphics.drawString(text, point.getX(), point.getY() - 1);
|
||||
graphics.drawString(text, point.getX() + 1, point.getY());
|
||||
graphics.drawString(text, point.getX() - 1, point.getY());
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.drawString(text, point.getX() + 1, point.getY() + 1);
|
||||
}
|
||||
|
||||
graphics.setColor(fg);
|
||||
graphics.drawString(text, point.getX(), point.getY());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
|
||||
import static ee.futur.easygiantsfoundry.EasyGiantsFoundryClientIDs.VARBIT_GAME_STAGE;
|
||||
import static ee.futur.easygiantsfoundry.EasyGiantsFoundryClientIDs.VARBIT_HEAT;
|
||||
import ee.futur.easygiantsfoundry.enums.Stage;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.gameval.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.events.*;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@PluginDescriptor(
|
||||
name = "Easy Giants' Foundry",
|
||||
description = "Helpful overlays for the Giants' Foundry minigame"
|
||||
)
|
||||
public class EasyGiantsFoundryPlugin extends Plugin
|
||||
{
|
||||
private static final int TRIP_HAMMER = 44619;
|
||||
private static final int GRINDSTONE = 44620;
|
||||
private static final int POLISHING_WHEEL = 44621;
|
||||
|
||||
private static final int LAVA_POOL = 44631;
|
||||
private static final int WATERFALL = 44632;
|
||||
|
||||
private static final int CRUCIBLE = 44776;
|
||||
private static final int MOULD_JIG = 44777;
|
||||
private static final int STORAGE = 44778;
|
||||
|
||||
private static final int KOVAC_NPC = 11472;
|
||||
|
||||
private static final int PREFORM = 27010;
|
||||
|
||||
private static final int REPUTATION_VARBIT = 3436;
|
||||
|
||||
// 5 total items, includes Smiths gloves (i);
|
||||
private static final Set<Integer> SMITHS_OUTFIT_IDS = Set.of(27023, 27025, 27027, 27029, 27031);
|
||||
|
||||
private Stage oldStage;
|
||||
|
||||
private int lastBoost;
|
||||
|
||||
private boolean bonusNotified = false;
|
||||
|
||||
@Getter
|
||||
private int reputation;
|
||||
|
||||
@Inject
|
||||
private EasyGiantsFoundryState state;
|
||||
|
||||
@Inject
|
||||
private EasyGiantsFoundryHelper helper;
|
||||
|
||||
@Inject
|
||||
private MetalBarCounter metalBarCounter;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private FoundryOverlay2D overlay2d;
|
||||
|
||||
@Inject
|
||||
private FoundryOverlay3D overlay3d;
|
||||
|
||||
@Inject
|
||||
private MouldHelper mouldHelper;
|
||||
|
||||
@Inject
|
||||
private EasyGiantsFoundryConfig config;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
overlayManager.add(overlay2d);
|
||||
overlayManager.add(overlay3d);
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
reputation = client.getVarpValue(REPUTATION_VARBIT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
overlayManager.remove(overlay2d);
|
||||
overlayManager.remove(overlay3d);
|
||||
metalBarCounter.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameObjectSpawned(GameObjectSpawned event)
|
||||
{
|
||||
GameObject gameObject = event.getGameObject();
|
||||
switch (gameObject.getId())
|
||||
{
|
||||
case POLISHING_WHEEL:
|
||||
state.setEnabled(true);
|
||||
overlay3d.polishingWheel = gameObject;
|
||||
break;
|
||||
case GRINDSTONE:
|
||||
overlay3d.grindstone = gameObject;
|
||||
break;
|
||||
case LAVA_POOL:
|
||||
overlay3d.lavaPool = gameObject;
|
||||
break;
|
||||
case WATERFALL:
|
||||
overlay3d.waterfall = gameObject;
|
||||
break;
|
||||
case TRIP_HAMMER:
|
||||
overlay3d.tripHammer = gameObject;
|
||||
break;
|
||||
case MOULD_JIG:
|
||||
overlay3d.mouldJig = gameObject;
|
||||
break;
|
||||
case CRUCIBLE:
|
||||
overlay3d.crucible = gameObject;
|
||||
break;
|
||||
case STORAGE:
|
||||
overlay3d.storage = gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
public void onGameObjectDespawned(GameObjectDespawned event)
|
||||
{
|
||||
GameObject gameObject = event.getGameObject();
|
||||
switch (gameObject.getId())
|
||||
{
|
||||
case POLISHING_WHEEL:
|
||||
state.setEnabled(false);
|
||||
overlay3d.polishingWheel = null;
|
||||
break;
|
||||
case GRINDSTONE:
|
||||
overlay3d.grindstone = null;
|
||||
break;
|
||||
case LAVA_POOL:
|
||||
overlay3d.lavaPool = null;
|
||||
break;
|
||||
case WATERFALL:
|
||||
overlay3d.waterfall = null;
|
||||
break;
|
||||
case TRIP_HAMMER:
|
||||
overlay3d.tripHammer = null;
|
||||
break;
|
||||
case MOULD_JIG:
|
||||
overlay3d.mouldJig = null;
|
||||
break;
|
||||
case CRUCIBLE:
|
||||
overlay3d.crucible = null;
|
||||
break;
|
||||
case STORAGE:
|
||||
overlay3d.storage = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (event.getGameState().equals(GameState.LOADING))
|
||||
{
|
||||
state.setEnabled(false);
|
||||
}
|
||||
|
||||
if (event.getGameState().equals(GameState.LOGGED_IN))
|
||||
{
|
||||
reputation = client.getVarpValue(REPUTATION_VARBIT);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onStatChanged(StatChanged statChanged)
|
||||
{
|
||||
final int curBoost = statChanged.getBoostedLevel();
|
||||
// if the difference between current and last boost is != 0 then a stat boost (or drop) change occurred
|
||||
if (!statChanged.getSkill().equals(Skill.SMITHING) ||
|
||||
curBoost != lastBoost ||
|
||||
!state.isEnabled() ||
|
||||
state.getCurrentStage() == null)
|
||||
{
|
||||
lastBoost = curBoost;
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.stageNotification().isEnabled() &&
|
||||
state.getActionsLeftInStage() == config.StageNotificationsThreshold() &&
|
||||
(oldStage == null || oldStage != state.getCurrentStage()))
|
||||
{
|
||||
notifier.notify(config.stageNotification(), "About to finish the current stage!");
|
||||
oldStage = state.getCurrentStage();
|
||||
}
|
||||
else if (config.heatNotification().isEnabled() &&
|
||||
state.getActionsForHeatLevel() == config.HeatNotificationsThreshold())
|
||||
{
|
||||
notifier.notify(config.heatNotification(), "About to run out of heat!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
public void onNpcSpawned(NpcSpawned event)
|
||||
{
|
||||
if (event.getNpc().getId() == KOVAC_NPC)
|
||||
{
|
||||
overlay3d.kovac = event.getNpc();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNpcDespawned(NpcDespawned event)
|
||||
{
|
||||
if (event.getNpc().getId() == KOVAC_NPC)
|
||||
{
|
||||
overlay3d.kovac = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onItemContainerChanged(ItemContainerChanged event)
|
||||
{
|
||||
if (event.getContainerId() == InventoryID.WORN)
|
||||
{
|
||||
if (event.getItemContainer().count(PREFORM) == 0)
|
||||
{
|
||||
state.reset();
|
||||
oldStage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
updateSmithsOutfitPieces();
|
||||
}
|
||||
}
|
||||
else if (event.getContainerId() == InventoryID.INV || event.getContainerId() == InventoryID.BANK)
|
||||
{
|
||||
metalBarCounter.put(event.getItemContainer());
|
||||
}
|
||||
}
|
||||
|
||||
public void onMenuEntryAdded(MenuEntryAdded event)
|
||||
{
|
||||
if (event.getOption().startsWith("Heat-preform") || event.getOption().startsWith("Dunk-preform"))
|
||||
{
|
||||
}
|
||||
else if (event.getOption().startsWith("Cool-preform") || event.getOption().startsWith("Quench-preform")) {
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuOptionClicked(MenuOptionClicked event)
|
||||
{
|
||||
clientThread.invokeAtTickEnd(() ->
|
||||
{
|
||||
if (!(event.getMenuAction() == MenuAction.GAME_OBJECT_FIRST_OPTION
|
||||
|| event.getMenuAction() == MenuAction.GAME_OBJECT_SECOND_OPTION
|
||||
|| event.getMenuAction() == MenuAction.GAME_OBJECT_THIRD_OPTION
|
||||
|| event.getMenuAction() == MenuAction.GAME_OBJECT_FOURTH_OPTION
|
||||
|| event.getMenuAction() == MenuAction.GAME_OBJECT_FIFTH_OPTION
|
||||
|| event.getMenuAction() == MenuAction.WIDGET_TARGET_ON_GAME_OBJECT
|
||||
|| event.getMenuAction() == MenuAction.WALK))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.isEnabled()) return;
|
||||
|
||||
if (event.getMenuTarget().contains("Crucible "))
|
||||
{
|
||||
if (event.getMenuOption().equals("Pour"))
|
||||
{
|
||||
if (client.getVarbitValue(VARBIT_GAME_STAGE) == 1)
|
||||
{
|
||||
state.setLastKnownCrucibleScore((int) state.getCrucibleScore());
|
||||
}
|
||||
// add persistent game message of the alloy value so user can reference later.
|
||||
client.addChatMessage(ChatMessageType.GAMEMESSAGE, "", "The score of the preform is <col=00FFFF>" + ((int) state.getCrucibleScore() + state.getMouldScore()), null);
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find a varbit to capture, so capture the menu-option directly.
|
||||
// start the HeatActionStateMachine when varbit begins to update in onVarbitChanged()
|
||||
if (event.getMenuOption().startsWith("Heat-preform"))
|
||||
{
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(false, true, "heats");
|
||||
}
|
||||
else if (event.getMenuOption().startsWith("Dunk-preform"))
|
||||
{
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(true, true, "dunks");
|
||||
}
|
||||
else if (event.getMenuOption().startsWith("Cool-preform"))
|
||||
{
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(false, false, "cools");
|
||||
}
|
||||
else if (event.getMenuOption().startsWith("Quench-preform"))
|
||||
{
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(true, false, "quenches");
|
||||
}
|
||||
else if (!state.heatActionStateMachine.isIdle()) // canceled heating/cooling, stop the heating state-machine
|
||||
{
|
||||
state.heatActionStateMachine.stop();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptPostFired(ScriptPostFired event)
|
||||
{
|
||||
if (event.getScriptId() == MouldHelper.DRAW_MOULD_LIST_SCRIPT
|
||||
|| event.getScriptId() == MouldHelper.REDRAW_MOULD_LIST_SCRIPT
|
||||
|| event.getScriptId() == MouldHelper.SELECT_MOULD_SCRIPT
|
||||
|| event.getScriptId() == MouldHelper.RESET_MOULD_SCRIPT)
|
||||
{
|
||||
mouldHelper.selectBest(event.getScriptId());
|
||||
updateMouldScore();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMouldScore() {
|
||||
|
||||
state.setMouldScore(mouldHelper.getTotalScore());
|
||||
|
||||
// show mould score on Mould UI Title
|
||||
Widget mouldParent = client.getWidget(47054850);
|
||||
int mouldScore = state.getMouldScore();
|
||||
if (mouldParent != null && mouldScore >= 0)
|
||||
{
|
||||
Widget title = Objects.requireNonNull(mouldParent.getChild(1));
|
||||
|
||||
// not sure why, the ":" character turns into ": ," when rendered; omitting it.
|
||||
title.setText("Giants' Foundry Mould Setup <col=FFFFFF>(Score " + mouldScore + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// previous heat varbit value, used to filter out passive heat decay.
|
||||
private int previousHeat = 0;
|
||||
|
||||
@Subscribe
|
||||
public void onVarbitChanged(VarbitChanged event)
|
||||
{
|
||||
if (event.getVarpId() == REPUTATION_VARBIT)
|
||||
{
|
||||
reputation = client.getVarpValue(REPUTATION_VARBIT);
|
||||
}
|
||||
|
||||
// STAGE becomes 0 again after player picks up the preform
|
||||
if (event.getVarbitId() == VARBIT_GAME_STAGE && event.getValue() == 0)
|
||||
{
|
||||
// clear out the current and soon to be previous scores.
|
||||
state.setLastKnownCrucibleScore(-1);
|
||||
state.setMouldScore(-1);
|
||||
}
|
||||
|
||||
|
||||
// start the heating state-machine when the varbit updates
|
||||
// if heat varbit updated and the user clicked, start the state-machine
|
||||
if (event.getVarbitId() == VARBIT_HEAT)
|
||||
{
|
||||
// ignore passive heat decay, one heat per two ticks
|
||||
int delta = event.getValue() - previousHeat;
|
||||
// sign check: num * num > 0 == same sign
|
||||
if (delta != -1)
|
||||
{
|
||||
if (state.heatActionStateMachine.getActionname() != null)
|
||||
{
|
||||
// if the state-machine is idle, start it
|
||||
if (state.heatActionStateMachine.isIdle())
|
||||
{
|
||||
state.heatActionStateMachine.start(state, config, previousHeat);
|
||||
}
|
||||
state.heatActionStateMachine.onTick();
|
||||
}
|
||||
|
||||
if (config.debugging())
|
||||
{
|
||||
client.addChatMessage(ChatMessageType.GAMEMESSAGE, "",
|
||||
"Heat: <col=FF0000>" + event.getValue() + "</col>" +
|
||||
"Delta: <col=00FFFF>" + delta + "</col> " +
|
||||
"Heating Ticks: <col=00FFFF>" + state.heatActionStateMachine.heatingTicks + "</col>" +
|
||||
" Cooling Ticks: <col=00FFFF>" + state.heatActionStateMachine.coolingTicks + "</col>" +
|
||||
" Remaining Ticks: <col=00FFFF>" + state.heatActionStateMachine.getRemainingDuration(), "");
|
||||
}
|
||||
}
|
||||
|
||||
// client.addChatMessage(ChatMessageType.GAMEMESSAGE, "", "Delta: <col=00FFFF>" + delta + "</col> ", "");
|
||||
previousHeat = event.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
protected void onConfigChanged(ConfigChanged configChanged)
|
||||
{
|
||||
if (!EasyGiantsFoundryConfig.GROUP.equals(configChanged.getGroup()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EasyGiantsFoundryConfig.SOUND_ID.equals(configChanged.getKey()))
|
||||
{
|
||||
clientThread.invoke(() -> client.playSoundEffect(config.soundId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
checkBonus();
|
||||
}
|
||||
|
||||
private void checkBonus()
|
||||
{
|
||||
if (!state.isEnabled() || state.getCurrentStage() == null
|
||||
|| state.getCurrentStage().getHeat() != state.getCurrentHeat()
|
||||
|| !BonusWidget.isActive(client))
|
||||
{
|
||||
bonusNotified = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bonusNotified)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
state.addBonusActionReceived();
|
||||
|
||||
if (config.bonusNotification().isEnabled())
|
||||
{
|
||||
notifier.notify(config.bonusNotification(), "Bonus - Click tool");
|
||||
}
|
||||
if (config.bonusSoundNotify())
|
||||
{
|
||||
client.playSoundEffect(config.soundId());
|
||||
}
|
||||
|
||||
bonusNotified = true;
|
||||
}
|
||||
|
||||
private void updateSmithsOutfitPieces()
|
||||
{
|
||||
int pieces = 0;
|
||||
|
||||
ItemContainer equipment = client.getItemContainer(InventoryID.WORN);
|
||||
if (equipment != null)
|
||||
{
|
||||
for (Item item : equipment.getItems())
|
||||
{
|
||||
if (item != null && isSmithsOutfitPiece(item.getId()))
|
||||
{
|
||||
pieces++;
|
||||
}
|
||||
}
|
||||
}
|
||||
state.setSmithsOutfitPieces(pieces);
|
||||
}
|
||||
|
||||
private boolean isSmithsOutfitPiece(int itemId)
|
||||
{
|
||||
return SMITHS_OUTFIT_IDS.contains(itemId);
|
||||
}
|
||||
|
||||
@Provides
|
||||
EasyGiantsFoundryConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(EasyGiantsFoundryConfig.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import static ee.futur.easygiantsfoundry.MathUtil.max1;
|
||||
import static ee.futur.easygiantsfoundry.EasyGiantsFoundryClientIDs.*;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.Heat;
|
||||
import ee.futur.easygiantsfoundry.enums.Stage;
|
||||
import static ee.futur.easygiantsfoundry.enums.Stage.GRINDSTONE;
|
||||
import static ee.futur.easygiantsfoundry.enums.Stage.POLISHING_WHEEL;
|
||||
import static ee.futur.easygiantsfoundry.enums.Stage.TRIP_HAMMER;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Singleton
|
||||
public class EasyGiantsFoundryState
|
||||
{
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private boolean enabled;
|
||||
|
||||
@Getter
|
||||
private int bonusActionsReceived = 0;
|
||||
|
||||
@Setter
|
||||
private int smithsOutfitPieces;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private int mouldScore = -1; // starts -1 because mould score is unknown
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private int lastKnownCrucibleScore = -1; // will be set when "Pour"ed (because the crucible will be empty then)
|
||||
|
||||
private final List<Stage> stages = new ArrayList<>();
|
||||
private double heatRangeRatio = 0;
|
||||
|
||||
public final HeatActionStateMachine heatActionStateMachine = new HeatActionStateMachine();
|
||||
|
||||
public void reset()
|
||||
{
|
||||
stages.clear();
|
||||
heatRangeRatio = 0;
|
||||
bonusActionsReceived = 0;
|
||||
}
|
||||
|
||||
public int getHeatAmount()
|
||||
{
|
||||
return client.getVarbitValue(VARBIT_HEAT);
|
||||
}
|
||||
|
||||
public int getProgressAmount()
|
||||
{
|
||||
return client.getVarbitValue(VARBIT_PROGRESS);
|
||||
}
|
||||
|
||||
public double getHeatRangeRatio()
|
||||
{
|
||||
if (heatRangeRatio == 0)
|
||||
{
|
||||
Widget heatWidget = client.getWidget(WIDGET_HEAT_PARENT);
|
||||
Widget medHeat = client.getWidget(WIDGET_MED_HEAT_PARENT);
|
||||
if (medHeat == null || heatWidget == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
heatRangeRatio = medHeat.getWidth() / (double) heatWidget.getWidth();
|
||||
}
|
||||
|
||||
return heatRangeRatio;
|
||||
}
|
||||
|
||||
public int[] getLowHeatRange()
|
||||
{
|
||||
return new int[]{
|
||||
(int) ((1 / 6d - getHeatRangeRatio() / 2) * 1000),
|
||||
(int) ((1 / 6d + getHeatRangeRatio() / 2) * 1000),
|
||||
};
|
||||
}
|
||||
|
||||
public int[] getMedHeatRange()
|
||||
{
|
||||
return new int[]{
|
||||
(int) ((3 / 6d - getHeatRangeRatio() / 2) * 1000),
|
||||
(int) ((3 / 6d + getHeatRangeRatio() / 2) * 1000),
|
||||
};
|
||||
}
|
||||
|
||||
public int[] getHighHeatRange()
|
||||
{
|
||||
return new int[]{
|
||||
(int) ((5 / 6d - getHeatRangeRatio() / 2) * 1000),
|
||||
(int) ((5 / 6d + getHeatRangeRatio() / 2) * 1000),
|
||||
};
|
||||
}
|
||||
|
||||
public List<Stage> getStages()
|
||||
{
|
||||
if (stages.isEmpty())
|
||||
{
|
||||
Widget progressParent = client.getWidget(WIDGET_PROGRESS_PARENT);
|
||||
if (progressParent == null || progressParent.getChildren() == null)
|
||||
{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
for (Widget child : progressParent.getChildren())
|
||||
{
|
||||
switch (child.getSpriteId())
|
||||
{
|
||||
case SPRITE_ID_TRIP_HAMMER:
|
||||
stages.add(TRIP_HAMMER);
|
||||
break;
|
||||
case SPRITE_ID_GRINDSTONE:
|
||||
stages.add(GRINDSTONE);
|
||||
break;
|
||||
case SPRITE_ID_POLISHING_WHEEL:
|
||||
stages.add(POLISHING_WHEEL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stages;
|
||||
}
|
||||
|
||||
public Stage getCurrentStage()
|
||||
{
|
||||
int index = (int) (getProgressAmount() / 1000d * getStages().size());
|
||||
if (index < 0 || index > getStages().size() - 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return getStages().get(index);
|
||||
}
|
||||
|
||||
public Heat getCurrentHeat()
|
||||
{
|
||||
int heat = getHeatAmount();
|
||||
|
||||
int[] low = getLowHeatRange();
|
||||
if (heat >= low[0] && heat <= low[1])
|
||||
{
|
||||
return Heat.LOW;
|
||||
}
|
||||
|
||||
int[] med = getMedHeatRange();
|
||||
if (heat >= med[0] && heat <= med[1])
|
||||
{
|
||||
return Heat.MED;
|
||||
}
|
||||
|
||||
int[] high = getHighHeatRange();
|
||||
if (heat >= high[0] && heat <= high[1])
|
||||
{
|
||||
return Heat.HIGH;
|
||||
}
|
||||
|
||||
return Heat.NONE;
|
||||
}
|
||||
|
||||
public int getHeatChangeNeeded()
|
||||
{
|
||||
Heat requiredHeat = getCurrentStage().getHeat();
|
||||
int heat = getHeatAmount();
|
||||
|
||||
int[] range;
|
||||
switch (requiredHeat)
|
||||
{
|
||||
case LOW:
|
||||
range = getLowHeatRange();
|
||||
break;
|
||||
case MED:
|
||||
range = getMedHeatRange();
|
||||
break;
|
||||
case HIGH:
|
||||
range = getHighHeatRange();
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (heat < range[0])
|
||||
return range[0] - heat;
|
||||
else if (heat > range[1])
|
||||
return range[1] - heat;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getBonusActionsExpected()
|
||||
{
|
||||
if (getStages().size() >= 6)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
public void addBonusActionReceived()
|
||||
{
|
||||
++bonusActionsReceived;
|
||||
}
|
||||
|
||||
public int getCrucibleCount()
|
||||
{
|
||||
int bronze = client.getVarbitValue(VARBIT_BRONZE_COUNT);
|
||||
int iron = client.getVarbitValue(VARBIT_IRON_COUNT);
|
||||
int steel = client.getVarbitValue(VARBIT_STEEL_COUNT);
|
||||
int mithril = client.getVarbitValue(VARBIT_MITHRIL_COUNT);
|
||||
int adamant = client.getVarbitValue(VARBIT_ADAMANT_COUNT);
|
||||
int rune = client.getVarbitValue(VARBIT_RUNE_COUNT);
|
||||
|
||||
return bronze + iron + steel + mithril + adamant + rune;
|
||||
}
|
||||
|
||||
public double getCrucibleScore()
|
||||
{
|
||||
// https://oldschool.runescape.wiki/w/Giants%27_Foundry#Metal_score
|
||||
|
||||
if (getCrucibleCount() == 0) return 0;
|
||||
|
||||
final int BRONZE_VALUE = 1;
|
||||
final int IRON_VALUE = 2;
|
||||
final int STEEL_VALUE = 3;
|
||||
final int MITHRIL_VALUE = 4;
|
||||
final int ADAMANT_VALUE = 5;
|
||||
final int RUNE_VALUE = 6;
|
||||
|
||||
|
||||
final int bronzeNum = client.getVarbitValue(VARBIT_BRONZE_COUNT);
|
||||
final int ironNum = client.getVarbitValue(VARBIT_IRON_COUNT);
|
||||
final int steelNum = client.getVarbitValue(VARBIT_STEEL_COUNT);
|
||||
final int mithrilNum = client.getVarbitValue(VARBIT_MITHRIL_COUNT);
|
||||
final int adamantNum = client.getVarbitValue(VARBIT_ADAMANT_COUNT);
|
||||
final int runeNum = client.getVarbitValue(VARBIT_RUNE_COUNT);
|
||||
|
||||
final double bronzeVal = (10 * BRONZE_VALUE * bronzeNum) / 28.0;
|
||||
final double ironVal = (10 * IRON_VALUE * ironNum) / 28.0;
|
||||
final double steelVal = (10 * STEEL_VALUE * steelNum) / 28.0;
|
||||
final double mithrilVal = (10 * MITHRIL_VALUE * mithrilNum) / 28.0;
|
||||
final double adamantVal = (10 * ADAMANT_VALUE * adamantNum) / 28.0;
|
||||
final double runeVal = (10 * RUNE_VALUE * runeNum) / 28.0;
|
||||
|
||||
Double[] metals = new Double[] {
|
||||
bronzeVal,
|
||||
ironVal,
|
||||
steelVal,
|
||||
mithrilVal,
|
||||
adamantVal,
|
||||
runeVal
|
||||
};
|
||||
|
||||
// Descending order
|
||||
Arrays.sort(metals, Collections.reverseOrder());
|
||||
|
||||
return
|
||||
((10 * metals[0] + 10 * metals[1]) + max1(metals[0]) * max1(metals[1])) / 10.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of progress each stage needs
|
||||
*/
|
||||
public double getProgressPerStage()
|
||||
{
|
||||
return 1000d / getStages().size();
|
||||
}
|
||||
|
||||
public int getActionsLeftInStage()
|
||||
{
|
||||
int progress = getProgressAmount();
|
||||
double progressPerStage = getProgressPerStage();
|
||||
double progressTillNext = progressPerStage - progress % progressPerStage;
|
||||
|
||||
Stage current = getCurrentStage();
|
||||
// Each Smith's Outfit piece adds 20% chance to increase action progress by 1, or 100% for all 4 pieces.
|
||||
double smithsOutfitBonus = smithsOutfitPieces == 4 ? 1 : 0.2 * smithsOutfitPieces;
|
||||
return (int) Math.ceil(progressTillNext / (current.getProgressPerAction() + smithsOutfitBonus));
|
||||
}
|
||||
|
||||
public int[] getCurrentHeatRange()
|
||||
{
|
||||
switch (getCurrentStage())
|
||||
{
|
||||
case POLISHING_WHEEL:
|
||||
return getLowHeatRange();
|
||||
case GRINDSTONE:
|
||||
return getMedHeatRange();
|
||||
case TRIP_HAMMER:
|
||||
return getHighHeatRange();
|
||||
default:
|
||||
return new int[]{0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of current stage actions that can be
|
||||
* performed before the heat drops too high or too low to
|
||||
* continue
|
||||
*/
|
||||
public int getActionsForHeatLevel()
|
||||
{
|
||||
Heat heatStage = getCurrentHeat();
|
||||
Stage stage = getCurrentStage();
|
||||
if (heatStage != stage.getHeat())
|
||||
{
|
||||
// not the right heat to start with
|
||||
return 0;
|
||||
}
|
||||
|
||||
int[] range = getCurrentHeatRange();
|
||||
int actions = 0;
|
||||
int heat = getHeatAmount();
|
||||
while (heat >= range[0] && heat <= range[1])
|
||||
{
|
||||
actions++;
|
||||
heat += stage.getHeatChange();
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
||||
public boolean isPlayerRunning()
|
||||
{
|
||||
return client.getVarpValue(173) == 1;
|
||||
}
|
||||
}
|
||||
460
src/main/java/ee/futur/easygiantsfoundry/FoundryOverlay2D.java
Normal file
460
src/main/java/ee/futur/easygiantsfoundry/FoundryOverlay2D.java
Normal file
@@ -0,0 +1,460 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.Heat;
|
||||
import ee.futur.easygiantsfoundry.enums.MetalBarType;
|
||||
import ee.futur.easygiantsfoundry.enums.Stage;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
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;
|
||||
|
||||
@Singleton
|
||||
public class FoundryOverlay2D extends OverlayPanel
|
||||
{
|
||||
private static final int REGION_ID = 13491;
|
||||
private static final int PANEL_WIDTH = 240;
|
||||
|
||||
private final Client client;
|
||||
private final EasyGiantsFoundryPlugin plugin;
|
||||
private final EasyGiantsFoundryState state;
|
||||
private final MetalBarCounter metalBarCounter;
|
||||
private final EasyGiantsFoundryConfig config;
|
||||
|
||||
@Inject
|
||||
private FoundryOverlay2D(
|
||||
Client client,
|
||||
EasyGiantsFoundryPlugin plugin,
|
||||
EasyGiantsFoundryState state,
|
||||
MetalBarCounter metalBarCounter,
|
||||
EasyGiantsFoundryConfig config)
|
||||
{
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.state = state;
|
||||
this.metalBarCounter = metalBarCounter;
|
||||
this.config = config;
|
||||
setPosition(OverlayPosition.BOTTOM_LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!config.alwaysDrawInfoPanel() && client.getLocalPlayer().getWorldLocation().getRegionID() != REGION_ID)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
panelComponent.getChildren().clear();
|
||||
panelComponent.setPreferredSize(new Dimension(PANEL_WIDTH, 0));
|
||||
|
||||
boolean hasContent = false;
|
||||
if (config.drawTitle())
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Easy Giants' Foundry").build());
|
||||
hasContent = true;
|
||||
}
|
||||
|
||||
boolean swordPickedUp = state.isEnabled() && state.getCurrentStage() != null;
|
||||
if (swordPickedUp)
|
||||
{
|
||||
hasContent = appendSection(buildStatusLines(), hasContent);
|
||||
hasContent = appendSection(buildProgressLines(), hasContent);
|
||||
hasContent = appendSection(buildHeatPlannerLines(), hasContent);
|
||||
}
|
||||
|
||||
hasContent = appendSection(buildReputationLines(), hasContent);
|
||||
hasContent = appendSection(buildMetalLines(), hasContent);
|
||||
|
||||
if (!hasContent)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
private boolean appendSection(List<LineComponent> lines, boolean hasPreviousContent)
|
||||
{
|
||||
if (lines.isEmpty())
|
||||
{
|
||||
return hasPreviousContent;
|
||||
}
|
||||
|
||||
if (hasPreviousContent)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left(" ").right("").build());
|
||||
}
|
||||
|
||||
panelComponent.getChildren().addAll(lines);
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<LineComponent> buildStatusLines()
|
||||
{
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
Heat heat = state.getCurrentHeat();
|
||||
Stage stage = state.getCurrentStage();
|
||||
|
||||
if (config.drawHeatInfo())
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Heat")
|
||||
.right(heat.getName() + " (" + state.getHeatAmount() / 10 + "%)")
|
||||
.rightColor(heat.getColor())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
if (config.drawStageInfo())
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Stage")
|
||||
.right(stage.getName() + " (" + state.getProgressAmount() / 10 + "%)")
|
||||
.rightColor(stage.getHeat().getColor())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private List<LineComponent> buildProgressLines()
|
||||
{
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
int actionsLeft = state.getActionsLeftInStage();
|
||||
int heatLeft = state.getActionsForHeatLevel();
|
||||
|
||||
if (config.drawActionsLeft())
|
||||
{
|
||||
lines.add(LineComponent.builder().left("Actions left").right(Integer.toString(actionsLeft)).build());
|
||||
}
|
||||
|
||||
if (config.drawHeatLeft())
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Heat left")
|
||||
.right(Integer.toString(heatLeft))
|
||||
.rightColor(getHeatColor(actionsLeft, heatLeft))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
if (config.drawBonusActions())
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Bonus actions")
|
||||
.right(state.getBonusActionsReceived() + "/" + state.getBonusActionsExpected())
|
||||
.rightColor(getBonusActionsColor(state.getBonusActionsReceived(), state.getBonusActionsExpected()))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private List<LineComponent> buildHeatPlannerLines()
|
||||
{
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
if (!config.drawHeatDelta() && !config.drawHeatPlan() && !config.drawHeatActionTimer())
|
||||
{
|
||||
return lines;
|
||||
}
|
||||
|
||||
if (state.getCurrentStage() == null)
|
||||
{
|
||||
return lines;
|
||||
}
|
||||
|
||||
int heatChangeNeeded = state.getHeatChangeNeeded();
|
||||
|
||||
if (config.drawHeatDelta())
|
||||
{
|
||||
lines.add(buildHeatDeltaLine(heatChangeNeeded));
|
||||
}
|
||||
|
||||
if (config.drawHeatPlan())
|
||||
{
|
||||
lines.addAll(buildHeatPlanLines(heatChangeNeeded));
|
||||
}
|
||||
|
||||
if (config.drawHeatActionTimer())
|
||||
{
|
||||
LineComponent timerLine = buildHeatActionTimerLine();
|
||||
if (timerLine != null)
|
||||
{
|
||||
lines.add(timerLine);
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private LineComponent buildHeatDeltaLine(int heatChangeNeeded)
|
||||
{
|
||||
String location;
|
||||
if (heatChangeNeeded > 0)
|
||||
{
|
||||
location = "Lava";
|
||||
}
|
||||
else if (heatChangeNeeded < 0)
|
||||
{
|
||||
location = "Waterfall";
|
||||
}
|
||||
else
|
||||
{
|
||||
location = "In range";
|
||||
}
|
||||
|
||||
String deltaText = heatChangeNeeded == 0
|
||||
? "0 (in range)"
|
||||
: String.format("%+d (%s)", heatChangeNeeded, location);
|
||||
|
||||
Color color = heatChangeNeeded == 0 ? ColorScheme.PROGRESS_COMPLETE_COLOR : config.lavaWaterfallColour();
|
||||
|
||||
return LineComponent.builder()
|
||||
.left("Heat Δ")
|
||||
.right(deltaText)
|
||||
.rightColor(color)
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<LineComponent> buildHeatPlanLines(int heatChangeNeeded)
|
||||
{
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
|
||||
if (heatChangeNeeded == 0)
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Heat plan")
|
||||
.right("Hold")
|
||||
.rightColor(ColorScheme.PROGRESS_COMPLETE_COLOR)
|
||||
.build()
|
||||
);
|
||||
return lines;
|
||||
}
|
||||
|
||||
boolean isHeating = heatChangeNeeded > 0;
|
||||
HeatActionSolver.DurationResult fast = solveHeatAction(true, isHeating);
|
||||
HeatActionSolver.DurationResult slow = solveHeatAction(false, isHeating);
|
||||
boolean added = false;
|
||||
|
||||
if (fast != null && fast.getDuration() > 0)
|
||||
{
|
||||
lines.add(buildHeatPlanLine("Fast", isHeating, true, fast));
|
||||
added = true;
|
||||
}
|
||||
|
||||
if (slow != null && slow.getDuration() > 0)
|
||||
{
|
||||
lines.add(buildHeatPlanLine("Slow", isHeating, false, slow));
|
||||
added = true;
|
||||
}
|
||||
|
||||
if (!added)
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Heat plan")
|
||||
.right("Hold")
|
||||
.rightColor(ColorScheme.PROGRESS_COMPLETE_COLOR)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private LineComponent buildHeatPlanLine(String label, boolean isHeating, boolean fast, HeatActionSolver.DurationResult result)
|
||||
{
|
||||
String actionName = getActionName(isHeating, fast);
|
||||
String value = String.format("%s %dt → %s%s",
|
||||
actionName,
|
||||
result.getDuration(),
|
||||
formatPredictedHeat(result.getPredictedHeat()),
|
||||
formatPlanStatus(result));
|
||||
|
||||
return LineComponent.builder()
|
||||
.left(label)
|
||||
.right(value)
|
||||
.rightColor(config.lavaWaterfallColour())
|
||||
.build();
|
||||
}
|
||||
|
||||
private HeatActionSolver.DurationResult solveHeatAction(boolean fast, boolean isHeating)
|
||||
{
|
||||
Stage stage = state.getCurrentStage();
|
||||
if (stage == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return HeatActionSolver.solve(
|
||||
stage,
|
||||
state.getCurrentHeatRange(),
|
||||
state.getActionsLeftInStage(),
|
||||
state.getHeatAmount(),
|
||||
fast,
|
||||
isHeating,
|
||||
config.heatActionPadTicks(),
|
||||
state.isPlayerRunning()
|
||||
);
|
||||
}
|
||||
|
||||
private LineComponent buildHeatActionTimerLine()
|
||||
{
|
||||
HeatActionStateMachine machine = state.heatActionStateMachine;
|
||||
if (machine.isIdle() || machine.getActionname() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return LineComponent.builder()
|
||||
.left("Action")
|
||||
.right(String.format(
|
||||
"%s %dt → %s%s",
|
||||
prettifyActionName(machine.getActionname()),
|
||||
machine.getRemainingDuration(),
|
||||
formatPredictedHeat(machine.getPredictedHeat()),
|
||||
machine.isGoalInRange() ? "" : machine.isOverShooting() ? " (overshoot)" : " (limit)"))
|
||||
.rightColor(config.lavaWaterfallColour())
|
||||
.build();
|
||||
}
|
||||
|
||||
private String formatPredictedHeat(int predictedHeat)
|
||||
{
|
||||
return heatPercent(predictedHeat) + "%";
|
||||
}
|
||||
|
||||
private String formatPlanStatus(HeatActionSolver.DurationResult result)
|
||||
{
|
||||
if (result == null || result.isGoalInRange())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return result.isOvershooting() ? " (overshoot)" : " (limit)";
|
||||
}
|
||||
|
||||
private String getActionName(boolean isHeating, boolean fast)
|
||||
{
|
||||
if (isHeating)
|
||||
{
|
||||
return fast ? "Dunk" : "Heat";
|
||||
}
|
||||
return fast ? "Quench" : "Cool";
|
||||
}
|
||||
|
||||
private String prettifyActionName(String action)
|
||||
{
|
||||
if (action == null || action.isEmpty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case "dunks":
|
||||
return "Dunk";
|
||||
case "heats":
|
||||
return "Heat";
|
||||
case "cools":
|
||||
return "Cool";
|
||||
case "quenches":
|
||||
return "Quench";
|
||||
default:
|
||||
return Character.toUpperCase(action.charAt(0)) + action.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
private int heatPercent(int heat)
|
||||
{
|
||||
return Math.max(0, Math.min(1000, heat)) / 10;
|
||||
}
|
||||
|
||||
private List<LineComponent> buildReputationLines()
|
||||
{
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
if (!config.drawShopPoints())
|
||||
{
|
||||
return lines;
|
||||
}
|
||||
|
||||
lines.add(LineComponent.builder().left("Reputation").right(Integer.toString(plugin.getReputation())).build());
|
||||
return lines;
|
||||
}
|
||||
|
||||
private List<LineComponent> buildMetalLines()
|
||||
{
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
if (!config.drawMetals())
|
||||
{
|
||||
return lines;
|
||||
}
|
||||
|
||||
if (!metalBarCounter.isSeenBank())
|
||||
{
|
||||
lines.add(
|
||||
LineComponent.builder()
|
||||
.left("Metals: open bank")
|
||||
.leftColor(Color.RED)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
addMetalCount(lines, "Bronze bars:", metalBarCounter.get(MetalBarType.BRONZE));
|
||||
addMetalCount(lines, "Iron bars:", metalBarCounter.get(MetalBarType.IRON));
|
||||
addMetalCount(lines, "Steel bars:", metalBarCounter.get(MetalBarType.STEEL));
|
||||
addMetalCount(lines, "Mithril bars:", metalBarCounter.get(MetalBarType.MITHRIL));
|
||||
addMetalCount(lines, "Adamant bars:", metalBarCounter.get(MetalBarType.ADAMANT));
|
||||
addMetalCount(lines, "Runite bars:", metalBarCounter.get(MetalBarType.RUNITE));
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private void addMetalCount(List<LineComponent> lines, String displayName, int count)
|
||||
{
|
||||
if (count > 0 || config.drawAllMetals())
|
||||
{
|
||||
lines.add(LineComponent.builder().left(displayName).right(Integer.toString(count)).build());
|
||||
}
|
||||
}
|
||||
|
||||
private Color getHeatColor(int actions, int heat)
|
||||
{
|
||||
if (heat >= actions)
|
||||
{
|
||||
return ColorScheme.PROGRESS_COMPLETE_COLOR;
|
||||
}
|
||||
|
||||
if (heat > 0)
|
||||
{
|
||||
return ColorScheme.PROGRESS_INPROGRESS_COLOR;
|
||||
}
|
||||
|
||||
return ColorScheme.PROGRESS_ERROR_COLOR;
|
||||
}
|
||||
|
||||
private Color getBonusActionsColor(int received, int expected)
|
||||
{
|
||||
if (received >= expected)
|
||||
{
|
||||
return ColorScheme.PROGRESS_COMPLETE_COLOR;
|
||||
}
|
||||
|
||||
return ColorScheme.PROGRESS_INPROGRESS_COLOR;
|
||||
}
|
||||
}
|
||||
617
src/main/java/ee/futur/easygiantsfoundry/FoundryOverlay3D.java
Normal file
617
src/main/java/ee/futur/easygiantsfoundry/FoundryOverlay3D.java
Normal file
@@ -0,0 +1,617 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import static ee.futur.easygiantsfoundry.EasyGiantsFoundryClientIDs.*;
|
||||
import static ee.futur.easygiantsfoundry.EasyGiantsFoundryHelper.getHeatColor;
|
||||
import static ee.futur.easygiantsfoundry.EasyGiantsFoundryHelper.renderTextLocation;
|
||||
import static ee.futur.easygiantsfoundry.MouldHelper.SWORD_TYPE_1_VARBIT;
|
||||
import static ee.futur.easygiantsfoundry.MouldHelper.SWORD_TYPE_2_VARBIT;
|
||||
import ee.futur.easygiantsfoundry.enums.CommissionType;
|
||||
import ee.futur.easygiantsfoundry.enums.FontType;
|
||||
import ee.futur.easygiantsfoundry.enums.Heat;
|
||||
import ee.futur.easygiantsfoundry.enums.Stage;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.outline.ModelOutlineRenderer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class FoundryOverlay3D extends Overlay
|
||||
{
|
||||
|
||||
private static final int HAND_IN_WIDGET = 49414221;
|
||||
private static final int CRUCIBLE_CAPACITY = 28;
|
||||
private final ModelOutlineRenderer modelOutlineRenderer;
|
||||
|
||||
GameObject tripHammer;
|
||||
GameObject grindstone;
|
||||
GameObject polishingWheel;
|
||||
GameObject lavaPool;
|
||||
GameObject waterfall;
|
||||
GameObject mouldJig;
|
||||
GameObject crucible;
|
||||
GameObject storage;
|
||||
NPC kovac;
|
||||
|
||||
private final Client client;
|
||||
private final EasyGiantsFoundryState state;
|
||||
private final EasyGiantsFoundryConfig config;
|
||||
|
||||
@Inject
|
||||
private FoundryOverlay3D(
|
||||
Client client,
|
||||
EasyGiantsFoundryState state,
|
||||
EasyGiantsFoundryConfig config,
|
||||
ModelOutlineRenderer modelOutlineRenderer)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
this.client = client;
|
||||
this.state = state;
|
||||
this.config = config;
|
||||
this.modelOutlineRenderer = modelOutlineRenderer;
|
||||
}
|
||||
|
||||
private Color getObjectColor(Stage stage, Heat heat)
|
||||
{
|
||||
if (stage.getHeat() != heat)
|
||||
{
|
||||
return config.toolBad();
|
||||
}
|
||||
|
||||
if (BonusWidget.isActive(client))
|
||||
{
|
||||
return config.toolBonus();
|
||||
}
|
||||
|
||||
int actionsLeft = state.getActionsLeftInStage();
|
||||
int heatLeft = state.getActionsForHeatLevel();
|
||||
if (actionsLeft <= 1 || heatLeft <= 1)
|
||||
{
|
||||
return config.toolCaution();
|
||||
}
|
||||
|
||||
return config.toolGood();
|
||||
}
|
||||
|
||||
private GameObject getStageObject(Stage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case TRIP_HAMMER:
|
||||
return tripHammer;
|
||||
case GRINDSTONE:
|
||||
return grindstone;
|
||||
case POLISHING_WHEEL:
|
||||
return polishingWheel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!state.isEnabled())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.dynamicOverlayFont() != FontType.DEFAULT)
|
||||
{
|
||||
graphics.setFont(config.dynamicOverlayFont().getFont());
|
||||
}
|
||||
|
||||
if (config.highlightKovac())
|
||||
{
|
||||
drawKovacIfHandIn(graphics);
|
||||
}
|
||||
|
||||
if (client.getVarbitValue(VARBIT_PREFORM_STORED) == 1)
|
||||
{
|
||||
if (config.highlightStorage())
|
||||
{
|
||||
drawStorage(graphics);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (state.getCurrentStage() == null)
|
||||
{
|
||||
if (config.highlightMould())
|
||||
{
|
||||
drawMouldIfNotSet(graphics);
|
||||
}
|
||||
if (config.highlightCrucible())
|
||||
{
|
||||
drawCrucibleIfMouldSet(graphics);
|
||||
}
|
||||
if (config.drawMouldInfoOverlay())
|
||||
{
|
||||
drawMouldScoreIfMouldSet(graphics);
|
||||
drawPreformScoreIfPoured(graphics);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Stage stage = state.getCurrentStage();
|
||||
GameObject stageObject = getStageObject(stage);
|
||||
if (stageObject == null || graphics == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Heat heat = state.getCurrentHeat();
|
||||
Color color = getObjectColor(stage, heat);
|
||||
// TODO Config
|
||||
if (config.highlightStyle() == HighlightStyle.HIGHLIGHT_CLICKBOX)
|
||||
{
|
||||
drawObjectClickbox(graphics, stageObject, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawObjectOutline(graphics, stageObject, color);
|
||||
}
|
||||
|
||||
// !state.heatingCoolingState.isIdle()
|
||||
// if the stage heat is already in range, but player still wants to do heat changes
|
||||
if ((stage.getHeat() != heat || !state.heatActionStateMachine.isIdle()) && config.highlightWaterAndLava())
|
||||
{
|
||||
drawHeatChangers(graphics);
|
||||
}
|
||||
// mouse hover over preview
|
||||
else if (config.drawLavaWaterInfoOverlay())
|
||||
{
|
||||
MenuEntry[] menuEntries = client.getMenuEntries();
|
||||
if (menuEntries.length != 0)
|
||||
{
|
||||
MenuEntry hoveredMenu = menuEntries[menuEntries.length - 1];
|
||||
|
||||
if (hoveredMenu.getIdentifier() == lavaPool.getId())
|
||||
{
|
||||
drawHeatChangerPreviewOverlay(graphics, lavaPool, true);
|
||||
}
|
||||
else if (hoveredMenu.getIdentifier() == waterfall.getId())
|
||||
{
|
||||
drawHeatChangerPreviewOverlay(graphics, waterfall, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawActionOverlay(graphics, stageObject);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void drawObjectClickbox(Graphics2D graphics, GameObject stageObject, Color color)
|
||||
{
|
||||
Shape objectClickbox = stageObject.getClickbox();
|
||||
if (objectClickbox != null && config.highlightTools())
|
||||
{
|
||||
objectClickbox = scaleShape(objectClickbox);
|
||||
Point mousePosition = client.getMouseCanvasPosition();
|
||||
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
||||
{
|
||||
graphics.setColor(color.darker());
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.setColor(color);
|
||||
}
|
||||
graphics.draw(objectClickbox);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(objectClickbox);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawObjectOutline(Graphics2D graphics, GameObject stageObject, Color color)
|
||||
{
|
||||
Color _color = new Color(color.getRed(), color.getGreen(), color.getBlue(), config.borderAlpha());
|
||||
modelOutlineRenderer.drawOutline(stageObject, config.borderThickness(), _color, config.borderFeather());
|
||||
}
|
||||
|
||||
private void drawHeatChangerPreviewOverlay(
|
||||
Graphics2D graphics,
|
||||
GameObject stageObject,
|
||||
boolean isLava
|
||||
)
|
||||
{
|
||||
|
||||
HeatActionSolver.DurationResult fastResult =
|
||||
HeatActionSolver.solve(
|
||||
state.getCurrentStage(),
|
||||
state.getCurrentHeatRange(),
|
||||
state.getActionsLeftInStage(),
|
||||
state.getHeatAmount(),
|
||||
true,
|
||||
isLava,
|
||||
config.heatActionPadTicks(),
|
||||
state.isPlayerRunning()
|
||||
);
|
||||
|
||||
final int fastDuration = fastResult.getDuration();
|
||||
HeatActionSolver.DurationResult slowResult =
|
||||
HeatActionSolver.solve(
|
||||
state.getCurrentStage(),
|
||||
state.getCurrentHeatRange(),
|
||||
state.getActionsLeftInStage(),
|
||||
state.getHeatAmount(),
|
||||
false,
|
||||
isLava,
|
||||
config.heatActionPadTicks(),
|
||||
state.isPlayerRunning()
|
||||
);
|
||||
final int slowDuration = slowResult.getDuration();
|
||||
|
||||
final String fastName = isLava ? "dunks" : "quenches";
|
||||
final String slowName = isLava ? "heats" : "cools";
|
||||
|
||||
String text;
|
||||
if (config.debugging())
|
||||
{
|
||||
text = String.format("%d %s (predicted: %d) or %d %s (predicted: %d) (overshoot: %s goal-in-range: %s)",
|
||||
fastDuration, fastName, fastResult.getPredictedHeat(), slowDuration, slowName, slowResult.getPredictedHeat(), slowResult.isOvershooting(), fastResult.isGoalInRange());
|
||||
}
|
||||
else
|
||||
{
|
||||
text = String.format("%d %s or %d %s ",
|
||||
fastDuration, fastName, slowDuration, slowName);
|
||||
}
|
||||
|
||||
LocalPoint stageLoc = stageObject.getLocalLocation();
|
||||
stageLoc = new LocalPoint(stageLoc.getX(), stageLoc.getY());
|
||||
|
||||
Point pos = Perspective.getCanvasTextLocation(client, graphics, stageLoc, text, 50);
|
||||
if (pos == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = config.lavaWaterfallColour();
|
||||
|
||||
renderTextLocation(graphics, pos, text, color, config.textBackground(), config.textOutline());
|
||||
}
|
||||
|
||||
private void drawHeatChangerOverlay(Graphics2D graphics, GameObject stageObject)
|
||||
{
|
||||
|
||||
String text;
|
||||
if (config.debugging())
|
||||
{
|
||||
text = String.format("%d %s (overshoot: %s) [goal-in-range: %s]",
|
||||
state.heatActionStateMachine.getRemainingDuration(),
|
||||
state.heatActionStateMachine.getActionname(),
|
||||
state.heatActionStateMachine.isOverShooting(),
|
||||
state.heatActionStateMachine.isGoalInRange()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = String.format("%d %s",
|
||||
state.heatActionStateMachine.getRemainingDuration(),
|
||||
state.heatActionStateMachine.getActionname()
|
||||
);
|
||||
}
|
||||
|
||||
LocalPoint stageLoc = stageObject.getLocalLocation();
|
||||
stageLoc = new LocalPoint(stageLoc.getX(), stageLoc.getY());
|
||||
|
||||
Point pos = Perspective.getCanvasTextLocation(client, graphics, stageLoc, text, 50);
|
||||
Color color = config.lavaWaterfallColour();
|
||||
|
||||
renderTextLocation(graphics, pos, text, color, config.textBackground(), config.textOutline());
|
||||
}
|
||||
|
||||
private void drawHeatChangers(Graphics2D graphics)
|
||||
{
|
||||
int change = state.getHeatChangeNeeded();
|
||||
Shape shape = null;
|
||||
|
||||
boolean isLava = change > 0;
|
||||
boolean isWaterfall = change < 0;
|
||||
if (isWaterfall || state.heatActionStateMachine.isCooling())
|
||||
{
|
||||
shape = waterfall.getClickbox();
|
||||
}
|
||||
else if (isLava || state.heatActionStateMachine.isHeating())
|
||||
{
|
||||
shape = lavaPool.getClickbox();
|
||||
}
|
||||
if (shape != null)
|
||||
{
|
||||
shape = scaleShape(shape);
|
||||
Point mousePosition = client.getMouseCanvasPosition();
|
||||
Color color = config.lavaWaterfallColour();
|
||||
if (shape.contains(mousePosition.getX(), mousePosition.getY()))
|
||||
{
|
||||
graphics.setColor(color.darker());
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.setColor(color);
|
||||
}
|
||||
graphics.draw(shape);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(shape);
|
||||
}
|
||||
|
||||
if (config.drawLavaWaterInfoOverlay())
|
||||
{
|
||||
if (state.heatActionStateMachine.isCooling())
|
||||
{
|
||||
drawHeatChangerOverlay(graphics, waterfall);
|
||||
}
|
||||
else if (isWaterfall)
|
||||
{
|
||||
drawHeatChangerPreviewOverlay(graphics, waterfall, false);
|
||||
}
|
||||
if (state.heatActionStateMachine.isHeating())
|
||||
{
|
||||
drawHeatChangerOverlay(graphics, lavaPool);
|
||||
}
|
||||
else if (isLava)
|
||||
{
|
||||
drawHeatChangerPreviewOverlay(graphics, lavaPool, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void drawCrucibleContent(Graphics2D graphics)
|
||||
{
|
||||
if (!config.drawCrucibleInfoOverlay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
String text = String.format("%d/%d score: %d", state.getCrucibleCount(), CRUCIBLE_CAPACITY, (int)state.getCrucibleScore());
|
||||
|
||||
LocalPoint crucibleLoc = crucible.getLocalLocation();
|
||||
crucibleLoc = new LocalPoint(crucibleLoc.getX() - 100, crucibleLoc.getY());
|
||||
|
||||
Point pos = Perspective.getCanvasTextLocation(client, graphics, crucibleLoc, text, 200);
|
||||
Color color;
|
||||
if (state.getCrucibleCount() == CRUCIBLE_CAPACITY)
|
||||
{
|
||||
color = config.toolGood();
|
||||
}
|
||||
else
|
||||
{
|
||||
color = config.generalHighlight();
|
||||
}
|
||||
renderTextLocation(graphics, pos, text, color, config.textBackground(), config.textOutline());
|
||||
}
|
||||
|
||||
private void drawMouldScoreIfMouldSet(Graphics2D graphics) {
|
||||
if (client.getVarbitValue(SWORD_TYPE_1_VARBIT) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (client.getVarbitValue(VARBIT_GAME_STAGE) != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.getMouldScore() < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String text = String.format("score: %d", state.getMouldScore());
|
||||
LocalPoint mouldLoc = mouldJig.getLocalLocation();
|
||||
Point pos = Perspective.getCanvasTextLocation(client, graphics, mouldLoc, text, 115);
|
||||
Color color = config.generalHighlight();
|
||||
|
||||
renderTextLocation(graphics, pos, text, color, config.textBackground(), config.textOutline());
|
||||
}
|
||||
private void drawPreformScoreIfPoured(Graphics2D graphics) {
|
||||
if (client.getVarbitValue(VARBIT_GAME_STAGE) != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.getMouldScore() < 0 || state.getLastKnownCrucibleScore() < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int preformScore = state.getLastKnownCrucibleScore() + state.getMouldScore();
|
||||
String text = String.format("score: %d", preformScore);
|
||||
LocalPoint mouldLoc = mouldJig.getLocalLocation();
|
||||
Point pos = Perspective.getCanvasTextLocation(client, graphics, mouldLoc, text, 115);
|
||||
|
||||
Color color = config.generalHighlight();
|
||||
|
||||
renderTextLocation(graphics, pos, text, color, config.textBackground(), config.textOutline());
|
||||
}
|
||||
|
||||
private void drawCrucibleIfMouldSet(Graphics2D graphics)
|
||||
{
|
||||
if (client.getVarbitValue(SWORD_TYPE_1_VARBIT) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (client.getVarbitValue(VARBIT_GAME_STAGE) != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.highlightStyle() == HighlightStyle.HIGHLIGHT_CLICKBOX)
|
||||
{
|
||||
Shape shape = crucible.getConvexHull();
|
||||
if (shape != null)
|
||||
{
|
||||
shape = scaleShape(shape);
|
||||
Color color = config.generalHighlight();
|
||||
if (state.getCrucibleCount() == CRUCIBLE_CAPACITY)
|
||||
{
|
||||
graphics.setColor(config.toolGood());
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.setColor(config.generalHighlight());
|
||||
}
|
||||
graphics.draw(shape);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(shape);
|
||||
}
|
||||
}
|
||||
else if (config.highlightStyle() == HighlightStyle.HIGHLIGHT_BORDER)
|
||||
{
|
||||
Color color;
|
||||
if (state.getCrucibleCount() == CRUCIBLE_CAPACITY)
|
||||
{
|
||||
color = config.toolGood();
|
||||
}
|
||||
else
|
||||
{
|
||||
color = config.generalHighlight();
|
||||
}
|
||||
drawObjectOutline(graphics, crucible, color);
|
||||
}
|
||||
|
||||
drawCrucibleContent(graphics);
|
||||
}
|
||||
|
||||
private void drawMouldIfNotSet(Graphics2D graphics)
|
||||
{
|
||||
if (client.getWidget(WIDGET_PROGRESS_PARENT) != null
|
||||
|| client.getVarbitValue(SWORD_TYPE_1_VARBIT) == 0
|
||||
|| (client.getVarbitValue(VARBIT_GAME_STAGE) != 0
|
||||
&& client.getVarbitValue(VARBIT_GAME_STAGE) != 2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (config.highlightStyle() == HighlightStyle.HIGHLIGHT_CLICKBOX)
|
||||
{
|
||||
Shape shape = mouldJig.getConvexHull();
|
||||
if (shape != null)
|
||||
{
|
||||
shape = scaleShape(shape);
|
||||
Color color = config.generalHighlight();
|
||||
graphics.setColor(color);
|
||||
graphics.draw(shape);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(shape);
|
||||
}
|
||||
}
|
||||
else if (config.highlightStyle() == HighlightStyle.HIGHLIGHT_BORDER)
|
||||
{
|
||||
drawObjectOutline(graphics, mouldJig, config.generalHighlight());
|
||||
}
|
||||
|
||||
if (config.drawMouldInfoOverlay())
|
||||
{
|
||||
CommissionType type1 = CommissionType.forVarbit(client.getVarbitValue(SWORD_TYPE_1_VARBIT));
|
||||
CommissionType type2 = CommissionType.forVarbit(client.getVarbitValue(SWORD_TYPE_2_VARBIT));
|
||||
String text = StringUtils.capitalize(type1.toString().toLowerCase()) + " " + StringUtils.capitalize(type2.toString().toLowerCase());
|
||||
LocalPoint textLocation = mouldJig.getLocalLocation();
|
||||
textLocation = new LocalPoint(textLocation.getX(), textLocation.getY());
|
||||
Point canvasLocation = Perspective.getCanvasTextLocation(client, graphics, textLocation, text, 100);
|
||||
canvasLocation = new Point(canvasLocation.getX(), canvasLocation.getY() + 10);
|
||||
renderTextLocation(graphics, canvasLocation, text, config.generalHighlight(), config.textBackground(), config.textOutline());
|
||||
}
|
||||
}
|
||||
|
||||
private Shape scaleShape(Shape shape)
|
||||
{
|
||||
if (shape == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
double scale = config.clickboxScale() / 100.0;
|
||||
if (Math.abs(scale - 1.0) < 0.01)
|
||||
{
|
||||
return shape;
|
||||
}
|
||||
|
||||
Rectangle2D bounds = shape.getBounds2D();
|
||||
AffineTransform transform = new AffineTransform();
|
||||
transform.translate(bounds.getCenterX(), bounds.getCenterY());
|
||||
transform.scale(scale, scale);
|
||||
transform.translate(-bounds.getCenterX(), -bounds.getCenterY());
|
||||
return transform.createTransformedShape(shape);
|
||||
}
|
||||
|
||||
private void drawStorage(Graphics2D graphics)
|
||||
{
|
||||
Shape shape = storage.getConvexHull();
|
||||
if (shape != null)
|
||||
{
|
||||
shape = scaleShape(shape);
|
||||
Color color = config.generalHighlight();
|
||||
graphics.setColor(color);
|
||||
graphics.draw(shape);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(shape);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawKovacIfHandIn(Graphics2D graphics)
|
||||
{
|
||||
Widget handInWidget = client.getWidget(HAND_IN_WIDGET);
|
||||
if (handInWidget != null && !handInWidget.isHidden())
|
||||
{
|
||||
Shape shape = kovac.getConvexHull();
|
||||
if (shape != null)
|
||||
{
|
||||
shape = scaleShape(shape);
|
||||
Color color = config.generalHighlight();
|
||||
graphics.setColor(color);
|
||||
graphics.draw(shape);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawActionOverlay(Graphics2D graphics, GameObject gameObject)
|
||||
{
|
||||
|
||||
|
||||
int actionsLeft = state.getActionsLeftInStage();
|
||||
int heatLeft = state.getActionsForHeatLevel();
|
||||
|
||||
// Draw heat left
|
||||
if (config.drawHeatLeftOverlay())
|
||||
{
|
||||
String text = "Heat left: " + heatLeft;
|
||||
LocalPoint textLocation = gameObject.getLocalLocation();
|
||||
textLocation = new LocalPoint(textLocation.getX(), textLocation.getY());
|
||||
Point canvasLocation = Perspective.getCanvasTextLocation(client, graphics, textLocation, text, 250);
|
||||
if (canvasLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
renderTextLocation(graphics, canvasLocation, text, getHeatColor(actionsLeft, heatLeft), config.textBackground(), config.textOutline());
|
||||
}
|
||||
if (config.drawActionLeftOverlay())
|
||||
// Draw actions left
|
||||
{
|
||||
String text = "Actions left: " + actionsLeft;
|
||||
LocalPoint textLocation = gameObject.getLocalLocation();
|
||||
textLocation = new LocalPoint(textLocation.getX(), textLocation.getY());
|
||||
Point canvasLocation = Perspective.getCanvasTextLocation(client, graphics, textLocation, text, 250);
|
||||
if (canvasLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
canvasLocation = new Point(canvasLocation.getX(), canvasLocation.getY() + graphics.getFontMetrics().getHeight());
|
||||
renderTextLocation(graphics, canvasLocation, text, getHeatColor(actionsLeft, heatLeft), config.textBackground(), config.textOutline());
|
||||
}
|
||||
}
|
||||
}
|
||||
385
src/main/java/ee/futur/easygiantsfoundry/HeatActionSolver.java
Normal file
385
src/main/java/ee/futur/easygiantsfoundry/HeatActionSolver.java
Normal file
@@ -0,0 +1,385 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.Stage;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Solves the heating/cooling action and predicts tick duration (index)
|
||||
* the naming convention is focused on the algorithm rather than in-game terminology for the context.
|
||||
* <p>
|
||||
* the dx_n refers to successive derivatives of an ordinary-differential-equations
|
||||
* https://en.wikipedia.org/wiki/Ordinary_differential_equation
|
||||
* also known as position (dx0), speed (dx1), and acceleration (dx2).
|
||||
* <p>
|
||||
* dx0 - players current heat at tick
|
||||
* dx1 - dx0_current - dx0_last_tick, aka the first derivative
|
||||
* dx2 - dx1_current - dx1_last_tick, aka the second derivative
|
||||
* <p>
|
||||
* for context, here's what dx1 extracted directly from in-game dunking looks like.
|
||||
* the purpose of the HeatActionSolver.java is to accurately model this data.
|
||||
* int[] dx1 = {
|
||||
* 7,
|
||||
* 8,
|
||||
* 9,
|
||||
* 11,
|
||||
* 13,
|
||||
* 15,
|
||||
* 17,
|
||||
* 19,
|
||||
* 21,
|
||||
* 24,
|
||||
* 27, -- dunk/quench starts here
|
||||
* 30,
|
||||
* 33,
|
||||
* 37,
|
||||
* 41,
|
||||
* 45,
|
||||
* 49,
|
||||
* 53,
|
||||
* 57,
|
||||
* 62,
|
||||
* 67,
|
||||
* 72,
|
||||
* 77,
|
||||
* 83,
|
||||
* 89,
|
||||
* 95,
|
||||
* 91,
|
||||
* };
|
||||
*/
|
||||
|
||||
/* The following code-snippet can be copy-pasted into runelite developer-tools "Shell" to extract dx1
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
int HEAT_ID = 13948;
|
||||
|
||||
AtomicInteger tickCounter = new AtomicInteger(-1);
|
||||
AtomicInteger prevHeat = new AtomicInteger(client.getVarbitValue(HEAT_ID));
|
||||
|
||||
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
|
||||
String output = "";
|
||||
|
||||
subscribe(VarbitChanged.class, ev ->
|
||||
{
|
||||
if (ev.getVarbitId() == HEAT_ID)
|
||||
{
|
||||
int deltaHeat = ev.getValue() - prevHeat.getAndSet(ev.getValue());
|
||||
|
||||
if (deltaHeat == -1) return; // ignore passive drain
|
||||
|
||||
String str = "[" + tickCounter.incrementAndGet()
|
||||
+ "] deltaHeat: " + deltaHeat;
|
||||
log.info(str);
|
||||
output = output + deltaHeat + "\n";
|
||||
StringSelection selection = new StringSelection(output);
|
||||
clipboard.setContents(selection, selection);
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
public class HeatActionSolver
|
||||
{
|
||||
|
||||
public static final int[] DX_1 = new int[]{
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
11,
|
||||
13,
|
||||
15,
|
||||
17,
|
||||
19,
|
||||
21,
|
||||
24,
|
||||
27, // -- dunk/quench starts here
|
||||
30,
|
||||
33,
|
||||
37,
|
||||
41,
|
||||
45,
|
||||
49,
|
||||
53,
|
||||
57,
|
||||
62,
|
||||
67,
|
||||
72,
|
||||
77,
|
||||
83,
|
||||
89,
|
||||
95,
|
||||
91, // last one will always overshoot 1000
|
||||
};
|
||||
|
||||
|
||||
// index is stage, ordinal order
|
||||
public static final int[] TOOL_TICK_CYCLE = new int[] {
|
||||
5,
|
||||
2,
|
||||
2
|
||||
};
|
||||
|
||||
public static final int MAX_INDEX = DX_1.length;
|
||||
public static final int FAST_INDEX = 10;
|
||||
|
||||
@Value(staticConstructor = "of")
|
||||
public static class SolveResult
|
||||
{
|
||||
int index;
|
||||
int dx0;
|
||||
int dx1;
|
||||
int dx2;
|
||||
}
|
||||
|
||||
private static SolveResult heatingSolve(int start, int goal, boolean overshoot, int max, boolean isFast)
|
||||
{
|
||||
return relativeSolve(goal - start, overshoot, max - start, isFast, -1);
|
||||
}
|
||||
|
||||
private static SolveResult coolingSolve(int start, int goal, boolean overshoot, int min, boolean isFast)
|
||||
{
|
||||
return relativeSolve(start - goal, overshoot, start - min, isFast, 1);
|
||||
}
|
||||
|
||||
private static SolveResult relativeSolve(int goal, boolean overshoot, int max, boolean isFast, int decayValue)
|
||||
{
|
||||
|
||||
int index = isFast ? FAST_INDEX : 0;
|
||||
int dx0 = 0;
|
||||
|
||||
boolean decay = false;
|
||||
|
||||
while (true) {
|
||||
|
||||
if (index > MAX_INDEX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!overshoot && dx0 + DX_1[index] > goal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (overshoot && dx0 >= goal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (dx0 + DX_1[index] >= max)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (decay)
|
||||
{
|
||||
dx0 -= decayValue;
|
||||
}
|
||||
|
||||
|
||||
dx0 += DX_1[index];
|
||||
++index;
|
||||
decay = !decay;
|
||||
}
|
||||
|
||||
if (isFast)
|
||||
{
|
||||
index -= FAST_INDEX;
|
||||
}
|
||||
|
||||
return SolveResult.of(index, dx0, DX_1[index], -1);
|
||||
}
|
||||
|
||||
|
||||
@Value(staticConstructor = "of")
|
||||
public static class DurationResult
|
||||
{
|
||||
int duration;
|
||||
boolean goalInRange;
|
||||
boolean overshooting;
|
||||
int predictedHeat;
|
||||
}
|
||||
|
||||
public static DurationResult solve(
|
||||
Stage stage,
|
||||
int[] range,
|
||||
int actionLeftInStage,
|
||||
int start,
|
||||
boolean isFast,
|
||||
boolean isActionHeating,
|
||||
int paddingTicks,
|
||||
boolean isRunning)
|
||||
{
|
||||
|
||||
final boolean isStageHeating = stage.isHeating();
|
||||
// adding tool cycle ticks because the first cycle at a tool is almost always nulled
|
||||
// (unless manually reaching the tile, then clicking the tool)
|
||||
final int toolDelay = TOOL_TICK_CYCLE[stage.ordinal()];
|
||||
final int travelTicks = solveTravelTicks(isRunning, stage, isActionHeating) + toolDelay;
|
||||
final int travelDecay = (int) Math.ceil((double) travelTicks / 2);
|
||||
|
||||
final int paddingDecay = (int) Math.ceil((double) paddingTicks / 2);
|
||||
|
||||
// adding 2.4s/8ticks worth of padding so preform doesn't decay out of range
|
||||
// average distance from lava+waterfall around 8 ticks
|
||||
// preform decays 1 heat every 2 ticks
|
||||
final int min = Math.max(0, Math.min(1000, range[0] + paddingDecay + travelDecay));
|
||||
final int max = Math.max(0, Math.min(1000, range[1] + paddingDecay + travelDecay));
|
||||
|
||||
final int actionsLeft_DeltaHeat = actionLeftInStage * stage.getHeatChange();
|
||||
|
||||
int estimatedDuration = 0;
|
||||
|
||||
final boolean goalInRange;
|
||||
boolean overshoot = false;
|
||||
|
||||
SolveResult result = null;
|
||||
|
||||
// case actions are all cooling, heating is mirrored version
|
||||
|
||||
// goal: in-range // stage: heating
|
||||
// overshoot goal
|
||||
// <----------|stop|<---------------- heat
|
||||
// ------|min|----------goal-------|max|
|
||||
// stage ---------------->
|
||||
|
||||
// goal: out-range // stage: heating
|
||||
// undershoot min
|
||||
// ...----------|stop|<--------------------- heat
|
||||
// -goal---|min|---------------------|max|
|
||||
// stage ----------------->
|
||||
|
||||
// goal: in-range // stage: cooling
|
||||
// undershoot goal
|
||||
// <-------------------------|stop|<--------------- heat
|
||||
// ------|min|----------goal-------|max|
|
||||
// <---------------- stage
|
||||
|
||||
// goal: out-range // stage: cooling
|
||||
// overshoot max
|
||||
// <--------------------|stop|<--------------- heat
|
||||
// --------|min|---------------------|max|----goal
|
||||
// <---------------- stage
|
||||
|
||||
if (isActionHeating)
|
||||
{
|
||||
int goal = min - actionsLeft_DeltaHeat;
|
||||
goalInRange = goal >= min && goal <= max;
|
||||
|
||||
if (isStageHeating)
|
||||
{
|
||||
|
||||
if (start <= max)
|
||||
{
|
||||
overshoot = !goalInRange;
|
||||
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = min;
|
||||
}
|
||||
|
||||
result = heatingSolve(start, goal, overshoot, max, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
else // cooling stage
|
||||
{
|
||||
// actionsLeft_DeltaHeat is negative here
|
||||
if (start <= max)
|
||||
{
|
||||
overshoot = goalInRange;
|
||||
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = max;
|
||||
}
|
||||
|
||||
result = heatingSolve(start, goal, overshoot, max, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // cooling action
|
||||
{
|
||||
int goal = max - actionsLeft_DeltaHeat;
|
||||
goalInRange = goal >= min && goal <= max;
|
||||
|
||||
if (isStageHeating)
|
||||
{
|
||||
if (start >= min)
|
||||
{
|
||||
overshoot = goalInRange;
|
||||
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = min;
|
||||
}
|
||||
|
||||
result = coolingSolve(start, goal, overshoot, min, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
else // cooling stage cooling action
|
||||
{
|
||||
if (start >= min)
|
||||
{
|
||||
overshoot = !goalInRange;
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = max;
|
||||
}
|
||||
|
||||
result = coolingSolve(start, goal, overshoot, min, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dx0 = result == null ? 0 : result.dx0;
|
||||
if (!isActionHeating)
|
||||
{
|
||||
dx0 *= -1;
|
||||
}
|
||||
|
||||
|
||||
return DurationResult.of(estimatedDuration, goalInRange, overshoot, start + dx0);
|
||||
}
|
||||
|
||||
private static int solveTravelTicks(boolean isRunning, Stage stage, boolean isLava)
|
||||
{
|
||||
final int distance;
|
||||
if (isLava)
|
||||
{
|
||||
distance = stage.getDistanceToLava();
|
||||
}
|
||||
else
|
||||
{
|
||||
distance = stage.getDistanceToWaterfall();
|
||||
}
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
// for odd distances, like 7
|
||||
// 7 / 2 = 3.5
|
||||
// rounded to 4
|
||||
return (int) Math.ceil((double) distance / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* A state-machine that keeps track of heating/cooling actions.
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
public class HeatActionStateMachine
|
||||
{
|
||||
/**
|
||||
* Tick counter for heating, -1 means not currently heating.
|
||||
*/
|
||||
int heatingTicks = -1;
|
||||
|
||||
/**
|
||||
* Tick counter for cooling, -1 means not currently cooling.
|
||||
*/
|
||||
int coolingTicks = -1;
|
||||
|
||||
boolean actionFast;
|
||||
|
||||
boolean actionHeating;
|
||||
|
||||
/**
|
||||
* The starting heat amount of the heating/cooling action.
|
||||
*/
|
||||
int startingHeat;
|
||||
|
||||
/**
|
||||
* The estimated tick duration of the heating/cooling action.
|
||||
*/
|
||||
int estimatedDuration;
|
||||
|
||||
/**
|
||||
* The goal heat amount of the heating/cooling action.
|
||||
*/
|
||||
int goalHeat = 0;
|
||||
|
||||
// debug
|
||||
boolean goalInRange;
|
||||
boolean isOverShooting;
|
||||
int predictedHeat;
|
||||
|
||||
/**
|
||||
* The last action the player clicked on. Used for ui overlay to display.
|
||||
* When null, the state-machine will stop() and reset.
|
||||
*/
|
||||
String actionname = null;
|
||||
|
||||
private EasyGiantsFoundryState state;
|
||||
private EasyGiantsFoundryConfig config;
|
||||
|
||||
/**
|
||||
* Start the state-machine with the given parameters.
|
||||
* <p>
|
||||
* These parameters have to be set-up manually before start().
|
||||
* Velocity, AccelerationBonus, ActionName
|
||||
*
|
||||
* @param state the current state of the foundry
|
||||
* @param config the current configuration of the plugin
|
||||
* @param startingHeat the starting heat amount
|
||||
* @see HeatActionStateMachine#setup(boolean, boolean, String)
|
||||
*/
|
||||
public void start(EasyGiantsFoundryState state, EasyGiantsFoundryConfig config, int startingHeat)
|
||||
{
|
||||
// use Velocity to determine if heating or cooling
|
||||
if (actionHeating)
|
||||
{
|
||||
heatingTicks = 0;
|
||||
coolingTicks = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
heatingTicks = -1;
|
||||
coolingTicks = 0;
|
||||
}
|
||||
|
||||
this.startingHeat = startingHeat;
|
||||
this.state = state;
|
||||
this.config = config;
|
||||
|
||||
updateEstimates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the estimated remaining duration of the heating/cooling action.
|
||||
*
|
||||
* @return the estimated remaining duration in ticks
|
||||
*/
|
||||
public int getRemainingDuration()
|
||||
{
|
||||
if (isHeating())
|
||||
{
|
||||
return Math.max(0, (estimatedDuration - heatingTicks));
|
||||
}
|
||||
else if (isCooling())
|
||||
{
|
||||
return Math.max(0, (estimatedDuration - coolingTicks));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Core logic. Runs once on {@link HeatActionStateMachine#start} and assumes synchronization with the game.
|
||||
* Calculate the estimated duration and goal heat amount of the heating/cooling action.
|
||||
*/
|
||||
public void updateEstimates()
|
||||
{
|
||||
|
||||
HeatActionSolver.DurationResult result =
|
||||
HeatActionSolver.solve(
|
||||
getState().getCurrentStage(),
|
||||
getState().getCurrentHeatRange(),
|
||||
getState().getActionsLeftInStage(),
|
||||
getStartingHeat(),
|
||||
actionFast,
|
||||
isHeating(),
|
||||
config.heatActionPadTicks(),
|
||||
state.isPlayerRunning()
|
||||
);
|
||||
|
||||
goalInRange = result.isGoalInRange();
|
||||
isOverShooting = result.isOvershooting();
|
||||
|
||||
predictedHeat = result.getPredictedHeat();
|
||||
|
||||
estimatedDuration = result.getDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to remind the neccessary parameters to start the state-machine.
|
||||
*
|
||||
* @param actionName the name of the action to display in the ui overlay
|
||||
*/
|
||||
public void setup(boolean isFast, boolean isHeating, String actionName)
|
||||
{
|
||||
actionFast = isFast;
|
||||
actionHeating = isHeating;
|
||||
actionname = actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the state-machine.
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
heatingTicks = -1;
|
||||
coolingTicks = -1;
|
||||
actionname = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the state is currently heating.
|
||||
*
|
||||
* @return true if heating, false otherwise
|
||||
*/
|
||||
public boolean isHeating()
|
||||
{
|
||||
return heatingTicks >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the state is currently cooling.
|
||||
*
|
||||
* @return true if cooling, false otherwise
|
||||
*/
|
||||
public boolean isCooling()
|
||||
{
|
||||
return coolingTicks >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the heating/cooling state is currently idle. Neither heating nor cooling.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isIdle()
|
||||
{
|
||||
return !(isHeating() || isCooling());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick the state-machine. This has to be called onVarbitChanged in order to sync with the game.
|
||||
*/
|
||||
public void onTick()
|
||||
{
|
||||
if (isIdle()) return;
|
||||
|
||||
if (isHeating())
|
||||
{
|
||||
if (heatingTicks >= estimatedDuration)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
heatingTicks++;
|
||||
}
|
||||
}
|
||||
else if (isCooling())
|
||||
{
|
||||
if (coolingTicks >= estimatedDuration)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
coolingTicks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
public enum HighlightStyle
|
||||
{
|
||||
HIGHLIGHT_BORDER,
|
||||
HIGHLIGHT_CLICKBOX
|
||||
}
|
||||
9
src/main/java/ee/futur/easygiantsfoundry/MathUtil.java
Normal file
9
src/main/java/ee/futur/easygiantsfoundry/MathUtil.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
public class MathUtil
|
||||
{
|
||||
public static double max1(double a)
|
||||
{
|
||||
return a > 0 ? a : 1;
|
||||
}
|
||||
}
|
||||
148
src/main/java/ee/futur/easygiantsfoundry/MetalBarCounter.java
Normal file
148
src/main/java/ee/futur/easygiantsfoundry/MetalBarCounter.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.MetalBarSource;
|
||||
import ee.futur.easygiantsfoundry.enums.MetalBarType;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.gameval.InventoryID;
|
||||
import net.runelite.api.gameval.ItemID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class MetalBarCounter
|
||||
{
|
||||
private final Map<Integer, Map<MetalBarType, CountsBySource>> index = new HashMap<>(); // container id -> bar counts
|
||||
|
||||
@Inject
|
||||
private EasyGiantsFoundryConfig config;
|
||||
|
||||
@Getter
|
||||
private boolean seenBank = false;
|
||||
|
||||
public int get(MetalBarType type)
|
||||
{
|
||||
int count = 0;
|
||||
for (Map<MetalBarType, CountsBySource> counts : index.values())
|
||||
{
|
||||
count += counts.get(type).sum(config);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
index.clear();
|
||||
seenBank = false;
|
||||
}
|
||||
|
||||
public void put(ItemContainer container)
|
||||
{
|
||||
int tinOre = 0;
|
||||
int copperOre = 0;
|
||||
int ironOre = 0;
|
||||
|
||||
if (container.getId() == InventoryID.BANK)
|
||||
{
|
||||
seenBank = true;
|
||||
}
|
||||
|
||||
Map<MetalBarType, CountsBySource> counts = newCounts();
|
||||
for (Item item : container.getItems())
|
||||
{
|
||||
MetalBarValues.Record record = MetalBarValues.get(item.getId());
|
||||
if (record == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// ore special cases:
|
||||
// * add bronze bars equal to min(tin, copper)
|
||||
// * there is an edge case here where it won't sum quite right multiple item containers but I don't really
|
||||
// care to code for that right now
|
||||
// * iron ore counts for both iron and steel
|
||||
switch (item.getId())
|
||||
{
|
||||
case ItemID.TIN_ORE:
|
||||
case ItemID.Cert.TIN_ORE:
|
||||
tinOre += item.getQuantity();
|
||||
break;
|
||||
case ItemID.COPPER_ORE:
|
||||
case ItemID.Cert.COPPER_ORE:
|
||||
copperOre += item.getQuantity();
|
||||
break;
|
||||
case ItemID.IRON_ORE:
|
||||
case ItemID.Cert.IRON_ORE:
|
||||
ironOre += item.getQuantity();
|
||||
break;
|
||||
default:
|
||||
counts.compute(record.getType(), (k, v) ->
|
||||
v.add(record.getSource(), record.getValue() * item.getQuantity()));
|
||||
}
|
||||
}
|
||||
|
||||
int finalTinOre = tinOre;
|
||||
int finalCopperOre = copperOre;
|
||||
int finalIronOre = ironOre;
|
||||
counts.compute(MetalBarType.BRONZE, (k, v) ->
|
||||
v.add(MetalBarSource.ORE, Math.min(finalTinOre, finalCopperOre)));
|
||||
counts.compute(MetalBarType.IRON, (k, v) ->
|
||||
v.add(MetalBarSource.ORE, finalIronOre));
|
||||
counts.compute(MetalBarType.STEEL, (k, v) ->
|
||||
v.add(MetalBarSource.ORE, finalIronOre));
|
||||
|
||||
index.put(container.getId(), counts);
|
||||
}
|
||||
|
||||
private static Map<MetalBarType, CountsBySource> newCounts()
|
||||
{
|
||||
Map<MetalBarType, CountsBySource> counts = new HashMap<>();
|
||||
counts.put(MetalBarType.BRONZE, CountsBySource.empty());
|
||||
counts.put(MetalBarType.IRON, CountsBySource.empty());
|
||||
counts.put(MetalBarType.STEEL, CountsBySource.empty());
|
||||
counts.put(MetalBarType.MITHRIL, CountsBySource.empty());
|
||||
counts.put(MetalBarType.ADAMANT, CountsBySource.empty());
|
||||
counts.put(MetalBarType.RUNITE, CountsBySource.empty());
|
||||
return counts;
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class CountsBySource
|
||||
{
|
||||
int ores, bars, equipment;
|
||||
|
||||
public static CountsBySource empty()
|
||||
{
|
||||
return new CountsBySource(0, 0, 0);
|
||||
}
|
||||
|
||||
public CountsBySource add(MetalBarSource source, int count)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case ORE:
|
||||
return new CountsBySource(ores + count, bars, equipment);
|
||||
case BAR:
|
||||
return new CountsBySource(ores, bars + count, equipment);
|
||||
case EQUIPMENT:
|
||||
return new CountsBySource(ores, bars, equipment + count);
|
||||
default:
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public int sum(EasyGiantsFoundryConfig config)
|
||||
{
|
||||
int sum = config.countOre() ? ores : 0;
|
||||
sum += config.countBars() ? bars : 0;
|
||||
sum += config.countEquipment() ? equipment : 0;
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
238
src/main/java/ee/futur/easygiantsfoundry/MetalBarValues.java
Normal file
238
src/main/java/ee/futur/easygiantsfoundry/MetalBarValues.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.MetalBarSource;
|
||||
import ee.futur.easygiantsfoundry.enums.MetalBarType;
|
||||
import lombok.Value;
|
||||
import net.runelite.api.gameval.ItemID;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class MetalBarValues
|
||||
{
|
||||
private static final HashMap<Integer, Record> values = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
// tin, copper, and iron are included here so the counter doesn't ignore them, but their actual counts are
|
||||
// handled as special cases
|
||||
putOre(ItemID.TIN_ORE, MetalBarType.BRONZE);
|
||||
putOre(ItemID.COPPER_ORE, MetalBarType.BRONZE);
|
||||
putOre(ItemID.IRON_BAR, MetalBarType.IRON);
|
||||
putOre(ItemID.MITHRIL_ORE, MetalBarType.MITHRIL);
|
||||
putOre(ItemID.ADAMANTITE_ORE, MetalBarType.ADAMANT);
|
||||
putOre(ItemID.RUNITE_ORE, MetalBarType.RUNITE);
|
||||
putOre(ItemID.Cert.TIN_ORE, MetalBarType.BRONZE);
|
||||
putOre(ItemID.Cert.COPPER_ORE, MetalBarType.BRONZE);
|
||||
putOre(ItemID.Cert.IRON_BAR, MetalBarType.IRON);
|
||||
putOre(ItemID.Cert.MITHRIL_ORE, MetalBarType.MITHRIL);
|
||||
putOre(ItemID.Cert.ADAMANTITE_ORE, MetalBarType.ADAMANT);
|
||||
putOre(ItemID.Cert.RUNITE_ORE, MetalBarType.RUNITE);
|
||||
|
||||
putBar(ItemID.BRONZE_BAR, MetalBarType.BRONZE);
|
||||
putBar(ItemID.IRON_BAR, MetalBarType.IRON);
|
||||
putBar(ItemID.STEEL_BAR, MetalBarType.STEEL);
|
||||
putBar(ItemID.MITHRIL_BAR, MetalBarType.MITHRIL);
|
||||
putBar(ItemID.ADAMANTITE_BAR, MetalBarType.ADAMANT);
|
||||
putBar(ItemID.RUNITE_BAR, MetalBarType.RUNITE);
|
||||
putBar(ItemID.Cert.BRONZE_BAR, MetalBarType.BRONZE);
|
||||
putBar(ItemID.Cert.IRON_BAR, MetalBarType.IRON);
|
||||
putBar(ItemID.Cert.STEEL_BAR, MetalBarType.STEEL);
|
||||
putBar(ItemID.Cert.MITHRIL_BAR, MetalBarType.MITHRIL);
|
||||
putBar(ItemID.Cert.ADAMANTITE_BAR, MetalBarType.ADAMANT);
|
||||
putBar(ItemID.Cert.RUNITE_BAR, MetalBarType.RUNITE);
|
||||
|
||||
putEquipment(ItemID.BRONZE_SCIMITAR, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.BRONZE_LONGSWORD, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.BRONZE_FULL_HELM, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.BRONZE_SQ_SHIELD, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.BRONZE_CLAWS, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.BRONZE_WARHAMMER, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_BATTLEAXE, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_CHAINBODY, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_KITESHIELD, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_2H_SWORD, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_PLATELEGS, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_PLATESKIRT, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.BRONZE_PLATEBODY, MetalBarType.BRONZE, 4);
|
||||
putEquipment(ItemID.Cert.BRONZE_SCIMITAR, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.Cert.BRONZE_LONGSWORD, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.Cert.BRONZE_FULL_HELM, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.Cert.BRONZE_SQ_SHIELD, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.Cert.BRONZE_CLAWS, MetalBarType.BRONZE, 1);
|
||||
putEquipment(ItemID.Cert.BRONZE_WARHAMMER, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_BATTLEAXE, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_CHAINBODY, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_KITESHIELD, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_2H_SWORD, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_PLATELEGS, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_PLATESKIRT, MetalBarType.BRONZE, 2);
|
||||
putEquipment(ItemID.Cert.BRONZE_PLATEBODY, MetalBarType.BRONZE, 4);
|
||||
|
||||
putEquipment(ItemID.IRON_SCIMITAR, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.IRON_LONGSWORD, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.IRON_FULL_HELM, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.IRON_SQ_SHIELD, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.IRON_CLAWS, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.IRON_WARHAMMER, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_BATTLEAXE, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_CHAINBODY, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_KITESHIELD, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_2H_SWORD, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_PLATELEGS, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_PLATESKIRT, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.IRON_PLATEBODY, MetalBarType.IRON, 4);
|
||||
putEquipment(ItemID.Cert.IRON_SCIMITAR, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.Cert.IRON_LONGSWORD, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.Cert.IRON_FULL_HELM, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.Cert.IRON_SQ_SHIELD, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.Cert.IRON_CLAWS, MetalBarType.IRON, 1);
|
||||
putEquipment(ItemID.Cert.IRON_WARHAMMER, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_BATTLEAXE, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_CHAINBODY, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_KITESHIELD, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_2H_SWORD, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_PLATELEGS, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_PLATESKIRT, MetalBarType.IRON, 2);
|
||||
putEquipment(ItemID.Cert.IRON_PLATEBODY, MetalBarType.IRON, 4);
|
||||
|
||||
putEquipment(ItemID.STEEL_SCIMITAR, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.STEEL_LONGSWORD, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.STEEL_FULL_HELM, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.STEEL_SQ_SHIELD, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.STEEL_CLAWS, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.STEEL_WARHAMMER, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_BATTLEAXE, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_CHAINBODY, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_KITESHIELD, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_2H_SWORD, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_PLATELEGS, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_PLATESKIRT, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.STEEL_PLATEBODY, MetalBarType.STEEL, 4);
|
||||
putEquipment(ItemID.Cert.STEEL_SCIMITAR, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.Cert.STEEL_LONGSWORD, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.Cert.STEEL_FULL_HELM, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.Cert.STEEL_SQ_SHIELD, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.Cert.STEEL_CLAWS, MetalBarType.STEEL, 1);
|
||||
putEquipment(ItemID.Cert.STEEL_WARHAMMER, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_BATTLEAXE, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_CHAINBODY, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_KITESHIELD, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_2H_SWORD, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_PLATELEGS, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_PLATESKIRT, MetalBarType.STEEL, 2);
|
||||
putEquipment(ItemID.Cert.STEEL_PLATEBODY, MetalBarType.STEEL, 4);
|
||||
|
||||
putEquipment(ItemID.MITHRIL_SCIMITAR, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.MITHRIL_LONGSWORD, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.MITHRIL_FULL_HELM, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.MITHRIL_SQ_SHIELD, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.MITHRIL_CLAWS, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.MITHRIL_WARHAMMER, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_BATTLEAXE, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_CHAINBODY, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_KITESHIELD, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_2H_SWORD, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_PLATELEGS, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_PLATESKIRT, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.MITHRIL_PLATEBODY, MetalBarType.MITHRIL, 4);
|
||||
putEquipment(ItemID.Cert.MITHRIL_SCIMITAR, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.Cert.MITHRIL_LONGSWORD, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.Cert.MITHRIL_FULL_HELM, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.Cert.MITHRIL_SQ_SHIELD, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.Cert.MITHRIL_CLAWS, MetalBarType.MITHRIL, 1);
|
||||
putEquipment(ItemID.Cert.MITHRIL_WARHAMMER, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_BATTLEAXE, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_CHAINBODY, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_KITESHIELD, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_2H_SWORD, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_PLATELEGS, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_PLATESKIRT, MetalBarType.MITHRIL, 2);
|
||||
putEquipment(ItemID.Cert.MITHRIL_PLATEBODY, MetalBarType.MITHRIL, 4);
|
||||
|
||||
putEquipment(ItemID.ADAMANT_SCIMITAR, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.ADAMANT_LONGSWORD, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.ADAMANT_FULL_HELM, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.ADAMANT_SQ_SHIELD, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.ADAMANT_CLAWS, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.ADAMNT_WARHAMMER, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_BATTLEAXE, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_CHAINBODY, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_KITESHIELD, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_2H_SWORD, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_PLATELEGS, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_PLATESKIRT, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.ADAMANT_PLATEBODY, MetalBarType.ADAMANT, 4);
|
||||
putEquipment(ItemID.Cert.ADAMANT_SCIMITAR, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.Cert.ADAMANT_LONGSWORD, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.Cert.ADAMANT_FULL_HELM, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.Cert.ADAMANT_SQ_SHIELD, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.Cert.ADAMANT_CLAWS, MetalBarType.ADAMANT, 1);
|
||||
putEquipment(ItemID.Cert.ADAMNT_WARHAMMER, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_BATTLEAXE, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_CHAINBODY, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_KITESHIELD, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_2H_SWORD, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_PLATELEGS, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_PLATESKIRT, MetalBarType.ADAMANT, 2);
|
||||
putEquipment(ItemID.Cert.ADAMANT_PLATEBODY, MetalBarType.ADAMANT, 4);
|
||||
|
||||
putEquipment(ItemID.RUNE_SCIMITAR, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.RUNE_LONGSWORD, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.RUNE_FULL_HELM, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.RUNE_SQ_SHIELD, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.RUNE_CLAWS, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.RUNE_WARHAMMER, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_BATTLEAXE, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_CHAINBODY, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_KITESHIELD, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_2H_SWORD, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_PLATELEGS, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_PLATESKIRT, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.RUNE_PLATEBODY, MetalBarType.RUNITE, 4);
|
||||
putEquipment(ItemID.Cert.RUNE_SCIMITAR, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.Cert.RUNE_LONGSWORD, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.Cert.RUNE_FULL_HELM, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.Cert.RUNE_SQ_SHIELD, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.Cert.RUNE_CLAWS, MetalBarType.RUNITE, 1);
|
||||
putEquipment(ItemID.Cert.RUNE_WARHAMMER, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_BATTLEAXE, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_CHAINBODY, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_KITESHIELD, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_2H_SWORD, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_PLATELEGS, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_PLATESKIRT, MetalBarType.RUNITE, 2);
|
||||
putEquipment(ItemID.Cert.RUNE_PLATEBODY, MetalBarType.RUNITE, 4);
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class Record
|
||||
{
|
||||
MetalBarType type;
|
||||
MetalBarSource source;
|
||||
int value;
|
||||
}
|
||||
|
||||
public static Record get(int id)
|
||||
{
|
||||
return values.get(id);
|
||||
}
|
||||
|
||||
private static void putOre(int id, MetalBarType type)
|
||||
{
|
||||
values.put(id, new Record(type, MetalBarSource.ORE, 1));
|
||||
}
|
||||
|
||||
private static void putBar(int id, MetalBarType type)
|
||||
{
|
||||
values.put(id, new Record(type, MetalBarSource.BAR, 1));
|
||||
}
|
||||
|
||||
private static void putEquipment(int id, MetalBarType type, int value)
|
||||
{
|
||||
values.put(id, new Record(type, MetalBarSource.EQUIPMENT, value));
|
||||
}
|
||||
|
||||
private MetalBarValues()
|
||||
{
|
||||
}
|
||||
}
|
||||
146
src/main/java/ee/futur/easygiantsfoundry/MouldHelper.java
Normal file
146
src/main/java/ee/futur/easygiantsfoundry/MouldHelper.java
Normal file
@@ -0,0 +1,146 @@
|
||||
package ee.futur.easygiantsfoundry;
|
||||
|
||||
import ee.futur.easygiantsfoundry.enums.CommissionType;
|
||||
import ee.futur.easygiantsfoundry.enums.Mould;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
|
||||
public class MouldHelper
|
||||
{
|
||||
static final int MOULD_LIST_PARENT = 47054857;
|
||||
static final int DRAW_MOULD_LIST_SCRIPT = 6093;
|
||||
static final int REDRAW_MOULD_LIST_SCRIPT = 6095;
|
||||
static final int RESET_MOULD_SCRIPT = 6108;
|
||||
public static final int SELECT_MOULD_SCRIPT = 6098;
|
||||
static final int SWORD_TYPE_1_VARBIT = 13907; // 4=Broad
|
||||
static final int SWORD_TYPE_2_VARBIT = 13908; // 3=Flat
|
||||
private static final int DISABLED_TEXT_COLOR = 0x9f9f9f;
|
||||
|
||||
private static final int SCORE_TYPE1_SCORE_WIDGET = 47054876;
|
||||
private static final int SCORE_TYPE2_SCORE_WIDGET = 47054878;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private EasyGiantsFoundryConfig config;
|
||||
|
||||
public Integer getTotalScore()
|
||||
{
|
||||
Widget type1Widget = client.getWidget(SCORE_TYPE1_SCORE_WIDGET);
|
||||
Widget type2Widget = client.getWidget(SCORE_TYPE2_SCORE_WIDGET);
|
||||
if (type1Widget == null || type2Widget == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String type1Str = type1Widget.getText();
|
||||
String type2Str = type2Widget.getText();
|
||||
|
||||
// (+6) 6
|
||||
// ^ space seperated
|
||||
// or
|
||||
// 6
|
||||
if (type1Str.contains(" "))
|
||||
{
|
||||
type1Str = type1Str.substring(type1Str.lastIndexOf(' ') + 1);
|
||||
}
|
||||
|
||||
if (type2Str.contains(" "))
|
||||
{
|
||||
type2Str = type2Str.substring(type2Str.lastIndexOf(' ') + 1);
|
||||
}
|
||||
|
||||
|
||||
int type1Score;
|
||||
int type2Score;
|
||||
|
||||
try
|
||||
{
|
||||
type1Score = Integer.parseInt(type1Str);
|
||||
type2Score = Integer.parseInt(type2Str);
|
||||
} catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return type1Score + type2Score;
|
||||
}
|
||||
public void selectBest(int scriptId)
|
||||
{
|
||||
Widget parent = client.getWidget(MOULD_LIST_PARENT);
|
||||
if (parent == null || parent.getChildren() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Mould, Widget> mouldToChild = getOptions(parent.getChildren());
|
||||
|
||||
int bestScore = -1;
|
||||
Widget bestWidget = null;
|
||||
CommissionType type1 = CommissionType.forVarbit(client.getVarbitValue(SWORD_TYPE_1_VARBIT));
|
||||
CommissionType type2 = CommissionType.forVarbit(client.getVarbitValue(SWORD_TYPE_2_VARBIT));
|
||||
for (Map.Entry<Mould, Widget> entry : mouldToChild.entrySet())
|
||||
{
|
||||
Mould mould = entry.getKey();
|
||||
int score = mould.getScore(type1, type2);
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestWidget = entry.getValue();
|
||||
}
|
||||
}
|
||||
if (bestWidget != null)
|
||||
{
|
||||
bestWidget.setTextColor(config.mouldTextColour().getRGB());
|
||||
}
|
||||
|
||||
if (scriptId == DRAW_MOULD_LIST_SCRIPT || scriptId == REDRAW_MOULD_LIST_SCRIPT)
|
||||
{
|
||||
Widget scrollBar = client.getWidget(718, 11);
|
||||
Widget scrollList = client.getWidget(718, 9);
|
||||
if (scrollBar != null && scrollList != null)
|
||||
{
|
||||
int height = scrollList.getHeight();
|
||||
int scrollMax = scrollList.getScrollHeight();
|
||||
Widget finalBestWidget = bestWidget;
|
||||
clientThread.invokeAtTickEnd(() ->
|
||||
{
|
||||
if (finalBestWidget != null)
|
||||
{
|
||||
client.runScript(
|
||||
ScriptID.UPDATE_SCROLLBAR,
|
||||
scrollBar.getId(),
|
||||
scrollList.getId(),
|
||||
Math.min(finalBestWidget.getOriginalY() - 2, scrollMax - height));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Mould, Widget> getOptions(Widget[] children)
|
||||
{
|
||||
Map<Mould, Widget> mouldToChild = new LinkedHashMap<>();
|
||||
for (int i = 2; i < children.length; i += 17)
|
||||
{
|
||||
Widget child = children[i];
|
||||
Mould mould = Mould.forName(child.getText());
|
||||
if (mould != null && child.getTextColor() != DISABLED_TEXT_COLOR)
|
||||
{
|
||||
mouldToChild.put(mould, child);
|
||||
}
|
||||
}
|
||||
return mouldToChild;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
public enum CommissionType
|
||||
{
|
||||
NONE,
|
||||
NARROW, // 1
|
||||
LIGHT, // 2
|
||||
FLAT, // 3
|
||||
BROAD, // 4
|
||||
HEAVY, // 5
|
||||
SPIKED, // 6
|
||||
;
|
||||
|
||||
public static final CommissionType[] values = CommissionType.values();
|
||||
|
||||
public static CommissionType forVarbit(int varbitValue)
|
||||
{
|
||||
if (varbitValue < 0 || varbitValue >= values.length)
|
||||
{
|
||||
return NONE;
|
||||
}
|
||||
return CommissionType.values[varbitValue];
|
||||
}
|
||||
}
|
||||
27
src/main/java/ee/futur/easygiantsfoundry/enums/FontType.java
Normal file
27
src/main/java/ee/futur/easygiantsfoundry/enums/FontType.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
|
||||
import java.awt.Font;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum FontType
|
||||
{
|
||||
DEFAULT("Default", null),
|
||||
REGULAR("Regular", FontManager.getRunescapeFont()),
|
||||
BOLD("Bold", FontManager.getRunescapeBoldFont()),
|
||||
SMALL("Small", FontManager.getRunescapeSmallFont()),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
private final Font font;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
20
src/main/java/ee/futur/easygiantsfoundry/enums/Heat.java
Normal file
20
src/main/java/ee/futur/easygiantsfoundry/enums/Heat.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Heat
|
||||
{
|
||||
LOW("Low", ColorScheme.PROGRESS_COMPLETE_COLOR),
|
||||
MED("Medium", ColorScheme.PROGRESS_INPROGRESS_COLOR),
|
||||
HIGH("High", ColorScheme.PROGRESS_ERROR_COLOR),
|
||||
NONE("Not in range", ColorScheme.LIGHT_GRAY_COLOR);
|
||||
|
||||
private final String name;
|
||||
private final Color color;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
public enum MetalBarSource
|
||||
{
|
||||
ORE,
|
||||
BAR,
|
||||
EQUIPMENT,
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
public enum MetalBarType
|
||||
{
|
||||
BRONZE,
|
||||
IRON,
|
||||
STEEL,
|
||||
MITHRIL,
|
||||
ADAMANT,
|
||||
RUNITE,
|
||||
;
|
||||
}
|
||||
74
src/main/java/ee/futur/easygiantsfoundry/enums/Mould.java
Normal file
74
src/main/java/ee/futur/easygiantsfoundry/enums/Mould.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static ee.futur.easygiantsfoundry.enums.CommissionType.*;
|
||||
import static ee.futur.easygiantsfoundry.enums.MouldType.*;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Mould
|
||||
{
|
||||
CHOPPER_FORTE("Chopper Forte", FORTE, ImmutableMap.of(BROAD, 4, LIGHT, 4, FLAT, 4)),
|
||||
GALDIUS_RICASSO("Galdius Ricasso", FORTE, ImmutableMap.of(BROAD, 4, HEAVY, 4, FLAT, 4)),
|
||||
DISARMING_FORTE("Disarming Forte", FORTE, ImmutableMap.of(NARROW, 4, LIGHT, 4, SPIKED, 4)),
|
||||
MEDUSA_RICASSO("Medusa Ricasso", FORTE, ImmutableMap.of(BROAD, 8, HEAVY, 6, FLAT, 8)),
|
||||
SERPENT_RICASSO("Serpent Ricasso", FORTE, ImmutableMap.of(NARROW, 6, LIGHT, 8, FLAT, 8)),
|
||||
SERRATED_FORTE("Serrated Forte", FORTE, ImmutableMap.of(NARROW, 8, HEAVY, 8, SPIKED, 6)),
|
||||
STILETTO_FORTE("Stiletto Forte", FORTE, ImmutableMap.of(NARROW, 8, LIGHT, 10, FLAT, 8)),
|
||||
DEFENDER_BASE("Defender Base", FORTE, ImmutableMap.of(BROAD, 8, HEAVY, 10, FLAT, 8)),
|
||||
JUGGERNAUT_FORTE("Juggernaut Forte", FORTE, ImmutableMap.of(BROAD, 4, HEAVY, 4, SPIKED, 16)),
|
||||
CHOPPER_FORTE_1("Chopper Forte +1", FORTE, ImmutableMap.of(BROAD, 3, LIGHT, 4, FLAT, 18)),
|
||||
SPIKER("Spiker!", FORTE, ImmutableMap.of(NARROW, 1, HEAVY, 2, SPIKED, 22)),
|
||||
SAW_BLADE("Saw Blade", BLADE, ImmutableMap.of(BROAD, 4, LIGHT, 4, SPIKED, 4)),
|
||||
DEFENDERS_EDGE("Defenders Edge", BLADE, ImmutableMap.of(BROAD, 4, HEAVY, 4, SPIKED, 4)),
|
||||
FISH_BLADE("Fish Blade", BLADE, ImmutableMap.of(NARROW, 4, LIGHT, 4, FLAT, 4)),
|
||||
MEDUSA_BLADE("Medusa Blade", BLADE, ImmutableMap.of(BROAD, 8, HEAVY, 8, FLAT, 6)),
|
||||
STILETTO_BLADE("Stiletto Blade", BLADE, ImmutableMap.of(NARROW, 8, LIGHT, 6, FLAT, 8)),
|
||||
GLADIUS_EDGE("Gladius Edge", BLADE, ImmutableMap.of(NARROW, 6, HEAVY, 8, FLAT, 8)),
|
||||
FLAMBERGE_BLADE("Flamberge Blade", BLADE, ImmutableMap.of(NARROW, 8, LIGHT, 8, SPIKED, 10)),
|
||||
SERPENT_BLADE("Serpent Blade", BLADE, ImmutableMap.of(NARROW, 10, LIGHT, 8, FLAT, 8)),
|
||||
CLAYMORE_BLADE("Claymore Blade", BLADE, ImmutableMap.of(BROAD, 16, HEAVY, 4, FLAT, 4)),
|
||||
FLEUR_DE_BLADE("Fleur de Blade", BLADE, ImmutableMap.of(BROAD, 4, HEAVY, 18, SPIKED, 1)),
|
||||
CHOPPA("Choppa!", BLADE, ImmutableMap.of(BROAD, 1, LIGHT, 22, FLAT, 2)),
|
||||
PEOPLE_POKER_POINT("People Poker Point", TIP, ImmutableMap.of(NARROW, 4, HEAVY, 4, FLAT, 4)),
|
||||
CHOPPER_TIP("Chopper Tip", TIP, ImmutableMap.of(BROAD, 4, LIGHT, 4, SPIKED, 4)),
|
||||
MEDUSAS_HEAD("Medusa's Head", TIP, ImmutableMap.of(BROAD, 4, HEAVY, 4, SPIKED, 4)),
|
||||
SERPENTS_FANG("Serpent's Fang", TIP, ImmutableMap.of(NARROW, 8, LIGHT, 6, SPIKED, 8)),
|
||||
GLADIUS_POINT("Gladius Point", TIP, ImmutableMap.of(NARROW, 8, HEAVY, 8, FLAT, 6)),
|
||||
SAW_TIP("Saw Tip", TIP, ImmutableMap.of(BROAD, 6, HEAVY, 8, SPIKED, 8)),
|
||||
CORRUPTED_POINT("Corrupted Point", TIP, ImmutableMap.of(NARROW, 8, LIGHT, 10, SPIKED, 8)),
|
||||
DEFENDERS_TIP("Defenders Tip", TIP, ImmutableMap.of(BROAD, 10, HEAVY, 8, SPIKED, 8)),
|
||||
SERRATED_TIP("Serrated Tip", TIP, ImmutableMap.of(NARROW, 4, LIGHT, 16, SPIKED, 4)),
|
||||
NEEDLE_POINT("Needle Point", TIP, ImmutableMap.of(NARROW, 18, LIGHT, 3, FLAT, 4)),
|
||||
THE_POINT("The Point!", TIP, ImmutableMap.of(BROAD, 2, LIGHT, 1, FLAT, 22)),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
private final MouldType mouldType;
|
||||
private final Map<CommissionType, Integer> typeToScore;
|
||||
|
||||
public static final Mould[] values = Mould.values();
|
||||
|
||||
public static Mould forName(String text)
|
||||
{
|
||||
for (Mould mould : values)
|
||||
{
|
||||
if (mould.name.equalsIgnoreCase(text))
|
||||
{
|
||||
return mould;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getScore(CommissionType type1, CommissionType type2)
|
||||
{
|
||||
int score = 0;
|
||||
score += typeToScore.getOrDefault(type1, 0);
|
||||
score += typeToScore.getOrDefault(type2, 0);
|
||||
return score;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
public enum MouldType
|
||||
{
|
||||
FORTE,
|
||||
BLADE,
|
||||
TIP,
|
||||
}
|
||||
32
src/main/java/ee/futur/easygiantsfoundry/enums/Stage.java
Normal file
32
src/main/java/ee/futur/easygiantsfoundry/enums/Stage.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package ee.futur.easygiantsfoundry.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Stage
|
||||
{
|
||||
TRIP_HAMMER("Hammer", Heat.HIGH, 20, -25, 4, 14),
|
||||
GRINDSTONE("Grind", Heat.MED, 10, 15, 7, 19),
|
||||
POLISHING_WHEEL("Polish", Heat.LOW, 10, -17, 12, 10);
|
||||
|
||||
private final String name;
|
||||
private final Heat heat;
|
||||
private final int progressPerAction;
|
||||
private final int heatChange;
|
||||
|
||||
private final int distanceToLava;
|
||||
private final int distanceToWaterfall;
|
||||
|
||||
public boolean isHeating()
|
||||
{
|
||||
return heatChange > 0;
|
||||
}
|
||||
|
||||
public boolean isCooling()
|
||||
{
|
||||
return heatChange < 0;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user