idlenotifier: Inventory idle notification

This commit is contained in:
Owain van Brakel
2019-07-12 02:04:16 +02:00
parent b697f986bd
commit 2e0af6122c
3 changed files with 226 additions and 16 deletions

View File

@@ -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."

View File

@@ -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();
}
}

View File

@@ -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();
}
}