Merge pull request #698 from SomeoneWithAnInternetConnection/ge-squashed

GE Offer tracking plugin
This commit is contained in:
Adam
2018-02-25 22:07:56 -05:00
committed by GitHub
15 changed files with 729 additions and 3 deletions

View File

@@ -138,6 +138,8 @@ public interface Client extends GameEngine
HashTable getComponentTable();
GrandExchangeOffer[] getGrandExchangeOffers();
boolean isPrayerActive(Prayer prayer);
int getSkillExperience(Skill skill);

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.api;
public interface GrandExchangeOffer
{
int getQuantitySold();
int getItemId();
int getTotalQuantity();
int getPrice();
int getSpent();
GrandExchangeOfferState getState();
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.api;
/**
* Describes the state of a Grand Exchange offer
*/
public enum GrandExchangeOfferState
{
/**
* An empty slot.
*/
EMPTY,
/**
* Any offer that's been cancelled
*/
CANCELLED,
/**
* A buy offer that is currently in progress.
*/
BUYING,
/**
* A buy offer that has completed.
*/
BOUGHT,
/**
* A sell offer that is currently in progress.
*/
SELLING,
/**
* A sell offer that has completed.
*/
SOLD;
}

View File

@@ -74,4 +74,11 @@ public interface ItemComposition
* @return true if members-only, false otherwise.
*/
boolean isMembers();
/**
* Returns whether or not the item stacks in the players' inventories
*
* @return true if stackable, false otherwise
*/
boolean isStackable();
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.api.events;
import lombok.Data;
import net.runelite.api.GrandExchangeOffer;
@Data
public class GrandExchangeOfferChanged
{
private GrandExchangeOffer offer;
private int slot;
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.grandexchange;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import javax.annotation.Nullable;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.TitledBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GrandExchangeOfferState;
import static net.runelite.api.GrandExchangeOfferState.EMPTY;
import net.runelite.api.ItemComposition;
import net.runelite.client.game.ItemManager;
@Slf4j
public class GrandExchangeOfferSlot extends JPanel
{
private static final Color GE_INPROGRESS_ORANGE = new Color(0xd8, 0x80, 0x20).brighter();
private static final Color GE_FINISHED_GREEN = new Color(0, 0x5f, 0);
private static final Color GE_CANCELLED_RED = new Color(0x8f, 0, 0);
private static final String INFO_CARD = "INFO_CARD";
private static final String EMPTY_CARD = "EMPTY_CARD";
private final ItemManager itemManager;
private final CardLayout cardLayout = new CardLayout();
private final JLabel itemIcon = new JLabel();
private final TitledBorder itemName = BorderFactory.createTitledBorder("Nothing");
private final JLabel offerState = new JLabel("Text so the label has height");
private final JProgressBar progressBar = new JProgressBar();
/**
* This (sub)panel is used for each GE slot displayed
* in the sidebar
*/
GrandExchangeOfferSlot(ItemManager itemManager)
{
this.itemManager = itemManager;
buildPanel();
}
private void buildPanel()
{
setBorder(BorderFactory.createCompoundBorder(
// Add a margin underneath each slot panel to space them out
BorderFactory.createEmptyBorder(0, 0, 3, 0),
itemName
));
// The default border color is kind of dark, so we change it to something lighter
itemName.setBorder(BorderFactory.createLineBorder(getBackground().brighter()));
progressBar.setStringPainted(true);
setLayout(cardLayout);
// Card for when the slot has an offer in it
JPanel infoCard = new JPanel();
add(infoCard, INFO_CARD);
// Add padding to give the icon and progress bar room to breathe
infoCard.setBorder(BorderFactory.createEmptyBorder(0, 2, 2, 2));
infoCard.setLayout(new BoxLayout(infoCard, BoxLayout.X_AXIS));
// Icon on the left
infoCard.add(itemIcon);
// Info on the right
JPanel offerStatePanel = new JPanel();
offerStatePanel.setLayout(new BoxLayout(offerStatePanel, BoxLayout.Y_AXIS));
offerStatePanel.add(offerState);
offerStatePanel.add(progressBar);
infoCard.add(offerStatePanel);
// Card for when the slot is empty
JPanel emptySlotCard = new JPanel();
add(emptySlotCard, EMPTY_CARD);
// Counteract the height lost to the text at the top of the TitledBorder
int itemNameBorderHeight = itemName.getBorderInsets(this).top;
emptySlotCard.setBorder(BorderFactory.createEmptyBorder(0, 0, (itemNameBorderHeight - 1) / 2, 0));
// Center the "Empty" label horizontally
emptySlotCard.setLayout( new BoxLayout(emptySlotCard, BoxLayout.X_AXIS));
emptySlotCard.add(Box.createHorizontalGlue());
emptySlotCard.add(new JLabel(getNameForState(EMPTY)), BorderLayout.CENTER);
emptySlotCard.add(Box.createHorizontalGlue());
cardLayout.show(this, EMPTY_CARD);
}
void updateOffer(@Nullable GrandExchangeOffer newOffer)
{
if (newOffer == null || newOffer.getState() == EMPTY)
{
cardLayout.show(this, EMPTY_CARD);
itemName.setTitle("Nothing");
}
else
{
cardLayout.show(this, INFO_CARD);
ItemComposition offerItem = itemManager.getItemComposition(newOffer.getItemId());
itemName.setTitle(offerItem.getName());
boolean shouldStack = offerItem.isStackable() || newOffer.getTotalQuantity() > 1;
ImageIcon newItemIcon = new ImageIcon(itemManager.getImage(newOffer.getItemId(), newOffer.getTotalQuantity(), shouldStack));
itemIcon.setIcon(newItemIcon);
offerState.setText(getNameForState(newOffer.getState()) + " at " + newOffer.getPrice() + (newOffer.getTotalQuantity() > 1 ? "gp ea" : "gp"));
progressBar.setMaximum(newOffer.getTotalQuantity());
progressBar.setValue(newOffer.getQuantitySold());
progressBar.setBackground(getColorForState(newOffer.getState()));
progressBar.setString(newOffer.getQuantitySold() + "/" + newOffer.getTotalQuantity());
}
repaint();
}
private String getNameForState(GrandExchangeOfferState state)
{
switch (state)
{
case CANCELLED:
return "Cancelled";
case BUYING:
return "Buying";
case BOUGHT:
return "Bought";
case SELLING:
return "Selling";
case SOLD:
return "Sold";
case EMPTY:
default:
return "Empty";
}
}
private Color getColorForState(GrandExchangeOfferState state)
{
switch (state)
{
case CANCELLED:
return GE_CANCELLED_RED;
case BUYING:
case SELLING:
return GE_INPROGRESS_ORANGE;
case BOUGHT:
case SOLD:
return GE_FINISHED_GREEN;
case EMPTY:
default:
return null;
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.grandexchange;
import javax.inject.Inject;
import javax.swing.BoxLayout;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.PluginPanel;
@Slf4j
class GrandExchangePanel extends PluginPanel
{
private static final int MAX_OFFERS = 8;
private GrandExchangeOfferSlot[] offerSlotPanels = new GrandExchangeOfferSlot[MAX_OFFERS];
@Inject
GrandExchangePanel(ItemManager itemManager)
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
for (int i = 0; i < offerSlotPanels.length; ++i)
{
offerSlotPanels[i] = new GrandExchangeOfferSlot(itemManager);
add(offerSlotPanels[i]);
}
setVisible(true);
}
void updateOffer(GrandExchangeOffer newOffer, int slot)
{
offerSlotPanels[slot].updateOffer(newOffer);
}
}

View File

@@ -0,0 +1,78 @@
/*
*
* Copyright (c) 2017, Robbie <https://github.com/rbbi>
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.grandexchange;
import com.google.common.eventbus.Subscribe;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.swing.SwingUtilities;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton;
@PluginDescriptor(
name = "Grand Exchange offers"
)
public class GrandExchangePlugin extends Plugin
{
private NavigationButton button;
private GrandExchangePanel panel;
@Inject
private ClientUI ui;
@Override
protected void startUp() throws IOException
{
panel = injector.getInstance(GrandExchangePanel.class);
BufferedImage icon = ImageIO.read(getClass().getResourceAsStream("ge_icon.png"));
button = new NavigationButton("GE Offers", icon, () -> panel);
ui.getPluginToolbar().addNavigation(button);
}
@Override
protected void shutDown()
{
ui.getPluginToolbar().removeNavigation(button);
}
@Subscribe
public void onGrandExchangeOfferChanged(GrandExchangeOfferChanged offerEvent)
{
SwingUtilities.invokeLater(() ->
{
panel.updateOffer(offerEvent.getOffer(), offerEvent.getSlot());
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.grandexchange;
import java.awt.image.BufferedImage;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GrandExchangeOfferState;
import net.runelite.api.ItemComposition;
import net.runelite.client.game.ItemManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class GrandExchangeOfferSlotTest
{
@Mock
private ItemManager itemManager;
@Mock
private GrandExchangeOffer offer;
@Test
public void testUpdateOffer()
{
when(itemManager.getItemComposition(anyInt())).thenReturn(mock(ItemComposition.class));
when(itemManager.getImage(anyInt(), anyInt(), anyBoolean())).thenReturn(mock(BufferedImage.class));
when(offer.getState()).thenReturn(GrandExchangeOfferState.CANCELLED);
GrandExchangeOfferSlot offerSlot = new GrandExchangeOfferSlot(itemManager);
offerSlot.updateOffer(offer);
}
}

View File

@@ -29,6 +29,7 @@ import java.util.List;
import net.runelite.api.ChatMessageType;
import net.runelite.api.ClanMember;
import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.IndexedSprite;
import net.runelite.api.InventoryID;
import net.runelite.api.MenuAction;
@@ -44,6 +45,7 @@ import net.runelite.api.Skill;
import net.runelite.api.Varbits;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.api.events.MapRegionChanged;
import net.runelite.api.events.PlayerMenuOptionsChanged;
import net.runelite.api.events.ResizeableChanged;
@@ -483,6 +485,28 @@ public abstract class RSClientMixin implements RSClient
eventBus.post(gameStateChange);
}
@Inject
@FieldHook("grandExchangeOffers")
public static void onGrandExchangeOffersChanged(int idx)
{
if (idx == -1)
{
return;
}
GrandExchangeOffer internalOffer = client.getGrandExchangeOffers()[idx];
if (internalOffer == null)
{
return;
}
GrandExchangeOfferChanged offerChangedEvent = new GrandExchangeOfferChanged();
offerChangedEvent.setOffer(internalOffer);
offerChangedEvent.setSlot(idx);
eventBus.post(offerChangedEvent);
}
@FieldHook("settings")
@Inject
public static void settingsChanged(int idx)

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.mixins;
import net.runelite.api.GrandExchangeOfferState;
import static net.runelite.api.GrandExchangeOfferState.BOUGHT;
import static net.runelite.api.GrandExchangeOfferState.BUYING;
import static net.runelite.api.GrandExchangeOfferState.CANCELLED;
import static net.runelite.api.GrandExchangeOfferState.EMPTY;
import static net.runelite.api.GrandExchangeOfferState.SELLING;
import static net.runelite.api.GrandExchangeOfferState.SOLD;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.Mixin;
import net.runelite.rs.api.RSGrandExchangeOffer;
@Mixin(RSGrandExchangeOffer.class)
public abstract class RSGrandExchangeOfferMixin implements RSGrandExchangeOffer
{
/*
Internally a GrandExchangeOffer's state is represented as 4 flags
packed into the lower half of a byte. They are:
*/
//Set for sell offers, unset for buy offers
private static final int IS_SELLING = 1 << 3; // 0b1000
/*
Set for offers that have finished, either because they've
been filled, or because they were cancelled
*/
private static final int COMPLETED = 1 << 2; // 0b0100
/*
Set for offers that are actually live
NB: Insta-buy/sell offers will be simultaneously LIVE and LOCAL
*/
private static final int LIVE = 1 << 1; // 0b0010
//True for just-made, just-cancelled, completely cancelled, and completed offers
private static final int LOCAL = 1;
@Inject
@Override
public GrandExchangeOfferState getState()
{
byte code = getRSState();
boolean isSelling = (code & IS_SELLING) == IS_SELLING;
boolean isFinished = (code & COMPLETED) == COMPLETED;
if (code == 0)
{
return EMPTY;
}
else if (isFinished && getQuantitySold() < getTotalQuantity())
{
return CANCELLED;
}
else if (isSelling)
{
if (isFinished)
{
return SOLD;
}
else // if isUnfinished
{
return SELLING;
}
}
else // if isBuying
{
if (isFinished)
{
return BOUGHT;
}
else // if isUnfinished
{
return BUYING;
}
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.mixins;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.Mixin;
import net.runelite.rs.api.RSItemComposition;
@Mixin(RSItemComposition.class)
public abstract class RSItemCompositionMixin implements RSItemComposition
{
@Inject
@Override
public boolean isStackable()
{
return getIsStackable() != 0;
}
}

View File

@@ -24,25 +24,33 @@
*/
package net.runelite.rs.api;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.mapping.Import;
public interface RSGrandExchangeOffer
public interface RSGrandExchangeOffer extends GrandExchangeOffer
{
@Import("quantitySold")
@Override
int getQuantitySold();
@Import("itemId")
@Override
int getItemId();
@Import("totalQuantity")
@Override
int getTotalQuantity();
@Import("price")
@Override
int getPrice();
@Import("spent")
@Override
int getSpent();
@Import("progress")
byte getProgress();
@Import("state")
byte getRSState();
}

View File

@@ -58,6 +58,15 @@ public interface RSItemComposition extends ItemComposition
@Override
boolean isMembers();
/**
* You probably want {@link #isStackable}
* <p>
* This is the <b>{@code int}</b> that client code uses internally to represent this true/false value. It appears to only ever be set to 1 or 0
* @return 0 when this type of item isn't stackable, 1 otherwise
*/
@Import("isStackable")
int getIsStackable();
@Import("maleModel")
int getMaleModel();
}