runelite-client: add world hopper plugin
Co-authored-by: Adam <Adam@anope.org> Co-authored-by: psikoi <ruben.amendoeira@gmail.com> Co-authored-by: Tomas Slusny <slusnucky@gmail.com>
This commit is contained in:
@@ -1434,7 +1434,14 @@ public interface Client extends GameEngine
|
||||
*/
|
||||
void setOculusOrbNormalSpeed(int speed);
|
||||
|
||||
/**
|
||||
* Opens in-game world hopper interface
|
||||
*/
|
||||
void openWorldHopper();
|
||||
|
||||
void hopToWorld(int world);
|
||||
/**
|
||||
* Hops using in-game world hopper widget to another world
|
||||
* @param world target world to hop to
|
||||
*/
|
||||
void hopToWorld(World world);
|
||||
}
|
||||
|
||||
@@ -382,7 +382,10 @@ public enum Varbits
|
||||
/**
|
||||
* Corp beast damage
|
||||
*/
|
||||
CORP_DAMAGE(999);
|
||||
CORP_DAMAGE(999),
|
||||
|
||||
WORLDHOPPER_FAVROITE_1(4597),
|
||||
WORLDHOPPER_FAVROITE_2(4598);
|
||||
|
||||
/**
|
||||
* The raw varbit ID.
|
||||
|
||||
@@ -431,7 +431,7 @@ public class ConfigManager
|
||||
|
||||
static Object stringToObject(String str, Class<?> type)
|
||||
{
|
||||
if (type == boolean.class)
|
||||
if (type == boolean.class || type == Boolean.class)
|
||||
{
|
||||
return Boolean.parseBoolean(str);
|
||||
}
|
||||
|
||||
@@ -27,17 +27,16 @@ package net.runelite.client.plugins.defaultworld;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Provides;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import javax.inject.Inject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.WorldType;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.SessionOpen;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.util.WorldUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldClient;
|
||||
import net.runelite.http.api.worlds.WorldResult;
|
||||
@@ -122,7 +121,7 @@ public class DefaultWorldPlugin extends Plugin
|
||||
rsWorld.setId(world.getId());
|
||||
rsWorld.setPlayerCount(world.getPlayers());
|
||||
rsWorld.setLocation(world.getLocation());
|
||||
rsWorld.setTypes(toWorldTypes(world.getTypes()));
|
||||
rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes()));
|
||||
|
||||
client.changeWorld(rsWorld);
|
||||
log.debug("Applied new world {}", correctedWorld);
|
||||
@@ -138,18 +137,6 @@ public class DefaultWorldPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
private static EnumSet<WorldType> toWorldTypes(final EnumSet<net.runelite.http.api.worlds.WorldType> apiTypes)
|
||||
{
|
||||
final EnumSet<WorldType> types = EnumSet.noneOf(WorldType.class);
|
||||
|
||||
for (net.runelite.http.api.worlds.WorldType apiType : apiTypes)
|
||||
{
|
||||
types.add(WorldType.valueOf(apiType.name()));
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private void applyWorld()
|
||||
{
|
||||
if (worldCache == 0)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.worldhopper;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Keybind;
|
||||
|
||||
@ConfigGroup(WorldHopperConfig.GROUP)
|
||||
public interface WorldHopperConfig extends Config
|
||||
{
|
||||
String GROUP = "worldhopper";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "previousKey",
|
||||
name = "Quick-hop previous",
|
||||
description = "When you press this key you'll hop to the previous world",
|
||||
position = 0
|
||||
)
|
||||
default Keybind previousKey()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "nextKey",
|
||||
name = "Quick-hop next",
|
||||
description = "When you press this key you'll hop to the next world",
|
||||
position = 1
|
||||
)
|
||||
default Keybind nextKey()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "quickhopOutOfDanger",
|
||||
name = "Quick-hop out of dangerous worlds",
|
||||
description = "Don't hop to a PVP/high risk world when quick-hopping",
|
||||
position = 2
|
||||
)
|
||||
default boolean quickhopOutOfDanger()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showSidebar",
|
||||
name = "Show world hopper sidebar",
|
||||
description = "Show sidebar containing all worlds that mimics in-game interface",
|
||||
position = 3
|
||||
)
|
||||
default boolean showSidebar()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.worldhopper;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.SwingUtilities;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.ChatPlayer;
|
||||
import net.runelite.api.ClanMember;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Friend;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.PlayerMenuOptionClicked;
|
||||
import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.chat.ChatColorType;
|
||||
import net.runelite.client.chat.ChatMessageBuilder;
|
||||
import net.runelite.client.chat.ChatMessageManager;
|
||||
import net.runelite.client.chat.QueuedMessage;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.ClientToolbar;
|
||||
import net.runelite.client.ui.NavigationButton;
|
||||
import net.runelite.client.util.HotkeyListener;
|
||||
import net.runelite.client.util.Text;
|
||||
import net.runelite.client.util.WorldUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldClient;
|
||||
import net.runelite.http.api.worlds.WorldResult;
|
||||
import net.runelite.http.api.worlds.WorldType;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "World Hopper",
|
||||
description = "Allows you to quickly hop worlds"
|
||||
)
|
||||
@Slf4j
|
||||
public class WorldHopperPlugin extends Plugin
|
||||
{
|
||||
private static final String HOP_TO = "Hop-to";
|
||||
private static final String KICK_OPTION = "Kick";
|
||||
private static final ImmutableList<String> BEFORE_OPTIONS = ImmutableList.of("Add friend", "Remove friend", KICK_OPTION);
|
||||
private static final ImmutableList<String> AFTER_OPTIONS = ImmutableList.of("Message");
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private ClientToolbar clientToolbar;
|
||||
|
||||
@Inject
|
||||
private KeyManager keyManager;
|
||||
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Inject
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
private WorldHopperConfig config;
|
||||
|
||||
private NavigationButton navButton;
|
||||
private WorldSwitcherPanel panel;
|
||||
|
||||
private int favoriteWorld1, favoriteWorld2;
|
||||
private Future<?> worldResultFuture;
|
||||
private WorldResult worldResult;
|
||||
private final HotkeyListener previousKeyListener = new HotkeyListener(() -> config.previousKey())
|
||||
{
|
||||
@Override
|
||||
public void hotkeyPressed()
|
||||
{
|
||||
hop(true);
|
||||
}
|
||||
};
|
||||
private final HotkeyListener nextKeyListener = new HotkeyListener(() -> config.nextKey())
|
||||
{
|
||||
@Override
|
||||
public void hotkeyPressed()
|
||||
{
|
||||
hop(false);
|
||||
}
|
||||
};
|
||||
|
||||
@Provides
|
||||
WorldHopperConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(WorldHopperConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
keyManager.registerKeyListener(previousKeyListener);
|
||||
keyManager.registerKeyListener(nextKeyListener);
|
||||
|
||||
worldResultFuture = executorService.submit(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
WorldResult worldResult = new WorldClient().lookupWorlds();
|
||||
|
||||
if (worldResult != null)
|
||||
{
|
||||
worldResult.getWorlds().sort(Comparator.comparingInt(World::getId));
|
||||
this.worldResult = worldResult;
|
||||
|
||||
SwingUtilities.invokeLater(() -> panel.populate(worldResult.getWorlds()));
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("Error looking up worlds", ex);
|
||||
}
|
||||
});
|
||||
|
||||
panel = new WorldSwitcherPanel(this);
|
||||
|
||||
final BufferedImage icon;
|
||||
synchronized (ImageIO.class)
|
||||
{
|
||||
icon = ImageIO.read(getClass().getResourceAsStream("icon.png"));
|
||||
}
|
||||
|
||||
navButton = NavigationButton.builder()
|
||||
.tooltip("World Switcher")
|
||||
.icon(icon)
|
||||
.priority(3)
|
||||
.panel(panel)
|
||||
.build();
|
||||
|
||||
if (config.showSidebar())
|
||||
{
|
||||
clientToolbar.addNavigation(navButton);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
keyManager.unregisterKeyListener(previousKeyListener);
|
||||
keyManager.unregisterKeyListener(nextKeyListener);
|
||||
|
||||
worldResultFuture.cancel(true);
|
||||
worldResultFuture = null;
|
||||
worldResult = null;
|
||||
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(final ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals(WorldHopperConfig.GROUP) && event.getKey().equals("showSidebar"))
|
||||
{
|
||||
if (config.showSidebar())
|
||||
{
|
||||
clientToolbar.addNavigation(navButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setFavoriteConfig(int world)
|
||||
{
|
||||
configManager.setConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, true);
|
||||
}
|
||||
|
||||
private boolean isFavoriteConfig(int world)
|
||||
{
|
||||
Boolean favorite = configManager.getConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, Boolean.class);
|
||||
return favorite != null && favorite;
|
||||
}
|
||||
|
||||
private void clearFavoriteConfig(int world)
|
||||
{
|
||||
configManager.unsetConfiguration(WorldHopperConfig.GROUP, "favorite_" + world);
|
||||
}
|
||||
|
||||
boolean isFavorite(World world)
|
||||
{
|
||||
int id = world.getId();
|
||||
return id == favoriteWorld1 || id == favoriteWorld2 || isFavoriteConfig(id);
|
||||
}
|
||||
|
||||
int getCurrentWorld()
|
||||
{
|
||||
return client.getWorld();
|
||||
}
|
||||
|
||||
void hopTo(World world)
|
||||
{
|
||||
hop(world.getId());
|
||||
}
|
||||
|
||||
void addToFavorites(World world)
|
||||
{
|
||||
log.debug("Adding world {} to favorites", world.getId());
|
||||
setFavoriteConfig(world.getId());
|
||||
}
|
||||
|
||||
void removeFromFavorites(World world)
|
||||
{
|
||||
log.debug("Removing world {} from favorites", world.getId());
|
||||
clearFavoriteConfig(world.getId());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onVarbitChanged(VarbitChanged varbitChanged)
|
||||
{
|
||||
int old1 = favoriteWorld1;
|
||||
int old2 = favoriteWorld2;
|
||||
|
||||
favoriteWorld1 = client.getVar(Varbits.WORLDHOPPER_FAVROITE_1);
|
||||
favoriteWorld2 = client.getVar(Varbits.WORLDHOPPER_FAVROITE_2);
|
||||
|
||||
if (old1 != favoriteWorld1 || old2 != favoriteWorld2)
|
||||
{
|
||||
SwingUtilities.invokeLater(panel::updateList);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuEntryAdded(MenuEntryAdded event)
|
||||
{
|
||||
int groupId = WidgetInfo.TO_GROUP(event.getActionParam1());
|
||||
String option = event.getOption();
|
||||
|
||||
if (groupId == WidgetInfo.FRIENDS_LIST.getGroupId() || groupId == WidgetInfo.CLAN_CHAT.getGroupId() ||
|
||||
groupId == WidgetInfo.CHATBOX.getGroupId() && !KICK_OPTION.equals(option) || //prevent from adding for Kick option (interferes with the raiding party one)
|
||||
groupId == WidgetInfo.RAIDING_PARTY.getGroupId() || groupId == WidgetInfo.PRIVATE_CHAT_MESSAGE.getGroupId())
|
||||
{
|
||||
boolean after;
|
||||
|
||||
if (AFTER_OPTIONS.contains(option))
|
||||
{
|
||||
after = true;
|
||||
}
|
||||
else if (BEFORE_OPTIONS.contains(option))
|
||||
{
|
||||
after = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't add entry if user is offline
|
||||
ChatPlayer player = getChatPlayerFromName(event.getTarget());
|
||||
|
||||
if (player == null || player.getWorld() == 0 || player.getWorld() == client.getWorld())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final MenuEntry hopTo = new MenuEntry();
|
||||
hopTo.setOption(HOP_TO);
|
||||
hopTo.setTarget(event.getTarget());
|
||||
hopTo.setType(MenuAction.RUNELITE.getId());
|
||||
hopTo.setParam0(event.getActionParam0());
|
||||
hopTo.setParam1(event.getActionParam1());
|
||||
|
||||
insertMenuEntry(hopTo, client.getMenuEntries(), after);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertMenuEntry(MenuEntry newEntry, MenuEntry[] entries, boolean after)
|
||||
{
|
||||
MenuEntry[] newMenu = ObjectArrays.concat(entries, newEntry);
|
||||
|
||||
if (after)
|
||||
{
|
||||
int menuEntryCount = newMenu.length;
|
||||
ArrayUtils.swap(newMenu, menuEntryCount - 1, menuEntryCount - 2);
|
||||
}
|
||||
|
||||
client.setMenuEntries(newMenu);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerMenuOptionClicked(PlayerMenuOptionClicked event)
|
||||
{
|
||||
if (!event.getMenuOption().equals(HOP_TO))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChatPlayer player = getChatPlayerFromName(event.getMenuTarget());
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
hop(player.getWorld());
|
||||
}
|
||||
}
|
||||
|
||||
private void hop(boolean previous)
|
||||
{
|
||||
if (worldResult == null || client.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
World currentWorld = worldResult.findWorld(client.getWorld());
|
||||
|
||||
if (currentWorld == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnumSet<WorldType> currentWorldTypes = currentWorld.getTypes().clone();
|
||||
// Make it so you always hop out of PVP and high risk worlds
|
||||
if (config.quickhopOutOfDanger())
|
||||
{
|
||||
currentWorldTypes.remove(WorldType.PVP);
|
||||
currentWorldTypes.remove(WorldType.PVP_HIGH_RISK);
|
||||
}
|
||||
// Don't regard skill total and bounty worlds as a type that must be hopped between
|
||||
currentWorldTypes.remove(WorldType.BOUNTY);
|
||||
currentWorldTypes.remove(WorldType.SKILL_TOTAL);
|
||||
// Allow hopping from a high risk world to a non-high risk world
|
||||
currentWorldTypes.remove(WorldType.PVP_HIGH_RISK);
|
||||
|
||||
List<World> worlds = worldResult.getWorlds();
|
||||
|
||||
int worldIdx = worlds.indexOf(currentWorld);
|
||||
int totalLevel = client.getTotalLevel();
|
||||
|
||||
World world;
|
||||
do
|
||||
{
|
||||
/*
|
||||
Get the previous or next world in the list,
|
||||
starting over at the other end of the list
|
||||
if there are no more elements in the
|
||||
current direction of iteration.
|
||||
*/
|
||||
if (previous)
|
||||
{
|
||||
worldIdx--;
|
||||
|
||||
if (worldIdx < 0)
|
||||
{
|
||||
worldIdx = worlds.size() - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
worldIdx++;
|
||||
|
||||
if (worldIdx >= worlds.size())
|
||||
{
|
||||
worldIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
world = worlds.get(worldIdx);
|
||||
|
||||
EnumSet<WorldType> types = world.getTypes().clone();
|
||||
|
||||
types.remove(WorldType.BOUNTY);
|
||||
|
||||
if (types.contains(WorldType.SKILL_TOTAL))
|
||||
{
|
||||
try
|
||||
{
|
||||
int totalRequirement = Integer.parseInt(world.getActivity().substring(0, world.getActivity().indexOf(" ")));
|
||||
|
||||
if (totalLevel >= totalRequirement)
|
||||
{
|
||||
types.remove(WorldType.SKILL_TOTAL);
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
log.warn("Failed to parse total level requirement for target world", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Break out if we've found a good world to hop to
|
||||
if (currentWorldTypes.equals(types))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (world != currentWorld);
|
||||
|
||||
if (world == currentWorld)
|
||||
{
|
||||
String chatMessage = new ChatMessageBuilder()
|
||||
.append(ChatColorType.NORMAL)
|
||||
.append("Couldn't find a world to quick-hop to.")
|
||||
.build();
|
||||
|
||||
chatMessageManager.queue(QueuedMessage.builder()
|
||||
.type(ChatMessageType.GAME)
|
||||
.runeLiteFormattedMessage(chatMessage)
|
||||
.build());
|
||||
}
|
||||
else
|
||||
{
|
||||
hop(world.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void hop(int worldId)
|
||||
{
|
||||
// Don't try to hop if the world doesn't exist
|
||||
World world = worldResult.findWorld(worldId);
|
||||
if (world == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final net.runelite.api.World rsWorld = client.createWorld();
|
||||
rsWorld.setActivity(world.getActivity());
|
||||
rsWorld.setAddress(world.getAddress());
|
||||
rsWorld.setId(world.getId());
|
||||
rsWorld.setPlayerCount(world.getPlayers());
|
||||
rsWorld.setLocation(world.getLocation());
|
||||
rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes()));
|
||||
|
||||
if (client.getGameState() == GameState.LOGIN_SCREEN)
|
||||
{
|
||||
// on the login screen we can just change the world by ourselves
|
||||
client.changeWorld(rsWorld);
|
||||
return;
|
||||
}
|
||||
|
||||
String chatMessage = new ChatMessageBuilder()
|
||||
.append(ChatColorType.NORMAL)
|
||||
.append("Quick-hopping to World ")
|
||||
.append(ChatColorType.HIGHLIGHT)
|
||||
.append(Integer.toString(world.getId()))
|
||||
.append(ChatColorType.NORMAL)
|
||||
.append("..")
|
||||
.build();
|
||||
|
||||
chatMessageManager
|
||||
.queue(QueuedMessage.builder()
|
||||
.type(ChatMessageType.GAME)
|
||||
.runeLiteFormattedMessage(chatMessage)
|
||||
.build());
|
||||
|
||||
clientThread.invokeLater(() ->
|
||||
{
|
||||
if (client.getWidget(WidgetInfo.WORLD_SWITCHER_LIST) == null)
|
||||
{
|
||||
client.openWorldHopper();
|
||||
return false;
|
||||
}
|
||||
|
||||
client.hopToWorld(rsWorld);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private ChatPlayer getChatPlayerFromName(String name)
|
||||
{
|
||||
String cleanName = Text.removeTags(name);
|
||||
|
||||
Friend[] friends = client.getFriends();
|
||||
|
||||
if (friends != null)
|
||||
{
|
||||
for (Friend friend : friends)
|
||||
{
|
||||
if (friend != null && friend.getName().equals(cleanName))
|
||||
{
|
||||
return friend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClanMember[] clanMembers = client.getClanMembers();
|
||||
|
||||
if (clanMembers != null)
|
||||
{
|
||||
for (ClanMember clanMember : clanMembers)
|
||||
{
|
||||
if (clanMember != null && clanMember.getUsername().equals(cleanName))
|
||||
{
|
||||
return clanMember;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/Psikoi>
|
||||
* 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.worldhopper;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.DynamicGridLayout;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
|
||||
@Slf4j
|
||||
class WorldSwitcherPanel extends PluginPanel
|
||||
{
|
||||
private static final Color ODD_ROW = new Color(44, 44, 44);
|
||||
|
||||
private static final int WORLD_COLUMN_WIDTH = 60;
|
||||
private static final int PLAYERS_COLUMN_WIDTH = 40;
|
||||
|
||||
private final JPanel listContainer = new JPanel();
|
||||
|
||||
private WorldTableHeader worldHeader;
|
||||
private WorldTableHeader playersHeader;
|
||||
private WorldTableHeader activityHeader;
|
||||
|
||||
private WorldOrder orderIndex = WorldOrder.WORLD;
|
||||
private boolean ascendingOrder = true;
|
||||
|
||||
private List<World> worlds;
|
||||
private WorldHopperPlugin plugin;
|
||||
|
||||
WorldSwitcherPanel(WorldHopperPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
|
||||
setBorder(null);
|
||||
setLayout(new DynamicGridLayout(0, 1));
|
||||
|
||||
JPanel headerContainer = buildHeader();
|
||||
|
||||
listContainer.setLayout(new GridLayout(0, 1));
|
||||
|
||||
add(headerContainer);
|
||||
add(listContainer);
|
||||
}
|
||||
|
||||
void updateList()
|
||||
{
|
||||
worlds.sort((w1, w2) ->
|
||||
{
|
||||
switch (orderIndex)
|
||||
{
|
||||
case WORLD:
|
||||
return Integer.compare(w1.getId(), w2.getId()) * (ascendingOrder ? 1 : -1);
|
||||
case PLAYERS:
|
||||
return Integer.compare(w1.getPlayers(), w2.getPlayers()) * (ascendingOrder ? 1 : -1);
|
||||
case ACTIVITY:
|
||||
return w1.getActivity().compareTo(w2.getActivity()) * (ascendingOrder ? 1 : -1);
|
||||
default:
|
||||
return 0;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
worlds.sort((w1, w2) ->
|
||||
{
|
||||
boolean b1 = plugin.isFavorite(w1);
|
||||
boolean b2 = plugin.isFavorite(w2);
|
||||
return Boolean.compare(b2, b1);
|
||||
});
|
||||
|
||||
listContainer.removeAll();
|
||||
|
||||
for (int i = 0; i < worlds.size(); i++)
|
||||
{
|
||||
World world = worlds.get(i);
|
||||
listContainer.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld(), plugin.isFavorite(world)));
|
||||
}
|
||||
|
||||
listContainer.revalidate();
|
||||
listContainer.repaint();
|
||||
}
|
||||
|
||||
void populate(List<World> worlds)
|
||||
{
|
||||
this.worlds = new ArrayList<>(worlds);
|
||||
updateList();
|
||||
}
|
||||
|
||||
private void orderBy(WorldOrder order)
|
||||
{
|
||||
worldHeader.highlight(false, ascendingOrder);
|
||||
playersHeader.highlight(false, ascendingOrder);
|
||||
activityHeader.highlight(false, ascendingOrder);
|
||||
|
||||
switch (order)
|
||||
{
|
||||
case WORLD:
|
||||
worldHeader.highlight(true, ascendingOrder);
|
||||
break;
|
||||
case PLAYERS:
|
||||
playersHeader.highlight(true, ascendingOrder);
|
||||
break;
|
||||
case ACTIVITY:
|
||||
activityHeader.highlight(true, ascendingOrder);
|
||||
break;
|
||||
}
|
||||
|
||||
orderIndex = order;
|
||||
updateList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the entire table header.
|
||||
*/
|
||||
private JPanel buildHeader()
|
||||
{
|
||||
JPanel header = new JPanel(new BorderLayout());
|
||||
JPanel leftSide = new JPanel(new BorderLayout());
|
||||
|
||||
worldHeader = new WorldTableHeader("World", orderIndex == WorldOrder.WORLD, ascendingOrder);
|
||||
worldHeader.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0));
|
||||
worldHeader.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
ascendingOrder = orderIndex != WorldOrder.WORLD || !ascendingOrder;
|
||||
orderBy(WorldOrder.WORLD);
|
||||
}
|
||||
});
|
||||
|
||||
playersHeader = new WorldTableHeader("#", orderIndex == WorldOrder.PLAYERS, ascendingOrder);
|
||||
playersHeader.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0));
|
||||
playersHeader.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
|
||||
ascendingOrder = orderIndex != WorldOrder.PLAYERS || !ascendingOrder;
|
||||
orderBy(WorldOrder.PLAYERS);
|
||||
}
|
||||
});
|
||||
|
||||
activityHeader = new WorldTableHeader("Activity", orderIndex == WorldOrder.ACTIVITY, ascendingOrder);
|
||||
activityHeader.setBorder(new EmptyBorder(3, 5, 3, 5));
|
||||
activityHeader.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
ascendingOrder = orderIndex != WorldOrder.ACTIVITY || !ascendingOrder;
|
||||
orderBy(WorldOrder.ACTIVITY);
|
||||
}
|
||||
});
|
||||
|
||||
leftSide.add(worldHeader, BorderLayout.WEST);
|
||||
leftSide.add(playersHeader, BorderLayout.EAST);
|
||||
|
||||
header.add(leftSide, BorderLayout.WEST);
|
||||
header.add(activityHeader, BorderLayout.CENTER);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row, that displays the world's information.
|
||||
*/
|
||||
private JPanel buildRow(World world, boolean stripe, boolean current, boolean favorite)
|
||||
{
|
||||
JPanel row = new WorldTableRow(world, current, favorite,
|
||||
world1 ->
|
||||
{
|
||||
plugin.hopTo(world1);
|
||||
},
|
||||
(world12, add) ->
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
plugin.addToFavorites(world12);
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.removeFromFavorites(world12);
|
||||
}
|
||||
|
||||
updateList();
|
||||
}
|
||||
);
|
||||
row.setBackground(stripe ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR);
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerates the multiple ordering options for the world list.
|
||||
*/
|
||||
private enum WorldOrder
|
||||
{
|
||||
WORLD,
|
||||
PLAYERS,
|
||||
ACTIVITY,
|
||||
PING
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/Psikoi>
|
||||
* 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.worldhopper;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
class WorldTableHeader extends JPanel
|
||||
{
|
||||
private static final ImageIcon ARROW_DOWN;
|
||||
private static final ImageIcon ARROW_UP;
|
||||
private static final ImageIcon ARROW_UP_FADED;
|
||||
|
||||
static
|
||||
{
|
||||
final BufferedImage arrowDown = ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "arrow_down.png");
|
||||
final BufferedImage arrowUp = ImageUtil.rotateImage(arrowDown, Math.PI);
|
||||
final BufferedImage arrowUpFaded = ImageUtil.grayscaleOffset(arrowUp, -80);
|
||||
ARROW_DOWN = new ImageIcon(arrowDown);
|
||||
ARROW_UP = new ImageIcon(arrowUp);
|
||||
ARROW_UP_FADED = new ImageIcon(arrowUpFaded);
|
||||
}
|
||||
|
||||
private final JLabel textLabel = new JLabel();
|
||||
private final JLabel arrowLabel = new JLabel();
|
||||
// Determines if this header column is being used to order the list
|
||||
private boolean ordering = false;
|
||||
|
||||
WorldTableHeader(String title, boolean ordered, boolean ascending)
|
||||
{
|
||||
setLayout(new BorderLayout(5, 0));
|
||||
setBorder(new CompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 0, 1, ColorScheme.MEDIUM_GRAY_COLOR),
|
||||
new EmptyBorder(0, 5, 0, 5)));
|
||||
setBackground(ColorScheme.SCROLL_TRACK_COLOR);
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
textLabel.setForeground(Color.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
if (ordering)
|
||||
{
|
||||
textLabel.setForeground(Color.WHITE);
|
||||
return;
|
||||
}
|
||||
|
||||
textLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
|
||||
}
|
||||
});
|
||||
|
||||
textLabel.setText(title);
|
||||
textLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
highlight(ordered, ascending);
|
||||
|
||||
add(textLabel, BorderLayout.WEST);
|
||||
add(arrowLabel, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
/**
|
||||
* The labels inherit the parent's mouse listeners.
|
||||
*/
|
||||
@Override
|
||||
public void addMouseListener(MouseListener mouseListener)
|
||||
{
|
||||
super.addMouseListener(mouseListener);
|
||||
textLabel.addMouseListener(mouseListener);
|
||||
arrowLabel.addMouseListener(mouseListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this column header is being used to order, then it should be
|
||||
* highlighted, changing it's font color and icon.
|
||||
*/
|
||||
public void highlight(boolean on, boolean ascending)
|
||||
{
|
||||
ordering = on;
|
||||
arrowLabel.setIcon(on ? (ascending ? ARROW_DOWN : ARROW_UP) : ARROW_UP_FADED);
|
||||
textLabel.setForeground(on ? Color.WHITE : ColorScheme.LIGHT_GRAY_COLOR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Psikoi <https://github.com/Psikoi>
|
||||
* 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.worldhopper;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldType;
|
||||
|
||||
class WorldTableRow extends JPanel
|
||||
{
|
||||
private static final ImageIcon FLAG_AUS;
|
||||
private static final ImageIcon FLAG_UK;
|
||||
private static final ImageIcon FLAG_US;
|
||||
private static final ImageIcon FLAG_GER;
|
||||
|
||||
private static final int WORLD_COLUMN_WIDTH = 60;
|
||||
private static final int PLAYERS_COLUMN_WIDTH = 40;
|
||||
|
||||
private static final Color CURRENT_WORLD = new Color(66, 227, 17);
|
||||
private static final Color UNAVAILABLE_WORLD = Color.GRAY.darker().darker();
|
||||
private static final Color DANGEROUS_WORLD = new Color(251, 62, 62);
|
||||
private static final Color TOURNAMENT_WORLD = new Color(79, 145, 255);
|
||||
private static final Color MEMBERS_WORLD = new Color(210, 193, 53);
|
||||
private static final Color FREE_WORLD = new Color(200, 200, 200);
|
||||
|
||||
static
|
||||
{
|
||||
FLAG_AUS = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_aus.png"));
|
||||
FLAG_UK = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_uk.png"));
|
||||
FLAG_US = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_us.png"));
|
||||
FLAG_GER = new ImageIcon(ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "flag_ger.png"));
|
||||
}
|
||||
|
||||
private Color lastBackground;
|
||||
private boolean current;
|
||||
|
||||
WorldTableRow(World world, boolean current, boolean favorite, Consumer<World> onSelect, BiConsumer<World, Boolean> onFavorite)
|
||||
{
|
||||
this.current = current;
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(2, 0, 2, 0));
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
if (onSelect != null)
|
||||
{
|
||||
onSelect.accept(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
setBackground(getBackground().brighter());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent mouseEvent)
|
||||
{
|
||||
if (mouseEvent.getClickCount() == 2)
|
||||
{
|
||||
setBackground(getBackground().darker());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
WorldTableRow.this.lastBackground = getBackground();
|
||||
setBackground(getBackground().brighter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
setBackground(lastBackground);
|
||||
}
|
||||
});
|
||||
|
||||
String favoriteAction = favorite ?
|
||||
"Remove " + world.getId() + " from favorites" :
|
||||
"Add " + world.getId() + " to favorites";
|
||||
|
||||
final JMenuItem fav = new JMenuItem(favoriteAction);
|
||||
fav.addActionListener(e ->
|
||||
{
|
||||
onFavorite.accept(world, !favorite);
|
||||
});
|
||||
|
||||
final JPopupMenu popupMenu = new JPopupMenu();
|
||||
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
popupMenu.add(fav);
|
||||
|
||||
setComponentPopupMenu(popupMenu);
|
||||
|
||||
JPanel leftSide = new JPanel(new BorderLayout());
|
||||
leftSide.setOpaque(false);
|
||||
|
||||
JPanel worldField = buildWorldField(world);
|
||||
worldField.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0));
|
||||
worldField.setOpaque(false);
|
||||
|
||||
JPanel playersField = buildPlayersField(world);
|
||||
playersField.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0));
|
||||
playersField.setOpaque(false);
|
||||
|
||||
JPanel activityField = buildActivityField(world);
|
||||
activityField.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
activityField.setOpaque(false);
|
||||
|
||||
leftSide.add(worldField, BorderLayout.WEST);
|
||||
leftSide.add(playersField, BorderLayout.EAST);
|
||||
|
||||
add(leftSide, BorderLayout.WEST);
|
||||
add(activityField, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the players list field (containing the amount of players logged in that world).
|
||||
*/
|
||||
private JPanel buildPlayersField(World world)
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
JLabel label = new JLabel(world.getPlayers() + "");
|
||||
label.setFont(FontManager.getRunescapeSmallFont());
|
||||
label.setForeground(current ? CURRENT_WORLD : Color.WHITE);
|
||||
|
||||
column.add(label, BorderLayout.WEST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the activity list field (containing that world's activity/theme).
|
||||
*/
|
||||
private JPanel buildActivityField(World world)
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout());
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
JLabel label = new JLabel(world.getActivity());
|
||||
label.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
if (current)
|
||||
{
|
||||
label.setForeground(CURRENT_WORLD);
|
||||
}
|
||||
else if (world.getTypes().contains(WorldType.PVP)
|
||||
|| world.getTypes().contains(WorldType.PVP_HIGH_RISK)
|
||||
|| world.getTypes().contains(WorldType.DEADMAN)
|
||||
|| world.getTypes().contains(WorldType.SEASONAL_DEADMAN))
|
||||
{
|
||||
label.setForeground(DANGEROUS_WORLD);
|
||||
}
|
||||
else if (world.getTypes().contains(WorldType.TOURNAMENT))
|
||||
{
|
||||
label.setForeground(TOURNAMENT_WORLD);
|
||||
}
|
||||
|
||||
column.add(label, BorderLayout.WEST);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the world list field (containing the country's flag and the world index).
|
||||
*/
|
||||
private JPanel buildWorldField(World world)
|
||||
{
|
||||
JPanel column = new JPanel(new BorderLayout(7, 0));
|
||||
column.setBorder(new EmptyBorder(0, 5, 0, 5));
|
||||
|
||||
JLabel label = new JLabel(world.getId() + "");
|
||||
|
||||
if (current)
|
||||
{
|
||||
label.setForeground(CURRENT_WORLD);
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setForeground(world.getTypes().contains(WorldType.MEMBERS) ? MEMBERS_WORLD : FREE_WORLD);
|
||||
}
|
||||
|
||||
JLabel flag = new JLabel(getFlag(world.getLocation()));
|
||||
|
||||
column.add(flag, BorderLayout.WEST);
|
||||
column.add(label, BorderLayout.CENTER);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
private ImageIcon getFlag(int locationId)
|
||||
{
|
||||
switch (locationId)
|
||||
{
|
||||
case 0:
|
||||
return FLAG_US;
|
||||
case 1:
|
||||
return FLAG_UK;
|
||||
case 3:
|
||||
return FLAG_AUS;
|
||||
default:
|
||||
return FLAG_GER;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||
* 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.util;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import net.runelite.api.WorldType;
|
||||
|
||||
/**
|
||||
* Utility class for RuneScape worlds
|
||||
*/
|
||||
public class WorldUtil
|
||||
{
|
||||
/**
|
||||
* Converts http-api world types to runelite-api world types
|
||||
* TODO: Find a better way to handle these to not have duplicate interfaces
|
||||
* @param apiTypes http-api world types
|
||||
* @return runelite-api world types
|
||||
*/
|
||||
public static EnumSet<WorldType> toWorldTypes(final EnumSet<net.runelite.http.api.worlds.WorldType> apiTypes)
|
||||
{
|
||||
final EnumSet<net.runelite.api.WorldType> types = EnumSet.noneOf(net.runelite.api.WorldType.class);
|
||||
|
||||
for (net.runelite.http.api.worlds.WorldType apiType : apiTypes)
|
||||
{
|
||||
types.add(net.runelite.api.WorldType.valueOf(apiType.name()));
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 89 B |
Binary file not shown.
|
After Width: | Height: | Size: 534 B |
Binary file not shown.
|
After Width: | Height: | Size: 433 B |
Binary file not shown.
|
After Width: | Height: | Size: 366 B |
Binary file not shown.
|
After Width: | Height: | Size: 589 B |
Binary file not shown.
|
After Width: | Height: | Size: 859 B |
@@ -25,6 +25,7 @@
|
||||
package net.runelite.mixins;
|
||||
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.World;
|
||||
import net.runelite.api.mixins.Inject;
|
||||
import net.runelite.api.mixins.Mixin;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
@@ -43,8 +44,9 @@ public abstract class WorldHoppingMixin implements RSClient
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public void hopToWorld(int world)
|
||||
public void hopToWorld(World world)
|
||||
{
|
||||
menuAction(world, WidgetInfo.WORLD_SWITCHER_LIST.getId(), MenuAction.WIDGET_DEFAULT.getId(), 1, "Switch", "<col=ff9040>" + (world - 300) + "</col>", 683, 244);
|
||||
final int worldId = world.getId();
|
||||
menuAction(worldId, WidgetInfo.WORLD_SWITCHER_LIST.getId(), MenuAction.WIDGET_DEFAULT.getId(), 1, "Switch", "<col=ff9040>" + (worldId - 300) + "</col>", 683, 244);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user