Skip to content

Commit

Permalink
Greatly improved price tracking consistency, fixed edge cases, and ot…
Browse files Browse the repository at this point in the history
…her QoL (#18)

* -Fix- Fixed negative gold values being partially hidden by the gold icon by altering the number formatting

* -Fix- Fixed depositing or withdrawing items from the bank the same tick it is closed causing profit to change

* -Fix- Fixed filling or emptying container items like plank sack affecting profit

* -Fix- Fixed new tick perfect bank close prevention interfering with reward withdraw interfaces

* -Improvement- Added many storage items to ignore list for fill/empty/use

* -Improvement- Added open storage items to ignore list for fill/use

* -Fix- Modified backup logic for depositing items to more flexibly handle things like deposit boxes, imp-in-a-box, and others

* -Fix- Fixed rune pouch changes often not updating profit until something else happens

* Price tracking overhaul, stabilization, edge case fixes

-Fix- Minute by minute GE price changes of held and worn items will no longer interfere with profit
-Improvement- Included bank as a resource to monitor profit (emptying pouches, banking reward chests)
-Improvement- Added additional monitoring to ensure deposit box profit is accounted for
-Fix- Huntsmans kit interaction no longer interferes with profit
-Fix- Seed vault no longer interferes with profit
-Fix- Fixed opening bank interfaces and doing nothing causing the next change to be ignored
-Improvement- Added automatic start setting to begin tracking on first game tick
-Fix- Fixed readme pointing to broken image

* -Change- Changed number formatting to truncate instead of round, so something like 1,953 gold would show as 1.9K instead of 2.0K

* -Fix- Fixed new bank tracking causing bank close tick perfect race condition to happen again in reverse

-Fix- Fixed seed vault tick perfect close race condition

* Fix previous commit not removing code

* -Dev Internal- Rename dif to difference to avoid unnecessary abbreviations

-Dev Internal- Minor cleanup of various container item handling for potential future improvements
-Improvement- Changed essence pouches to storage only items that ignore profit changes
-Improvement- Added and fixed a few storage items to ensure profit tracking occurs

* Update ProfitTrackerPlugin.java

-Fix- Fixed new bank tracking holding onto previous bank state between toggling plugin
-Dev Internal- Fixed profit calculation debug message issue with concatenation instead of addition
-Dev Internal- Updated several instances of dif to difference
-Fix- Closed possibility of race condition when calculating profit in case prices can be updated in parallel
  • Loading branch information
NeilHarbin0 authored Dec 16, 2024
1 parent dcec5c2 commit 0d8e1ad
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 42 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<img src="https://oldschool.runescape.wiki/images/thumb/3/36/Coins_10000_detail.png/1024px-Coins_10000_detail.png?e07e3" width="200" title="hover text">
<img src="https://oldschool.runescape.wiki/images/Coins_detail.png?404bc" width="200" title="Coins 10000+">


# Profit Tracker Plugin
Expand All @@ -13,24 +13,25 @@ Depositing or withdrawing items will not affect profit value.


# Gold drops
Every change in your inventory is monitored and a corresponding profit animation will be shown.
Every change in your inventory and bank is monitored and a corresponding profit animation will be shown.
![image](https://user-images.githubusercontent.com/8212109/94357070-393c0680-009e-11eb-96a1-8fa7469ee6e1.png)

For example, if you buy in a general shop, an item for 20 coins, which is worth in GE 220 coins,
ProfitTracker will generate a gold drop animation of 200 coins.

# How to use
The plugin will simply begin tracking when it is loaded. So be sure to reload the plugin when you are starting your money routine!
The plugin will begin tracking when entering the game. Be sure to reload the plugin when you are starting a new money routine!

Certain actions will not be recorded as profit unless the bank has been opened at least once to create a baseline, and again to see changes. Things like using a deposit box to empty storage pouches, or banking items directly from reward chests. So make sure to open up the bank once for better accuracy before doing those things.

# Running the plugin from repo
Clone the repo, and run ProfitTrackerTest java class from Intellij.

# Missing features
I've developed this while being F2P.
I've developed this while being F2P.
There is no tracking of member stuff like tridents, dwarf cannon.

# Credits
Credit to wikiworm (Brandon Ripley) for his runelite plugin
https://github.com/wikiworm/InventoryValue
which helped for the creation of this plugin!

which helped for the creation of this plugin!
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies {
}

group = 'com.profittracker'
version = '1.4'
version = '1.5'
sourceCompatibility = '1.8'

tasks.withType(JavaCompile) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/profittracker/ProfitTrackerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,15 @@ default boolean goldDrops()
{
return true;
}

@ConfigItem(
keyName = "autoStart",
name = "Automatically start tracking",
description = "Automatically begin tracking profit on session start."
)
default boolean autoStart()
{
return true;
}
}

52 changes: 43 additions & 9 deletions src/main/java/com/profittracker/ProfitTrackerGoldDrops.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,22 +242,56 @@ private void resetXpDropTextColor(Widget xpDropTextWidget)

private String formatGoldDropText(long goldDropValue)
{
// format gold value runescape style
// up to 10,000K
// I.E: 100,000 -> 100K

if (Math.abs(goldDropValue) < 10000L)
{
// Format gold value to fit in xp drop to avoid being cutoff by gold sprite
// 999
// 1.0K
// 20K
// 300K
// 1.0M

float goldValueRep = goldDropValue;
String suffix = "";
boolean useDecimal = false;
if (Math.abs(goldDropValue) < 1000L) { // 1-999
return Long.toString(goldDropValue);
}
else if (Math.abs(goldDropValue) < 1000L * 1000L)
else if (Math.abs(goldDropValue) < 10000L) // 1,000-9,999
{
goldValueRep = (goldDropValue / 1000.0F);
suffix = "K";
useDecimal = true;
}
else if (Math.abs(goldDropValue) < 1000000L) // 10,000-999,999
{
return (goldDropValue / 1000) + "K";
goldValueRep = (goldDropValue / 1000.0F);
suffix = "K";
}
else if (Math.abs(goldDropValue) < 10000000L) // 1,000,000-9,999,999
{
goldValueRep = (goldDropValue / 1000000.0F);
suffix = "M";
useDecimal = true;
}
else if (Math.abs(goldDropValue) < 1000000000L) // 10,000,000-999,999,999
{
goldValueRep = (goldDropValue / 1000000.0F);
suffix = "M";
}
else if (Math.abs(goldDropValue) < 1000000000000L) // 1,000,000,000+
{
goldValueRep = (goldDropValue / 1000000000.0F);
suffix = "B";
useDecimal = true;
}
else
{
return "ALOT";
}

if(useDecimal)
{
return String.format("%.1f%s", Math.floor(goldValueRep * 10) / 10, suffix);
}else{
return String.format("%.0f%s", Math.floor(goldValueRep * 10) / 10, suffix);
}
}
}
115 changes: 109 additions & 6 deletions src/main/java/com/profittracker/ProfitTrackerInventoryValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.*;
import net.runelite.client.game.ItemManager;
import org.apache.commons.lang3.ArrayUtils;

import java.util.Arrays;
import java.util.*;
import java.util.stream.LongStream;

@Slf4j
Expand Down Expand Up @@ -70,7 +71,7 @@ private long calculateItemValue(Item item) {
if (Arrays.stream(RUNE_POUCH_ITEM_IDS).anyMatch(pouchID -> itemId == pouchID))
{
log.debug(String.format("calculateItemValue itemId = %d (Rune pouch variant)", itemId));
return calculateRunePouchValue();
return item.getQuantity() * calculateRunePouchValue();
}

log.debug(String.format("calculateItemValue itemId = %d", itemId));
Expand All @@ -96,14 +97,20 @@ public long calculateContainerValue(InventoryID ContainerID)

Item[] items = container.getItems();

newInventoryValue = Arrays.stream(items).flatMapToLong(item ->
return calculateItemValue(items);
}

/**
* Calculates the value of an array of items
* @param items
* @return
*/
public long calculateItemValue(Item[] items) {
return Arrays.stream(items).flatMapToLong(item ->
LongStream.of(calculateItemValue(item))
).sum();

return newInventoryValue;
}


public long calculateInventoryValue()
{
/*
Expand Down Expand Up @@ -157,5 +164,101 @@ public long calculateInventoryAndEquipmentValue()
return calculateInventoryValue() + calculateEquipmentValue();
}

/**
* Gets all items on the player, or null if inventory or equipment is null
* @return Array of items from inventory and equipment containers
*/
public Item[] getInventoryAndEquipmentContents(){
ItemContainer inventoryContainer = client.getItemContainer(InventoryID.INVENTORY);
ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT);


if (inventoryContainer == null || equipmentContainer == null)
{
return null;
}

Item[] inventoryItems = inventoryContainer.getItems();
Item[] equipmentItems = equipmentContainer.getItems();
Item[] personItems = ArrayUtils.addAll(inventoryItems,equipmentItems);
// Expand to have runes from pouch as individual items
return expandContainers(personItems);
}

public Item[] getBankContents(){
ItemContainer bankContainer = client.getItemContainer(InventoryID.BANK);

if (bankContainer == null)
{
return null;
}
return expandContainers(bankContainer.getItems());
}

private Item[] expandContainers(Item[] items){
Item[] extraItems = new Item[0];
for (int i = 0; i < items.length; i++){
final int lambdaId = items[i].getId(); // Compiler complains without this
if (Arrays.stream(RUNE_POUCH_ITEM_IDS).anyMatch(pouchID -> lambdaId == pouchID)){
extraItems = ArrayUtils.addAll(extraItems,getRunePouchItems());
items[i] = new Item(-1,0); // Get rid of pouch
break; //TODO Other containers
}
}
return ArrayUtils.addAll(items,extraItems);
}

private Item[] getRunePouchItems(){
List<Item> runes = new ArrayList<>();
EnumComposition runePouchEnum = client.getEnum(EnumID.RUNEPOUCH_RUNE);

for (int i = 0; i < RUNE_POUCH_AMOUNT_VARBITS.length; i++)
{
int itemID = runePouchEnum.getIntValue(client.getVarbitValue(RUNE_POUCH_RUNE_VARBITS[i]));
runes.add(new Item(itemID,client.getVarbitValue(RUNE_POUCH_AMOUNT_VARBITS[i])));
}

return runes.toArray(new Item[0]);
}

/**
* Compares the two arrays, returning an array of item differences
* For example, dropping a shark would be an array of 1 shark item, with quantity -1
* @param originalItems
* @param newItems
* @return
*/
public Item[] getItemCollectionDifference(Item[] originalItems, Item[] newItems){
//Iterate over each item, finding any instances of its existence from before
Item[] negativeItems = originalItems.clone();
for (int i = 0; i < originalItems.length; i++){
negativeItems[i] = new Item(originalItems[i].getId(),-originalItems[i].getQuantity());
}
Item[] itemIntermediateDifference = ArrayUtils.addAll(negativeItems,newItems);

//Create a nicer looking item list with only the actual changes
HashMap<Integer, Integer> itemDifferenceHash = new HashMap<>();

for (int i = 0; i < itemIntermediateDifference.length; i++){
int itemID = itemIntermediateDifference[i].getId();
itemDifferenceHash.putIfAbsent(itemID, 0);
itemDifferenceHash.put(itemID, itemDifferenceHash.get(itemID) + itemIntermediateDifference[i].getQuantity());
}

Iterator mapIt = itemDifferenceHash.entrySet().iterator();
while (mapIt.hasNext()){
Map.Entry pair = (Map.Entry)mapIt.next();
if ((Integer)(pair.getValue()) == 0){
mapIt.remove();
}
}
List<Item> itemDifference = new ArrayList<>();
mapIt = itemDifferenceHash.entrySet().iterator();
while (mapIt.hasNext()){
Map.Entry pair = (Map.Entry)mapIt.next();
itemDifference.add(new Item((Integer)pair.getKey(),(Integer)pair.getValue()));
}

return itemDifference.toArray(new Item[0]);
}
}
Loading

0 comments on commit 0d8e1ad

Please sign in to comment.