Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2022-06-17 17:01:19 +02:00
14 changed files with 415 additions and 49 deletions

View File

@@ -50,4 +50,13 @@ public final class ParamID
public static final int SETTING_SLIDER_IS_DRAGGABLE = 1108;
public static final int SETTING_SLIDER_DEADZONE = 1109;
public static final int SETTING_SLIDER_DEADTIME = 1110;
public static final int OC_ITEM_OP1 = 451;
public static final int OC_ITEM_OP2 = 452;
public static final int OC_ITEM_OP3 = 453;
public static final int OC_ITEM_OP4 = 454;
public static final int OC_ITEM_OP5 = 455;
public static final int OC_ITEM_OP6 = 456;
public static final int OC_ITEM_OP7 = 457;
public static final int OC_ITEM_OP8 = 458;
}

View File

@@ -105,15 +105,12 @@ class NpcOverlay extends Overlay
if (highlightedNpc.isSwTile())
{
int size = npcComposition.getSize();
LocalPoint lp = actor.getLocalLocation();
int x = lp.getX() - ((size - 1) * Perspective.LOCAL_TILE_SIZE / 2);
int y = lp.getY() - ((size - 1) * Perspective.LOCAL_TILE_SIZE / 2);
Polygon southWestTilePoly = Perspective.getCanvasTilePoly(client, new LocalPoint(x, y));
renderPoly(graphics, borderColor, borderWidth, fillColor, southWestTilePoly);
LocalPoint lp = LocalPoint.fromWorld(client, actor.getWorldLocation());
if (lp != null)
{
Polygon tilePoly = Perspective.getCanvasTilePoly(client, lp);
renderPoly(graphics, borderColor, borderWidth, fillColor, tilePoly);
}
}
if (highlightedNpc.isOutline())

View File

@@ -33,6 +33,17 @@ public interface CorpConfig extends Config
{
String GROUP = "corp";
@ConfigItem(
keyName = "leftClickCore",
name = "Left click walk on core",
description = "Prioritizes Walk here over Attack on the Dark energy core",
position = 1
)
default boolean leftClickCore()
{
return true;
}
@ConfigItem(
keyName = "showDamage",
name = "Show damage overlay",

View File

@@ -37,12 +37,15 @@ import net.runelite.api.Actor;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Varbits;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.HitsplatApplied;
import net.runelite.api.events.InteractingChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.VarbitChanged;
@@ -66,6 +69,9 @@ import net.runelite.client.ui.overlay.OverlayManager;
@Slf4j
public class CorpPlugin extends Plugin
{
private static final String ATTACK = "Attack";
private static final String DARK_ENERGY_CORE = "Dark energy core";
@Getter(AccessLevel.PACKAGE)
private NPC corp;
@@ -243,4 +249,24 @@ public class CorpPlugin extends Plugin
}
}
}
@Subscribe
public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded)
{
final MenuEntry menuEntry = menuEntryAdded.getMenuEntry();
final NPC npc = menuEntry.getNpc();
if (npc == null || !DARK_ENERGY_CORE.equals(npc.getName()))
{
return;
}
if (menuEntry.getType() != MenuAction.NPC_SECOND_OPTION
|| !menuEntry.getOption().equals(ATTACK)
|| !config.leftClickCore())
{
return;
}
menuEntry.setDeprioritized(true);
}
}

View File

@@ -176,4 +176,15 @@ public interface EntityHiderConfig extends Config
{
return false;
}
@ConfigItem(
position = 14,
keyName = "hideDeadNpcs",
name = "Hide Dead NPCs",
description = "Hides NPCs when their health reaches 0"
)
default boolean hideDeadNpcs()
{
return false;
}
}

View File

@@ -68,6 +68,7 @@ public class EntityHiderPlugin extends Plugin
private boolean hideLocalPlayer2D;
private boolean hideNPCs;
private boolean hideNPCs2D;
private boolean hideDeadNpcs;
private boolean hidePets;
private boolean hideAttackers;
private boolean hideProjectiles;
@@ -118,6 +119,7 @@ public class EntityHiderPlugin extends Plugin
hideNPCs = config.hideNPCs();
hideNPCs2D = config.hideNPCs2D();
hideDeadNpcs = config.hideDeadNpcs();
hidePets = config.hidePets();
@@ -187,6 +189,12 @@ public class EntityHiderPlugin extends Plugin
return !hidePets;
}
// dead npcs can also be interacting so prioritize it over the interacting check
if (npc.isDead() && hideDeadNpcs)
{
return false;
}
if (npc.getInteracting() == client.getLocalPlayer())
{
boolean b = hideAttackers;

View File

@@ -211,7 +211,7 @@ class LootTrackerPanel extends PluginPanel
SwingUtil.removeButtonDecorations(collapseBtn);
collapseBtn.setIcon(EXPAND_ICON);
collapseBtn.setSelectedIcon(COLLAPSE_ICON);
SwingUtil.addModalTooltip(collapseBtn, "Collapse All", "Un-Collapse All");
SwingUtil.addModalTooltip(collapseBtn, "Expand All", "Collapse All");
collapseBtn.setBackground(ColorScheme.DARKER_GRAY_COLOR);
collapseBtn.setUI(new BasicButtonUI()); // substance breaks the layout
collapseBtn.addActionListener(ev -> changeCollapse());

View File

@@ -878,4 +878,15 @@ public interface MenuEntrySwapperConfig extends Config
{
return false;
}
@ConfigItem(
keyName = "removeDeadNpcMenus",
name = "Remove dead npc menus",
description = "Remove menu options such as Attack and Talk-to from dead npcs",
section = npcSection
)
default boolean removeDeadNpcMenus()
{
return false;
}
}

View File

@@ -42,6 +42,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.inject.Inject;
@@ -55,10 +56,13 @@ import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.NpcID;
import net.runelite.api.ObjectComposition;
import net.runelite.api.ParamID;
import net.runelite.api.events.ClientTick;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.PostItemComposition;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
@@ -100,6 +104,9 @@ public class MenuEntrySwapperPlugin extends Plugin
private static final String ITEM_KEY_PREFIX = "item_";
private static final String OBJECT_KEY_PREFIX = "object_";
private static final String NPC_KEY_PREFIX = "npc_";
private static final String NPC_SHIFT_KEY_PREFIX = "npc_shift_";
private static final String WORN_ITEM_KEY_PREFIX = "wornitem_";
private static final String WORN_ITEM_SHIFT_KEY_PREFIX = "wornitem_shift_";
// Shift click
private static final WidgetMenuOption FIXED_INVENTORY_TAB_CONFIGURE_SC = new WidgetMenuOption(CONFIGURE,
@@ -522,6 +529,33 @@ public class MenuEntrySwapperPlugin extends Plugin
configManager.unsetConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + itemId);
}
private Integer getWornItemSwapConfig(boolean shift, int itemId)
{
itemId = ItemVariationMapping.map(itemId);
String config = configManager.getConfiguration(MenuEntrySwapperConfig.GROUP,
(shift ? WORN_ITEM_SHIFT_KEY_PREFIX : WORN_ITEM_KEY_PREFIX) + itemId);
if (config == null || config.isEmpty())
{
return null;
}
return Integer.parseInt(config);
}
private void setWornItemSwapConfig(boolean shift, int itemId, int index)
{
itemId = ItemVariationMapping.map(itemId);
configManager.setConfiguration(MenuEntrySwapperConfig.GROUP,
(shift ? WORN_ITEM_SHIFT_KEY_PREFIX : WORN_ITEM_KEY_PREFIX) + itemId, index);
}
private void unsetWornItemSwapConfig(boolean shift, int itemId)
{
itemId = ItemVariationMapping.map(itemId);
configManager.unsetConfiguration(MenuEntrySwapperConfig.GROUP,
(shift ? WORN_ITEM_SHIFT_KEY_PREFIX : WORN_ITEM_KEY_PREFIX) + itemId);
}
private void enableCustomization()
{
rebuildCustomizationMenus();
@@ -544,6 +578,7 @@ public class MenuEntrySwapperPlugin extends Plugin
{
configureObjectClick(event);
configureNpcClick(event);
configureWornItems(event);
return;
}
@@ -711,6 +746,26 @@ public class MenuEntrySwapperPlugin extends Plugin
}
}
private Consumer<MenuEntry> walkHereConsumer(boolean shift, NPCComposition composition)
{
return e ->
{
final String message = new ChatMessageBuilder()
.append("The default ").append(shift ? "shift" : "left").append(" click option for '").append(Text.removeTags(composition.getName())).append("' ")
.append("has been set to Walk here.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(message)
.build());
log.debug("Set npc {} click swap for {} to Walk here", shift ? "shift" : "left", composition.getId());
setNpcSwapConfig(shift, composition.getId(), -1);
};
}
private void configureNpcClick(MenuOpened event)
{
if (!shiftModifier() || !config.npcLeftClickCustomization())
@@ -729,18 +784,20 @@ public class MenuEntrySwapperPlugin extends Plugin
final NPC npc = entry.getNpc();
assert npc != null;
final NPCComposition composition = npc.getTransformedComposition();
assert composition != null;
final String[] actions = composition.getActions();
final Integer swapConfig = getNpcSwapConfig(composition.getId());
final Integer swapConfig = getNpcSwapConfig(false, composition.getId());
final boolean hasAttack = Arrays.stream(composition.getActions()).anyMatch("Attack"::equalsIgnoreCase);
final MenuAction currentAction = swapConfig != null ? NPC_MENU_TYPES.get(swapConfig) :
final MenuAction currentAction = swapConfig == null ?
// Attackable NPCs always have Attack as the first, last (deprioritized), or when hidden, no, option.
// Due to this the default action would be either Attack or the first non-Attack option, based on
// the game settings. Since it may be valid to swap an option up to override Attack, even when Attack
// is left-click, we cannot assume any default currentAction on attackable NPCs.
// Non-attackable NPCS have a predictable default action which we can prevent a swap to if no swap
// config is set, which just avoids showing a Swap option on a 1-op NPC, which looks odd.
(hasAttack ? null : defaultAction(composition));
(hasAttack ? null : defaultAction(composition)) :
(swapConfig == -1 ? MenuAction.WALK : NPC_MENU_TYPES.get(swapConfig));
for (int actionIdx = 0; actionIdx < NPC_MENU_TYPES.size(); ++actionIdx)
{
@@ -759,8 +816,7 @@ public class MenuEntrySwapperPlugin extends Plugin
continue;
}
if ("Pickpocket".equals(actions[actionIdx])
|| "Knock-Out".equals(actions[actionIdx])
if ("Knock-Out".equals(actions[actionIdx])
|| "Lure".equals(actions[actionIdx]))
{
// https://secure.runescape.com/m=news/another-message-about-unofficial-clients?oldschool=1
@@ -785,11 +841,24 @@ public class MenuEntrySwapperPlugin extends Plugin
log.debug("Set npc swap for {} to {}", composition.getId(), menuAction);
setNpcSwapConfig(composition.getId(), menuIdx);
setNpcSwapConfig(false, composition.getId(), menuIdx);
});
}
if (getNpcSwapConfig(composition.getId()) != null)
// Walk here swap
client.createMenuEntry(idx)
.setOption("Swap left click Walk here")
.setTarget(entry.getTarget())
.setType(MenuAction.RUNELITE)
.onClick(walkHereConsumer(false, composition));
client.createMenuEntry(idx)
.setOption("Swap shift click Walk here")
.setTarget(entry.getTarget())
.setType(MenuAction.RUNELITE)
.onClick(walkHereConsumer(true, composition));
if (getNpcSwapConfig(true, composition.getId()) != null || getNpcSwapConfig(false, composition.getId()) != null) // NOPMD: BrokenNullCheck
{
// Reset
client.createMenuEntry(idx)
@@ -799,8 +868,8 @@ public class MenuEntrySwapperPlugin extends Plugin
.onClick(e ->
{
final String message = new ChatMessageBuilder()
.append("The default left click option for '").append(Text.removeTags(composition.getName())).append("' ")
.append("has been reset.")
.append("The default left and shift click options for '").append(Text.removeTags(composition.getName())).append("' ")
.append("have been reset.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
@@ -809,13 +878,129 @@ public class MenuEntrySwapperPlugin extends Plugin
.build());
log.debug("Unset npc swap for {}", composition.getId());
unsetNpcSwapConfig(composition.getId());
unsetNpcSwapConfig(true, composition.getId());
unsetNpcSwapConfig(false, composition.getId());
});
}
}
}
}
private void configureWornItems(MenuOpened event)
{
if (!shiftModifier())
{
return;
}
final MenuEntry[] entries = event.getMenuEntries();
for (int idx = entries.length - 1; idx >= 0; --idx)
{
final MenuEntry entry = entries[idx];
Widget w = entry.getWidget();
if (w != null && WidgetInfo.TO_GROUP(w.getId()) == WidgetID.EQUIPMENT_GROUP_ID
&& "Examine".equals(entry.getOption()) && entry.getIdentifier() == 10)
{
w = w.getChild(1);
if (w != null && w.getItemId() > -1)
{
final ItemComposition itemComposition = itemManager.getItemComposition(w.getItemId());
final Integer leftClickOp = getWornItemSwapConfig(false, itemComposition.getId());
final Integer shiftClickOp = getWornItemSwapConfig(true, itemComposition.getId());
for (int paramId = ParamID.OC_ITEM_OP1, opId = 2; paramId <= ParamID.OC_ITEM_OP8; ++paramId, ++opId)
{
final String opName = itemComposition.getStringValue(paramId);
if (!Strings.isNullOrEmpty(opName))
{
if (leftClickOp == null || leftClickOp != opId)
{
client.createMenuEntry(idx)
.setOption("Swap left click " + opName)
.setTarget(entry.getTarget())
.setType(MenuAction.RUNELITE)
.onClick(wornItemConsumer(itemComposition, opName, opId, false));
}
if (shiftClickOp == null || shiftClickOp != opId)
{
client.createMenuEntry(idx)
.setOption("Swap shift click " + opName)
.setTarget(entry.getTarget())
.setType(MenuAction.RUNELITE)
.onClick(wornItemConsumer(itemComposition, opName, opId, true));
}
}
}
if (leftClickOp != null)
{
client.createMenuEntry(idx)
.setOption("Reset swap left click")
.setTarget(entry.getTarget())
.setType(MenuAction.RUNELITE)
.onClick(e ->
{
final String message = new ChatMessageBuilder()
.append("The default worn left click option for '").append(itemComposition.getName()).append("' ")
.append("has been reset.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(message)
.build());
log.debug("Unset worn item left swap for {}", itemComposition.getName());
unsetWornItemSwapConfig(false, itemComposition.getId());
});
}
if (shiftClickOp != null)
{
client.createMenuEntry(idx)
.setOption("Reset swap shift click")
.setTarget(entry.getTarget())
.setType(MenuAction.RUNELITE)
.onClick(e ->
{
final String message = new ChatMessageBuilder()
.append("The default worn shift click option for '").append(itemComposition.getName()).append("' ")
.append("has been reset.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(message)
.build());
log.debug("Unset worn item shift swap for {}", itemComposition.getName());
unsetWornItemSwapConfig(true, itemComposition.getId());
});
}
}
}
}
}
private Consumer<MenuEntry> wornItemConsumer(ItemComposition itemComposition, String opName, int opIdx, boolean shift)
{
return e ->
{
final String message = new ChatMessageBuilder()
.append("The default worn ").append(shift ? "shift" : "left").append(" click option for '").append(Text.removeTags(itemComposition.getName())).append("' ")
.append("has been set to '").append(opName).append("'.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(message)
.build());
log.debug("Set worn item {} swap for {} to {}", shift ? "shift" : "left", itemComposition.getName(), opIdx);
setWornItemSwapConfig(shift, itemComposition.getId(), opIdx);
};
}
private boolean swapBank(MenuEntry menuEntry, MenuAction type)
{
if (type != MenuAction.CC_OP && type != MenuAction.CC_OP_LOW_PRIORITY)
@@ -952,6 +1137,25 @@ public class MenuEntrySwapperPlugin extends Plugin
}
}
// Worn items swap
Widget w = menuEntry.getWidget();
if (w != null && WidgetInfo.TO_GROUP(w.getId()) == WidgetID.EQUIPMENT_GROUP_ID)
{
w = w.getChild(1);
if (w != null && w.getItemId() > -1)
{
final Integer wornItemSwapConfig = getWornItemSwapConfig(shiftModifier(), w.getItemId());
if (wornItemSwapConfig != null)
{
if (wornItemSwapConfig == menuEntry.getIdentifier())
{
swap(optionIndexes, menuEntries, index, menuEntries.length - 1);
}
return;
}
}
}
if (OBJECT_MENU_TYPES.contains(menuAction))
{
// Get multiloc id
@@ -981,24 +1185,33 @@ public class MenuEntrySwapperPlugin extends Plugin
assert npc != null;
final NPCComposition composition = npc.getTransformedComposition();
Integer customOption = getNpcSwapConfig(composition.getId());
Integer customOption = getNpcSwapConfig(shiftModifier(), composition.getId());
if (customOption != null)
{
MenuAction swapAction = NPC_MENU_TYPES.get(customOption);
if (swapAction == menuAction)
// Walk here swap
if (customOption == -1)
{
// Advance to the top-most op for this NPC. Normally menuEntries.length - 1 is examine, and swapping
// with that works due to it being sorted later, but if other plugins like NPC indicators add additional
// menus before examine that are also >1000, like RUNELITE menus, that would result in the >1000 menus being
// reordered relative to each other.
int i = index;
while (i < menuEntries.length - 1 && NPC_MENU_TYPES.contains(menuEntries[i + 1].getType()))
// we can achieve this by just deprioritizing the normal npc menus
menuEntry.setDeprioritized(true);
}
else
{
MenuAction swapAction = NPC_MENU_TYPES.get(customOption);
if (swapAction == menuAction)
{
++i;
}
// Advance to the top-most op for this NPC. Normally menuEntries.length - 1 is examine, and swapping
// with that works due to it being sorted later, but if other plugins like NPC indicators add additional
// menus before examine that are also >1000, like RUNELITE menus, that would result in the >1000 menus being
// reordered relative to each other.
int i = index;
while (i < menuEntries.length - 1 && NPC_MENU_TYPES.contains(menuEntries[i + 1].getType()))
{
++i;
}
swap(optionIndexes, menuEntries, index, i);
return;
swap(optionIndexes, menuEntries, index, i);
return;
}
}
}
}
@@ -1049,6 +1262,53 @@ public class MenuEntrySwapperPlugin extends Plugin
{
swapMenuEntry(menuEntries, idx++, entry);
}
if (config.removeDeadNpcMenus())
{
removeDeadNpcs();
}
}
private void removeDeadNpcs()
{
MenuEntry[] oldEntries = client.getMenuEntries();
MenuEntry[] newEntries = Arrays.stream(oldEntries)
.filter(e ->
{
final NPC npc = e.getNpc();
if (npc == null)
{
return true;
}
final int id = npc.getId();
switch (id)
{
// These NPCs hit 0hp but don't actually die
case NpcID.GARGOYLE:
case NpcID.GARGOYLE_413:
case NpcID.GARGOYLE_1543:
case NpcID.ZYGOMITE:
case NpcID.ZYGOMITE_1024:
case NpcID.ANCIENT_ZYGOMITE:
case NpcID.ROCKSLUG:
case NpcID.ROCKSLUG_422:
case NpcID.DESERT_LIZARD:
case NpcID.DESERT_LIZARD_460:
case NpcID.DESERT_LIZARD_461:
case NpcID.ICE_DEMON:
case NpcID.ICE_DEMON_7585:
return true;
default:
return !npc.isDead();
}
})
.toArray(MenuEntry[]::new);
if (oldEntries.length != newEntries.length)
{
client.setMenuEntries(newEntries);
}
}
@Subscribe
@@ -1278,9 +1538,10 @@ public class MenuEntrySwapperPlugin extends Plugin
return null;
}
private Integer getNpcSwapConfig(int npcId)
private Integer getNpcSwapConfig(boolean shift, int npcId)
{
String config = configManager.getConfiguration(MenuEntrySwapperConfig.GROUP, NPC_KEY_PREFIX + npcId);
String config = configManager.getConfiguration(MenuEntrySwapperConfig.GROUP,
(shift ? NPC_SHIFT_KEY_PREFIX : NPC_KEY_PREFIX) + npcId);
if (config == null || config.isEmpty())
{
return null;
@@ -1289,14 +1550,14 @@ public class MenuEntrySwapperPlugin extends Plugin
return Integer.parseInt(config);
}
private void setNpcSwapConfig(int npcId, int index)
private void setNpcSwapConfig(boolean shift, int npcId, int index)
{
configManager.setConfiguration(MenuEntrySwapperConfig.GROUP, NPC_KEY_PREFIX + npcId, index);
configManager.setConfiguration(MenuEntrySwapperConfig.GROUP, (shift ? NPC_SHIFT_KEY_PREFIX : NPC_KEY_PREFIX) + npcId, index);
}
private void unsetNpcSwapConfig(int npcId)
private void unsetNpcSwapConfig(boolean shift, int npcId)
{
configManager.unsetConfiguration(MenuEntrySwapperConfig.GROUP, NPC_KEY_PREFIX + npcId);
configManager.unsetConfiguration(MenuEntrySwapperConfig.GROUP, (shift ? NPC_SHIFT_KEY_PREFIX : NPC_KEY_PREFIX) + npcId);
}
private static MenuAction defaultAction(NPCComposition composition)

View File

@@ -78,11 +78,22 @@ public interface XpGlobesConfig extends Config
return true;
}
@ConfigItem(
keyName = "showTimeTilGoal",
name = "Show time til goal",
description = "Shows the amount of time until goal level in the globe tooltip box",
position = 4
)
default boolean showTimeTilGoal()
{
return true;
}
@ConfigItem(
keyName = "hideMaxed",
name = "Hide maxed skills",
description = "Stop globes from showing up for level 99 skills",
position = 4
position = 14
)
default boolean hideMaxed()
{
@@ -93,7 +104,7 @@ public interface XpGlobesConfig extends Config
keyName = "showVirtualLevel",
name = "Show virtual level",
description = "Shows virtual level if over 99 in a skill and Hide maxed skill is not checked",
position = 5
position = 15
)
default boolean showVirtualLevel()
{
@@ -104,7 +115,7 @@ public interface XpGlobesConfig extends Config
keyName = "enableCustomArcColor",
name = "Enable custom arc color",
description = "Enables the custom coloring of the globe's arc instead of using the skill's default color.",
position = 6
position = 16
)
default boolean enableCustomArcColor()
{
@@ -116,7 +127,7 @@ public interface XpGlobesConfig extends Config
keyName = "Progress arc color",
name = "Progress arc color",
description = "Change the color of the progress arc in the xp orb",
position = 7
position = 17
)
default Color progressArcColor()
{
@@ -128,7 +139,7 @@ public interface XpGlobesConfig extends Config
keyName = "Progress orb outline color",
name = "Progress orb outline color",
description = "Change the color of the progress orb outline",
position = 8
position = 18
)
default Color progressOrbOutLineColor()
{
@@ -140,7 +151,7 @@ public interface XpGlobesConfig extends Config
keyName = "Progress orb background color",
name = "Progress orb background color",
description = "Change the color of the progress orb background",
position = 9
position = 19
)
default Color progressOrbBackgroundColor()
{
@@ -151,7 +162,7 @@ public interface XpGlobesConfig extends Config
keyName = "Progress arc width",
name = "Progress arc width",
description = "Change the stroke width of the progress arc",
position = 10
position = 20
)
@Units(Units.PIXELS)
default int progressArcStrokeWidth()
@@ -163,7 +174,7 @@ public interface XpGlobesConfig extends Config
keyName = "Orb size",
name = "Size of orbs",
description = "Change the size of the xp orbs",
position = 11
position = 21
)
@Units(Units.PIXELS)
default int xpOrbSize()
@@ -175,7 +186,7 @@ public interface XpGlobesConfig extends Config
keyName = "Orb duration",
name = "Duration of orbs",
description = "Change the duration the xp orbs are visible",
position = 12
position = 22
)
@Units(Units.SECONDS)
default int xpOrbDuration()

View File

@@ -352,6 +352,16 @@ public class XpGlobesOverlay extends Overlay
.build());
}
}
if (config.showTimeTilGoal())
{
String timeLeft = xpTrackerService.getTimeTilGoal(mouseOverSkill.getSkill());
xpTooltip.getChildren().add(LineComponent.builder()
.left("Time left:")
.leftColor(Color.ORANGE)
.right(timeLeft)
.build());
}
}
tooltipManager.add(this.xpTooltip);

View File

@@ -204,7 +204,7 @@ class XpStateSingle
// return time remaining in hh:mm:ss or mm:ss format where hh can be > 24
if (durationHoursTotal > 0)
{
return String.format("%02d:%02d:%02d", durationHoursTotal, durationMinutes, durationSeconds);
return String.format("%d:%02d:%02d", durationHoursTotal, durationMinutes, durationSeconds);
}
// Minutes and seconds will always be present

View File

@@ -62,4 +62,9 @@ public interface XpTrackerService
* Get the amount of XP left until goal level
*/
int getEndGoalXp(Skill skill);
/**
* Get the amount of time left until goal level
*/
String getTimeTilGoal(Skill skill);
}

View File

@@ -80,4 +80,10 @@ class XpTrackerServiceImpl implements XpTrackerService
{
return plugin.getSkillSnapshot(skill).getEndGoalXp();
}
@Override
public String getTimeTilGoal(Skill skill)
{
return plugin.getSkillSnapshot(skill).getTimeTillGoalShort();
}
}