idlenotifier: Inventory idle notification
This commit is contained in:
@@ -42,11 +42,22 @@ public interface IdleNotifierConfig extends Config
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "outOfItemsIdle",
|
||||
name = "Out of Items Idle Notifications",
|
||||
position = 2,
|
||||
description = "Configures if notifications for running out of items for another action are enabled."
|
||||
)
|
||||
default boolean outOfItemsIdle()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "animationidlesound",
|
||||
name = "Idle Animation Sound",
|
||||
description = "Plays a custom sound accompanying Idle Animation notifications",
|
||||
position = 2
|
||||
position = 3
|
||||
)
|
||||
default boolean animationIdleSound()
|
||||
{
|
||||
@@ -57,7 +68,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "interactionidle",
|
||||
name = "Idle Interaction Notifications",
|
||||
description = "Configures if idle interaction notifications are enabled e.g. combat, fishing",
|
||||
position = 3
|
||||
position = 4
|
||||
)
|
||||
default boolean interactionIdle()
|
||||
{
|
||||
@@ -68,7 +79,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "interactionidlesound",
|
||||
name = "Idle Interaction Sound",
|
||||
description = "Plays a custom sound accompanying Idle Interaction notifications",
|
||||
position = 4
|
||||
position = 5
|
||||
)
|
||||
default boolean interactionIdleSound()
|
||||
{
|
||||
@@ -79,7 +90,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "logoutidle",
|
||||
name = "Idle Logout Notifications",
|
||||
description = "Configures if the idle logout notifications are enabled",
|
||||
position = 5
|
||||
position = 6
|
||||
)
|
||||
default boolean logoutIdle()
|
||||
{
|
||||
@@ -90,7 +101,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "outofcombatsound",
|
||||
name = "Out of Combat Sound",
|
||||
description = "Plays a custom sound whenever you leave combat",
|
||||
position = 6
|
||||
position = 7
|
||||
)
|
||||
default boolean outOfCombatSound()
|
||||
{
|
||||
@@ -98,7 +109,7 @@ public interface IdleNotifierConfig extends Config
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 7,
|
||||
position = 8,
|
||||
keyName = "skullNotification",
|
||||
name = "Skull Notification",
|
||||
description = "Receive a notification when you skull."
|
||||
@@ -109,7 +120,7 @@ public interface IdleNotifierConfig extends Config
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 8,
|
||||
position = 9,
|
||||
keyName = "unskullNotification",
|
||||
name = "Unskull Notification",
|
||||
description = "Receive a notification when you unskull."
|
||||
@@ -123,7 +134,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "timeout",
|
||||
name = "Idle Notification Delay (ms)",
|
||||
description = "The notification delay after the player is idle",
|
||||
position = 9
|
||||
position = 10
|
||||
)
|
||||
default int getIdleNotificationDelay()
|
||||
{
|
||||
@@ -134,7 +145,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "hitpoints",
|
||||
name = "Hitpoints Notification Threshold",
|
||||
description = "The amount of hitpoints to send a notification at. A value of 0 will disable notification.",
|
||||
position = 10
|
||||
position = 11
|
||||
)
|
||||
default int getHitpointsThreshold()
|
||||
{
|
||||
@@ -156,7 +167,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "prayer",
|
||||
name = "Prayer Notification Threshold",
|
||||
description = "The amount of prayer points to send a notification at. A value of 0 will disable notification.",
|
||||
position = 12
|
||||
position = 13
|
||||
)
|
||||
default int getPrayerThreshold()
|
||||
{
|
||||
@@ -167,7 +178,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "playPrayerSound",
|
||||
name = "Play sound for Low Prayer",
|
||||
description = "Will play a sound for every Low Prayer notification sent",
|
||||
position = 13
|
||||
position = 14
|
||||
)
|
||||
default boolean getPlayPrayerSound()
|
||||
{
|
||||
@@ -177,7 +188,7 @@ public interface IdleNotifierConfig extends Config
|
||||
@ConfigItem(
|
||||
keyName = "oxygen",
|
||||
name = "Oxygen Notification Threshold",
|
||||
position = 14,
|
||||
position = 15,
|
||||
description = "The amount of remaining oxygen to send a notification at. A value of 0 will disable notification."
|
||||
)
|
||||
default int getOxygenThreshold()
|
||||
@@ -188,7 +199,7 @@ public interface IdleNotifierConfig extends Config
|
||||
@ConfigItem(
|
||||
keyName = "spec",
|
||||
name = "Special Attack Energy Notification Threshold",
|
||||
position = 15,
|
||||
position = 16,
|
||||
description = "The amount of spec energy reached to send a notification at. A value of 0 will disable notification."
|
||||
)
|
||||
default int getSpecEnergyThreshold()
|
||||
@@ -200,7 +211,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "specSound",
|
||||
name = "Special Attack Energy Sound",
|
||||
description = "Plays a custom sound accompanying Special Attack energy notifications",
|
||||
position = 16
|
||||
position = 17
|
||||
)
|
||||
default boolean getSpecSound()
|
||||
{
|
||||
@@ -211,7 +222,7 @@ public interface IdleNotifierConfig extends Config
|
||||
keyName = "overspec",
|
||||
name = "Over Special Energy Notification",
|
||||
description = "Will repeat notifications for any value over the special energy threshold",
|
||||
position = 17
|
||||
position = 18
|
||||
)
|
||||
default boolean getOverSpecEnergy()
|
||||
{
|
||||
@@ -221,7 +232,7 @@ public interface IdleNotifierConfig extends Config
|
||||
@ConfigItem(
|
||||
keyName = "pkers",
|
||||
name = "PKer Notifier",
|
||||
position = 18,
|
||||
position = 19,
|
||||
description = "Notifies if an attackable player based on your level range appears on screen.",
|
||||
group = "PvP",
|
||||
warning = "This will not notify you if the player is in your cc or is online on your friends list."
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.google.inject.Provides;
|
||||
import java.awt.TrayIcon;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@@ -44,6 +45,9 @@ import net.runelite.api.Constants;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.GraphicID;
|
||||
import net.runelite.api.Hitsplat;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NPCDefinition;
|
||||
import net.runelite.api.Player;
|
||||
@@ -58,6 +62,7 @@ import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.HitsplatApplied;
|
||||
import net.runelite.api.events.InteractingChanged;
|
||||
import net.runelite.api.events.ItemContainerChanged;
|
||||
import net.runelite.api.events.PlayerSpawned;
|
||||
import net.runelite.api.events.SpotAnimationChanged;
|
||||
import net.runelite.client.Notifier;
|
||||
@@ -117,6 +122,10 @@ public class IdleNotifierPlugin extends Plugin
|
||||
private int lastCombatCountdown = 0;
|
||||
private Instant sixHourWarningTime;
|
||||
private boolean ready;
|
||||
private Instant lastTimeItemsUsedUp;
|
||||
private List<Integer> itemIdsPrevious = new ArrayList<>();
|
||||
private List<Integer> itemQuantitiesPrevious = new ArrayList<>();
|
||||
private final List<Integer> itemQuantitiesChange = new ArrayList<>();
|
||||
private boolean lastInteractWasCombat;
|
||||
private SkullIcon lastTickSkull = null;
|
||||
private boolean isFirstTick = true;
|
||||
@@ -146,6 +155,7 @@ public class IdleNotifierPlugin extends Plugin
|
||||
private boolean getSpecSound;
|
||||
private boolean getOverSpecEnergy;
|
||||
private boolean notifyPkers;
|
||||
private boolean outOfItemsIdle;
|
||||
|
||||
@Provides
|
||||
IdleNotifierConfig provideConfig(ConfigManager configManager)
|
||||
@@ -299,6 +309,84 @@ public class IdleNotifierPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onItemContainerChanged(ItemContainerChanged event)
|
||||
{
|
||||
ItemContainer itemContainer = event.getItemContainer();
|
||||
|
||||
if (itemContainer != client.getItemContainer(InventoryID.INVENTORY) || !config.outOfItemsIdle())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Item[] items = itemContainer.getItems();
|
||||
ArrayList<Integer> itemQuantities = new ArrayList<>();
|
||||
ArrayList<Integer> itemIds = new ArrayList<>();
|
||||
|
||||
// Populate list of items in inventory without duplicates
|
||||
for (Item value : items)
|
||||
{
|
||||
int itemId = OutOfItemsMapping.mapFirst(value.getId());
|
||||
if (itemIds.indexOf(itemId) == -1) // -1 if item not yet in list
|
||||
{
|
||||
itemIds.add(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate quantity of each item in inventory
|
||||
for (int j = 0; j < itemIds.size(); j++)
|
||||
{
|
||||
itemQuantities.add(0);
|
||||
for (Item item : items)
|
||||
{
|
||||
if (itemIds.get(j) == OutOfItemsMapping.mapFirst(item.getId()))
|
||||
{
|
||||
itemQuantities.set(j, itemQuantities.get(j) + item.getQuantity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itemQuantitiesChange.clear();
|
||||
|
||||
// Calculate the quantity of each item consumed by the last action
|
||||
if (!itemIdsPrevious.isEmpty())
|
||||
{
|
||||
for (int i = 0; i < itemIdsPrevious.size(); i++)
|
||||
{
|
||||
int id = itemIdsPrevious.get(i);
|
||||
int currentIndex = itemIds.indexOf(id);
|
||||
int currentQuantity;
|
||||
if (currentIndex != -1) // -1 if item is no longer in inventory
|
||||
{
|
||||
currentQuantity = itemQuantities.get(currentIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentQuantity = 0;
|
||||
}
|
||||
itemQuantitiesChange.add(currentQuantity - itemQuantitiesPrevious.get(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIdsPrevious = itemIds;
|
||||
itemQuantitiesPrevious = itemQuantities;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check we have enough items left for another action.
|
||||
for (int i = 0; i < itemQuantitiesPrevious.size(); i++)
|
||||
{
|
||||
if (-itemQuantitiesChange.get(i) * 2 > itemQuantitiesPrevious.get(i))
|
||||
{
|
||||
lastTimeItemsUsedUp = Instant.now();
|
||||
return;
|
||||
}
|
||||
}
|
||||
itemIdsPrevious = itemIds;
|
||||
itemQuantitiesPrevious = itemQuantities;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onInteractingChanged(InteractingChanged event)
|
||||
{
|
||||
@@ -432,6 +520,7 @@ public class IdleNotifierPlugin extends Plugin
|
||||
|| client.getKeyboardIdleTicks() < 10)
|
||||
{
|
||||
resetTimers();
|
||||
resetOutOfItemsIdleChecks();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -445,6 +534,13 @@ public class IdleNotifierPlugin extends Plugin
|
||||
notifier.notify("[" + local.getName() + "] is about to log out from being online for 6 hours!");
|
||||
}
|
||||
|
||||
if (this.outOfItemsIdle && checkOutOfItemsIdle(waitDuration))
|
||||
{
|
||||
notifier.notify("[" + local.getName() + "] has run out of items!");
|
||||
// If this triggers, don't also trigger animation idle notification afterwards.
|
||||
lastAnimation = IDLE;
|
||||
}
|
||||
|
||||
if (this.animationIdle && checkAnimationIdle(waitDuration, local))
|
||||
{
|
||||
notifier.notify("[" + local.getName() + "] is now idle!");
|
||||
@@ -713,6 +809,23 @@ public class IdleNotifierPlugin extends Plugin
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkOutOfItemsIdle(Duration waitDuration)
|
||||
{
|
||||
if (lastTimeItemsUsedUp == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Instant.now().compareTo(lastTimeItemsUsedUp.plus(waitDuration)) >= 0)
|
||||
{
|
||||
resetTimers();
|
||||
resetOutOfItemsIdleChecks();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetTimers()
|
||||
{
|
||||
final Player local = client.getLocalPlayer();
|
||||
@@ -732,6 +845,14 @@ public class IdleNotifierPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
private void resetOutOfItemsIdleChecks()
|
||||
{
|
||||
lastTimeItemsUsedUp = null;
|
||||
itemQuantitiesChange.clear();
|
||||
itemIdsPrevious.clear();
|
||||
itemQuantitiesPrevious.clear();
|
||||
}
|
||||
|
||||
private void skullNotifier()
|
||||
{
|
||||
final Player local = client.getLocalPlayer();
|
||||
@@ -796,5 +917,6 @@ public class IdleNotifierPlugin extends Plugin
|
||||
this.getSpecSound = config.getSpecSound();
|
||||
this.getOverSpecEnergy = config.getOverSpecEnergy();
|
||||
this.notifyPkers = config.notifyPkers();
|
||||
this.outOfItemsIdle = config.outOfItemsIdle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Twiglet1022 <https://github.com/Twiglet1022>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.idlenotifier;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import java.util.Collection;
|
||||
import static net.runelite.api.ItemID.*;
|
||||
|
||||
public enum OutOfItemsMapping
|
||||
{
|
||||
AERIAL_FISHING_CUTTING(BLUEGILL, COMMON_TENCH, MOTTLED_EEL, GREATER_SIREN);
|
||||
|
||||
private static final Multimap<Integer, Integer> MAPPINGS = HashMultimap.create();
|
||||
private final int groupedItemKey;
|
||||
private final int[] groupedItemIDs;
|
||||
|
||||
static
|
||||
{
|
||||
for (final OutOfItemsMapping item : values())
|
||||
{
|
||||
for (int itemId : item.groupedItemIDs)
|
||||
{
|
||||
MAPPINGS.put(itemId, item.groupedItemKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutOfItemsMapping(int groupedItemKey, int... groupedItemIDs)
|
||||
{
|
||||
this.groupedItemKey = groupedItemKey;
|
||||
this.groupedItemIDs = groupedItemIDs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some actions consume multiple different items. To properly handle these
|
||||
* cases for the out of items notification the different items must be
|
||||
* recognised as belonging to a single group.
|
||||
*
|
||||
* Map an item that is part of a group of items that are consumed by a single
|
||||
* action to the first item in that group.
|
||||
*/
|
||||
public static int mapFirst(int itemId)
|
||||
{
|
||||
final Collection<Integer> mapping = MAPPINGS.get(itemId);
|
||||
|
||||
if (mapping == null || mapping.isEmpty())
|
||||
{
|
||||
return itemId;
|
||||
}
|
||||
|
||||
return mapping.iterator().next();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user