api: develop
This commit is contained in:
@@ -48,7 +48,7 @@ public class MenuEntry implements Cloneable
|
|||||||
/**
|
/**
|
||||||
* An identifier value for the target of the action.
|
* An identifier value for the target of the action.
|
||||||
*/
|
*/
|
||||||
private int type;
|
private int identifier;
|
||||||
/**
|
/**
|
||||||
* The action the entry will trigger.
|
* The action the entry will trigger.
|
||||||
* {@link MenuAction}
|
* {@link MenuAction}
|
||||||
@@ -74,7 +74,7 @@ public class MenuEntry implements Cloneable
|
|||||||
{
|
{
|
||||||
this.option = option;
|
this.option = option;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.type = type;
|
this.identifier = type;
|
||||||
this.opcode = opcode;
|
this.opcode = opcode;
|
||||||
this.actionParam0 = actionParam0;
|
this.actionParam0 = actionParam0;
|
||||||
this.actionParam1 = actionParam1;
|
this.actionParam1 = actionParam1;
|
||||||
@@ -104,6 +104,16 @@ public class MenuEntry implements Cloneable
|
|||||||
this.actionParam1 = i;
|
this.actionParam1 = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setType(int i)
|
||||||
|
{
|
||||||
|
this.opcode = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType()
|
||||||
|
{
|
||||||
|
return this.opcode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get opcode, but as it's enum counterpart
|
* Get opcode, but as it's enum counterpart
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -88,10 +88,25 @@ public class MenuOptionClicked extends MenuEntry implements Event
|
|||||||
{
|
{
|
||||||
setOption(e.getOption());
|
setOption(e.getOption());
|
||||||
setTarget(e.getTarget());
|
setTarget(e.getTarget());
|
||||||
setType(e.getType());
|
setIdentifier(e.getIdentifier());
|
||||||
setOpcode(e.getOpcode());
|
setOpcode(e.getOpcode());
|
||||||
setActionParam0(e.getActionParam0());
|
setActionParam0(e.getActionParam0());
|
||||||
setActionParam1(e.getActionParam1());
|
setActionParam1(e.getActionParam1());
|
||||||
setForceLeftClick(e.isForceLeftClick());
|
setForceLeftClick(e.isForceLeftClick());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getWidgetId()
|
||||||
|
{
|
||||||
|
return getActionParam1();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMenuTarget()
|
||||||
|
{
|
||||||
|
return getTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMenuOption()
|
||||||
|
{
|
||||||
|
return getOption();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class CombatLevelRequirement implements Requirement
|
||||||
|
{
|
||||||
|
private final int level;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return level + " " + "Combat";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean satisfiesRequirement(Client client)
|
||||||
|
{
|
||||||
|
return client.getLocalPlayer() != null && client.getLocalPlayer().getCombatLevel() >= level;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
class DiaryRequirement
|
||||||
|
{
|
||||||
|
private final String task;
|
||||||
|
private final List<Requirement> requirements;
|
||||||
|
|
||||||
|
DiaryRequirement(String task, Requirement[] requirements)
|
||||||
|
{
|
||||||
|
this.task = task;
|
||||||
|
this.requirements = ImmutableList.copyOf(requirements);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.FontTypeFace;
|
||||||
|
import net.runelite.api.ScriptID;
|
||||||
|
import net.runelite.api.events.WidgetLoaded;
|
||||||
|
import net.runelite.api.widgets.Widget;
|
||||||
|
import net.runelite.api.widgets.WidgetID;
|
||||||
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
|
import net.runelite.client.callback.ClientThread;
|
||||||
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.ArdougneDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.DesertDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.FaladorDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.FremennikDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.KandarinDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.KaramjaDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.KourendDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.LumbridgeDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.MorytaniaDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.VarrockDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.WesternDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.diaries.WildernessDiaryRequirement;
|
||||||
|
import net.runelite.client.util.Text;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@PluginDescriptor(
|
||||||
|
name = "Diary Requirements",
|
||||||
|
description = "Display level requirements in Achievement Diary interface",
|
||||||
|
tags = {"achievements", "tasks"}
|
||||||
|
)
|
||||||
|
public class DiaryRequirementsPlugin extends Plugin
|
||||||
|
{
|
||||||
|
private static final String AND_JOINER = ", ";
|
||||||
|
private static final Pattern AND_JOINER_PATTERN = Pattern.compile("(?<=, )");
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ClientThread clientThread;
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onWidgetLoaded(final WidgetLoaded event)
|
||||||
|
{
|
||||||
|
if (event.getGroupId() == WidgetID.DIARY_QUEST_GROUP_ID)
|
||||||
|
{
|
||||||
|
String widgetTitle = Text.removeTags(
|
||||||
|
client.getWidget(
|
||||||
|
WidgetInfo.DIARY_QUEST_WIDGET_TITLE)
|
||||||
|
.getText())
|
||||||
|
.replace(' ', '_')
|
||||||
|
.toUpperCase();
|
||||||
|
if (widgetTitle.startsWith("ACHIEVEMENT_DIARY"))
|
||||||
|
{
|
||||||
|
showDiaryRequirements();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDiaryRequirements()
|
||||||
|
{
|
||||||
|
Widget widget = client.getWidget(WidgetInfo.DIARY_QUEST_WIDGET_TEXT);
|
||||||
|
Widget[] children = widget.getStaticChildren();
|
||||||
|
|
||||||
|
Widget titleWidget = children[0];
|
||||||
|
if (titleWidget == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontTypeFace font = titleWidget.getFont();
|
||||||
|
int maxWidth = titleWidget.getWidth();
|
||||||
|
|
||||||
|
List<String> originalAchievements = getOriginalAchievements(children);
|
||||||
|
|
||||||
|
// new requirements starts out as a copy of the original
|
||||||
|
List<String> newRequirements = new ArrayList<>(originalAchievements);
|
||||||
|
|
||||||
|
GenericDiaryRequirement requirements = getRequirementsForTitle(titleWidget.getText());
|
||||||
|
if (requirements == null)
|
||||||
|
{
|
||||||
|
log.debug("Unknown achievement diary {}", titleWidget.getText());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> skillRequirements = buildRequirements(requirements.getRequirements());
|
||||||
|
if (skillRequirements == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
String taskBuffer = "";
|
||||||
|
for (int i = 0; i < originalAchievements.size(); i++)
|
||||||
|
{
|
||||||
|
String rowText = Text.removeTags(originalAchievements.get(i));
|
||||||
|
if (skillRequirements.get(taskBuffer + " " + rowText) != null)
|
||||||
|
{
|
||||||
|
taskBuffer = taskBuffer + " " + rowText;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
taskBuffer = rowText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skillRequirements.get(taskBuffer) != null)
|
||||||
|
{
|
||||||
|
String levelRequirement = skillRequirements.get(taskBuffer);
|
||||||
|
String task = originalAchievements.get(i);
|
||||||
|
|
||||||
|
int taskWidth = font.getTextWidth(task);
|
||||||
|
int ourWidth = font.getTextWidth(levelRequirement);
|
||||||
|
String strike = task.startsWith("<str>") ? "<str>" : "";
|
||||||
|
|
||||||
|
if (ourWidth + taskWidth < maxWidth)
|
||||||
|
{
|
||||||
|
// Merge onto 1 line
|
||||||
|
newRequirements.set(i + offset, task + levelRequirement);
|
||||||
|
}
|
||||||
|
else if (ourWidth < maxWidth)
|
||||||
|
{
|
||||||
|
// 2 line split
|
||||||
|
newRequirements.add(i + (++offset), strike + levelRequirement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Full text layout
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append(task);
|
||||||
|
int runningWidth = font.getTextWidth(b.toString());
|
||||||
|
for (String word : AND_JOINER_PATTERN.split(levelRequirement))
|
||||||
|
{
|
||||||
|
int wordWidth = font.getTextWidth(word);
|
||||||
|
if (runningWidth == 0 || wordWidth + runningWidth < maxWidth)
|
||||||
|
{
|
||||||
|
runningWidth += wordWidth;
|
||||||
|
b.append(word);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newRequirements.add(i + (offset++), b.toString());
|
||||||
|
b.delete(0, b.length());
|
||||||
|
runningWidth = wordWidth;
|
||||||
|
b.append(strike);
|
||||||
|
b.append(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRequirements.set(i + offset, b.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastLine = 0;
|
||||||
|
for (int i = 0; i < newRequirements.size() && i < children.length; i++)
|
||||||
|
{
|
||||||
|
Widget achievementWidget = children[i];
|
||||||
|
String text = newRequirements.get(i);
|
||||||
|
achievementWidget.setText(text);
|
||||||
|
if (text != null && !text.isEmpty())
|
||||||
|
{
|
||||||
|
lastLine = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numLines = lastLine;
|
||||||
|
clientThread.invokeLater(() -> client.runScript(ScriptID.DIARY_QUEST_UPDATE_LINECOUNT, 1, numLines));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getOriginalAchievements(Widget[] children)
|
||||||
|
{
|
||||||
|
List<String> preloadedRequirements = new ArrayList<>(children.length);
|
||||||
|
for (Widget requirementWidget : children)
|
||||||
|
{
|
||||||
|
preloadedRequirements.add(requirementWidget.getText());
|
||||||
|
}
|
||||||
|
return preloadedRequirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenericDiaryRequirement getRequirementsForTitle(String title)
|
||||||
|
{
|
||||||
|
String diaryName = Text.removeTags(title
|
||||||
|
.replaceAll(" ", "_")
|
||||||
|
.toUpperCase());
|
||||||
|
|
||||||
|
GenericDiaryRequirement diaryRequirementContainer;
|
||||||
|
switch (diaryName)
|
||||||
|
{
|
||||||
|
case "ARDOUGNE_AREA_TASKS":
|
||||||
|
diaryRequirementContainer = new ArdougneDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "DESERT_TASKS":
|
||||||
|
diaryRequirementContainer = new DesertDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "FALADOR_AREA_TASKS":
|
||||||
|
diaryRequirementContainer = new FaladorDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "FREMENNIK_TASKS":
|
||||||
|
diaryRequirementContainer = new FremennikDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "KANDARIN_TASKS":
|
||||||
|
diaryRequirementContainer = new KandarinDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "KARAMJA_AREA_TASKS":
|
||||||
|
diaryRequirementContainer = new KaramjaDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "KOUREND_&_KEBOS_TASKS":
|
||||||
|
diaryRequirementContainer = new KourendDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "LUMBRIDGE_&_DRAYNOR_TASKS":
|
||||||
|
diaryRequirementContainer = new LumbridgeDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "MORYTANIA_TASKS":
|
||||||
|
diaryRequirementContainer = new MorytaniaDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "VARROCK_TASKS":
|
||||||
|
diaryRequirementContainer = new VarrockDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "WESTERN_AREA_TASKS":
|
||||||
|
diaryRequirementContainer = new WesternDiaryRequirement();
|
||||||
|
break;
|
||||||
|
case "WILDERNESS_AREA_TASKS":
|
||||||
|
diaryRequirementContainer = new WildernessDiaryRequirement();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return diaryRequirementContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a map of task -> level requirements
|
||||||
|
private Map<String, String> buildRequirements(Collection<DiaryRequirement> requirements)
|
||||||
|
{
|
||||||
|
Map<String, String> reqs = new HashMap<>();
|
||||||
|
for (DiaryRequirement req : requirements)
|
||||||
|
{
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("<col=ffffff>(");
|
||||||
|
|
||||||
|
assert !req.getRequirements().isEmpty();
|
||||||
|
for (Requirement ireq : req.getRequirements())
|
||||||
|
{
|
||||||
|
boolean satifisfied = ireq.satisfiesRequirement(client);
|
||||||
|
b.append(satifisfied ? "<col=000080><str>" : "<col=800000>");
|
||||||
|
b.append(ireq.toString());
|
||||||
|
b.append(satifisfied ? "</str>" : "<col=000080>");
|
||||||
|
b.append(AND_JOINER);
|
||||||
|
}
|
||||||
|
|
||||||
|
b.delete(b.length() - AND_JOINER.length(), b.length());
|
||||||
|
|
||||||
|
b.append("<col=ffffff>)");
|
||||||
|
|
||||||
|
reqs.put(req.getTask(), b.toString());
|
||||||
|
}
|
||||||
|
return reqs;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 William <https://github.com/monsterxsync>
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Favour;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class FavourRequirement implements Requirement
|
||||||
|
{
|
||||||
|
private final Favour house;
|
||||||
|
private final int percent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return percent + "% " + house.getName() + " favour";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean satisfiesRequirement(Client client)
|
||||||
|
{
|
||||||
|
int realFavour = client.getVar(house.getVarbit());
|
||||||
|
return (realFavour / 10) >= percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public abstract class GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
@Getter
|
||||||
|
private Set<DiaryRequirement> requirements = new HashSet<>();
|
||||||
|
|
||||||
|
protected void add(String task, Requirement... requirements)
|
||||||
|
{
|
||||||
|
DiaryRequirement diaryRequirement = new DiaryRequirement(task, requirements);
|
||||||
|
this.requirements.add(diaryRequirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
|
||||||
|
public class OrRequirement implements Requirement
|
||||||
|
{
|
||||||
|
@Getter
|
||||||
|
private final List<Requirement> requirements;
|
||||||
|
|
||||||
|
public OrRequirement(Requirement... reqs)
|
||||||
|
{
|
||||||
|
this.requirements = ImmutableList.copyOf(reqs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return Joiner.on(" or ").join(requirements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean satisfiesRequirement(Client client)
|
||||||
|
{
|
||||||
|
for (Requirement r : getRequirements())
|
||||||
|
{
|
||||||
|
if (r.satisfiesRequirement(client))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.VarPlayer;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class QuestPointRequirement implements Requirement
|
||||||
|
{
|
||||||
|
private final int qp;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return qp + " " + "Quest points";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean satisfiesRequirement(Client client)
|
||||||
|
{
|
||||||
|
return client.getVar(VarPlayer.QUEST_POINTS) >= qp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.QuestState;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class QuestRequirement implements Requirement
|
||||||
|
{
|
||||||
|
private final Quest quest;
|
||||||
|
private final boolean started;
|
||||||
|
|
||||||
|
public QuestRequirement(Quest quest)
|
||||||
|
{
|
||||||
|
this(quest, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
if (started)
|
||||||
|
{
|
||||||
|
return "Started " + quest.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return quest.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean satisfiesRequirement(Client client)
|
||||||
|
{
|
||||||
|
QuestState questState = quest.getState(client);
|
||||||
|
if (started)
|
||||||
|
{
|
||||||
|
return questState != QuestState.NOT_STARTED;
|
||||||
|
}
|
||||||
|
return questState == QuestState.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
|
||||||
|
public interface Requirement
|
||||||
|
{
|
||||||
|
boolean satisfiesRequirement(Client client);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.achievementdiary;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class SkillRequirement implements Requirement
|
||||||
|
{
|
||||||
|
private final Skill skill;
|
||||||
|
private final int level;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return level + " " + skill.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean satisfiesRequirement(Client client)
|
||||||
|
{
|
||||||
|
return client.getRealSkillLevel(skill) >= level;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class ArdougneDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public ArdougneDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Have Wizard Cromperty teleport you to the Rune Essence mine.",
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Steal a cake from the Ardougne market stalls.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 5));
|
||||||
|
add("Enter the Combat Training Camp north of W. Ardougne.",
|
||||||
|
new QuestRequirement(Quest.BIOHAZARD));
|
||||||
|
add("Go out fishing on the Fishing Trawler.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 15));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Enter the Unicorn pen in Ardougne zoo using Fairy rings.",
|
||||||
|
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
|
||||||
|
add("Grapple over Yanille's south wall.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 39),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 38),
|
||||||
|
new SkillRequirement(Skill.RANGED, 21));
|
||||||
|
add("Harvest some strawberries from the Ardougne farming patch.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 31));
|
||||||
|
add("Cast the Ardougne Teleport spell.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 51),
|
||||||
|
new QuestRequirement(Quest.PLAGUE_CITY));
|
||||||
|
add("Travel to Castlewars by Hot Air Balloon.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 50),
|
||||||
|
new QuestRequirement(Quest.ENLIGHTENED_JOURNEY));
|
||||||
|
add("Claim buckets of sand from Bert in Yanille.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 49),
|
||||||
|
new QuestRequirement(Quest.THE_HAND_IN_THE_SAND));
|
||||||
|
add("Catch any fish on the Fishing Platform.",
|
||||||
|
new QuestRequirement(Quest.SEA_SLUG, true));
|
||||||
|
add("Pickpocket the master farmer north of Ardougne.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 38));
|
||||||
|
add("Collect some Nightshade from the Skavid Caves.",
|
||||||
|
new QuestRequirement(Quest.WATCHTOWER, true));
|
||||||
|
add("Kill a swordchick in the Tower of Life.",
|
||||||
|
new QuestRequirement(Quest.TOWER_OF_LIFE));
|
||||||
|
add("Equip Iban's upgraded staff or upgrade an Iban staff.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 50),
|
||||||
|
new SkillRequirement(Skill.ATTACK, 50),
|
||||||
|
new QuestRequirement(Quest.UNDERGROUND_PASS));
|
||||||
|
add("Visit the Island East of the Necromancer's tower.",
|
||||||
|
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
// When the task is completed "the Totem" changes to "Totem" - so we add
|
||||||
|
// both variations.
|
||||||
|
add("Recharge some Jewellery at the Totem in the Legends Guild.",
|
||||||
|
new QuestRequirement(Quest.LEGENDS_QUEST));
|
||||||
|
add("Recharge some Jewellery at Totem in the Legends Guild.",
|
||||||
|
new QuestRequirement(Quest.LEGENDS_QUEST));
|
||||||
|
add("Enter the Magic Guild.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 66));
|
||||||
|
add("Attempt to steal from a chest in Ardougne Castle.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 72));
|
||||||
|
add("Have a zookeeper put you in Ardougne Zoo's monkey cage.",
|
||||||
|
new QuestRequirement(Quest.MONKEY_MADNESS_I, true));
|
||||||
|
add("Teleport to the Watchtower.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 58),
|
||||||
|
new QuestRequirement(Quest.WATCHTOWER));
|
||||||
|
add("Catch a Red Salamander.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 59));
|
||||||
|
add("Check the health of a Palm tree near tree gnome village.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 68));
|
||||||
|
add("Pick some Poison Ivy berries from the patch south of Ardougne.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 70));
|
||||||
|
add("Smith a Mithril platebody near Ardougne.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 68));
|
||||||
|
add("Enter your POH from Yanille.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 50));
|
||||||
|
add("Smith a Dragon sq shield in West Ardougne.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 60),
|
||||||
|
new QuestRequirement(Quest.LEGENDS_QUEST));
|
||||||
|
add("Craft some Death runes.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 65),
|
||||||
|
new QuestRequirement(Quest.MOURNINGS_END_PART_II));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Catch a Manta ray in the Fishing Trawler and cook it in Port Khazard.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 81),
|
||||||
|
new SkillRequirement(Skill.COOKING, 91)
|
||||||
|
);
|
||||||
|
add("Attempt to picklock the door to the basement of Yanille Agility Dungeon.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 82));
|
||||||
|
add("Pickpocket a Hero.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 80));
|
||||||
|
add("Make a rune crossbow yourself from scratch within Witchaven or Yanille.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 10),
|
||||||
|
new SkillRequirement(Skill.SMITHING, 91),
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 69));
|
||||||
|
add("Imbue a salve amulet at Nightmare Zone or equip an imbued salve amulet.",
|
||||||
|
new QuestRequirement(Quest.HAUNTED_MINE));
|
||||||
|
add("Pick some Torstol from the patch north of Ardougne.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 85));
|
||||||
|
add("Complete a lap of Ardougne's rooftop agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 90));
|
||||||
|
add("Cast Ice Barrage on another player within Castlewars.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 94),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class DesertDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public DesertDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Catch a Golden Warbler.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 5));
|
||||||
|
add("Mine 5 clay in the north-eastern desert.",
|
||||||
|
new SkillRequirement(Skill.MINING, 5));
|
||||||
|
add("Open the Sarcophagus in the first room of Pyramid Plunder.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 21),
|
||||||
|
new QuestRequirement(Quest.ICTHLARINS_LITTLE_HELPER, true));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Climb to the summit of the Agility Pyramid.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 30));
|
||||||
|
add("Slay a desert lizard.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 22));
|
||||||
|
add("Catch an Orange Salamander.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 47));
|
||||||
|
add("Steal a feather from the Desert Phoenix.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 25));
|
||||||
|
add("Travel to Uzer via Magic Carpet.",
|
||||||
|
new QuestRequirement(Quest.THE_GOLEM));
|
||||||
|
add("Travel to the Desert via Eagle.",
|
||||||
|
new QuestRequirement(Quest.EAGLES_PEAK));
|
||||||
|
add("Pray at the Elidinis statuette in Nardah.",
|
||||||
|
new QuestRequirement(Quest.SPIRITS_OF_THE_ELID));
|
||||||
|
add("Create a combat potion in the desert.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 36));
|
||||||
|
add("Teleport to Enakhra's Temple with the Camulet.",
|
||||||
|
new QuestRequirement(Quest.ENAKHRAS_LAMENT));
|
||||||
|
add("Visit the Genie.",
|
||||||
|
new QuestRequirement(Quest.SPIRITS_OF_THE_ELID));
|
||||||
|
add("Teleport to Pollnivneach with a redirected teleport to house tablet.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 20));
|
||||||
|
add("Chop some Teak logs near Uzer.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 35));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Knock out and pickpocket a Menaphite Thug.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 65),
|
||||||
|
new QuestRequirement(Quest.THE_FEUD));
|
||||||
|
add("Mine some Granite.",
|
||||||
|
new SkillRequirement(Skill.MINING, 45));
|
||||||
|
add("Refill your waterskins in the Desert using Lunar magic.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 68),
|
||||||
|
new QuestRequirement(Quest.DREAM_MENTOR));
|
||||||
|
add("Complete a lap of the Pollnivneach agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 70));
|
||||||
|
add("Slay a Dust Devil with a Slayer helmet equipped.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 65),
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 10),
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 55),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE, true));
|
||||||
|
add("Activate Ancient Magicks at the altar in the Jaldraocht Pyramid.",
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE));
|
||||||
|
add("Defeat a Locust Rider with Keris.",
|
||||||
|
new SkillRequirement(Skill.ATTACK, 50),
|
||||||
|
new QuestRequirement(Quest.CONTACT));
|
||||||
|
add("Burn some yew logs on the Nardah Mayor's balcony.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 60));
|
||||||
|
add("Create a Mithril Platebody in Nardah.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 68));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Bake a wild pie at the Nardah Clay Oven.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 85));
|
||||||
|
add("Cast Ice Barrage against a foe in the Desert.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 94),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE));
|
||||||
|
add("Fletch some Dragon darts at the Bedabin Camp.",
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 95),
|
||||||
|
new QuestRequirement(Quest.THE_TOURIST_TRAP));
|
||||||
|
add("Speak to the KQ head in your POH.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 78),
|
||||||
|
new QuestRequirement(Quest.PRIEST_IN_PERIL));
|
||||||
|
add("Steal from the Grand Gold Chest in the final room of Pyramid Plunder.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 91),
|
||||||
|
new QuestRequirement(Quest.ICTHLARINS_LITTLE_HELPER, true));
|
||||||
|
add("Restore at least 85 Prayer points when praying at the Altar in Sophanem.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 85),
|
||||||
|
new QuestRequirement(Quest.ICTHLARINS_LITTLE_HELPER, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class FaladorDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public FaladorDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Find out what your family crest is from Sir Renitee.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 16));
|
||||||
|
add("Climb over the western Falador wall.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 5));
|
||||||
|
add("Make a mind tiara.",
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Smith some Blurite Limbs on Doric's Anvil.",
|
||||||
|
new SkillRequirement(Skill.MINING, 10),
|
||||||
|
new SkillRequirement(Skill.SMITHING, 13),
|
||||||
|
new QuestRequirement(Quest.THE_KNIGHTS_SWORD),
|
||||||
|
new QuestRequirement(Quest.DORICS_QUEST));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Light a Bullseye lantern at the Chemist's in Rimmington.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 49));
|
||||||
|
add("Telegrab some Wine of Zamorak at the Chaos Temple by the Wilderness.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 33));
|
||||||
|
add("Place a Scarecrow in the Falador farming patch.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 23));
|
||||||
|
add("Kill a Mogre at Mudskipper Point.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 32),
|
||||||
|
new QuestRequirement(Quest.SKIPPY_AND_THE_MOGRES));
|
||||||
|
add("Visit the Port Sarim Rat Pits.",
|
||||||
|
new QuestRequirement(Quest.RATCATCHERS, true));
|
||||||
|
add("Grapple up and then jump off the north Falador wall.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 11),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 37),
|
||||||
|
new SkillRequirement(Skill.RANGED, 19));
|
||||||
|
add("Pickpocket a Falador guard.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 40));
|
||||||
|
add("Pray at the Altar of Guthix in Taverley whilst wearing full Initiate.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 10),
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 20),
|
||||||
|
new QuestRequirement(Quest.RECRUITMENT_DRIVE));
|
||||||
|
add("Mine some Gold ore at the Crafting Guild.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 40),
|
||||||
|
new SkillRequirement(Skill.MINING, 40));
|
||||||
|
add("Squeeze through the crevice in the Dwarven mines.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 42));
|
||||||
|
add("Chop and burn some Willow logs in Taverley",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 30),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 30));
|
||||||
|
add("Craft a fruit basket on the Falador Farm loom.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 36));
|
||||||
|
add("Teleport to Falador.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 37));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Craft 140 Mind runes simultaneously.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 56));
|
||||||
|
add("Change your family crest to the Saradomin symbol.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 70));
|
||||||
|
add("Kill a Skeletal Wyvern in the Asgarnia Ice Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 72));
|
||||||
|
add("Complete a lap of the Falador rooftop agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 50));
|
||||||
|
add("Enter the mining guild wearing full prospector.",
|
||||||
|
new SkillRequirement(Skill.MINING, 60));
|
||||||
|
add("Kill the Blue Dragon under the Heroes' Guild.",
|
||||||
|
new QuestRequirement(Quest.HEROES_QUEST));
|
||||||
|
add("Crack a wall safe within Rogues Den.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 50));
|
||||||
|
add("Recharge your prayer in the Port Sarim church while wearing full Proselyte.",
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 30),
|
||||||
|
new QuestRequirement(Quest.THE_SLUG_MENACE));
|
||||||
|
add("Equip a dwarven helmet within the dwarven mines.",
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 50),
|
||||||
|
new QuestRequirement(Quest.GRIM_TALES));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Craft 252 Air Runes simultaneously.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 88));
|
||||||
|
add("Purchase a White 2h Sword from Sir Vyvin.",
|
||||||
|
new QuestRequirement(Quest.WANTED));
|
||||||
|
add("Find at least 3 magic roots at once when digging up your magic tree in Falador.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 91),
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 75));
|
||||||
|
add("Jump over the strange floor in Taverley dungeon.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 80));
|
||||||
|
add("Mix a Saradomin brew in Falador east bank.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 81));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class FremennikDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public FremennikDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Catch a Cerulean twitch.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 11));
|
||||||
|
add("Change your boots at Yrsa's Shoe Store.",
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
|
||||||
|
add("Craft a tiara from scratch in Rellekka.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 23),
|
||||||
|
new SkillRequirement(Skill.MINING, 20),
|
||||||
|
new SkillRequirement(Skill.SMITHING, 20),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
|
||||||
|
add("Browse the Stonemasons shop.",
|
||||||
|
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
|
||||||
|
add("Steal from the Keldagrim crafting or baker's stall.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 5),
|
||||||
|
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
|
||||||
|
add("Enter the Troll Stronghold.",
|
||||||
|
new QuestRequirement(Quest.DEATH_PLATEAU),
|
||||||
|
new QuestRequirement(Quest.TROLL_STRONGHOLD, true));
|
||||||
|
add("Chop and burn some oak logs in the Fremennik Province.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 15),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 15));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Slay a Brine rat.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 47),
|
||||||
|
new QuestRequirement(Quest.OLAFS_QUEST, true));
|
||||||
|
add("Travel to the Snowy Hunter Area via Eagle.",
|
||||||
|
new QuestRequirement(Quest.EAGLES_PEAK));
|
||||||
|
add("Mine some coal in Rellekka.",
|
||||||
|
new SkillRequirement(Skill.MINING, 30),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
|
||||||
|
add("Steal from the Rellekka Fish stalls.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 42),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
|
||||||
|
add("Travel to Miscellania by Fairy ring.",
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS),
|
||||||
|
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
|
||||||
|
add("Catch a Snowy knight.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 35));
|
||||||
|
add("Pick up your Pet Rock from your POH Menagerie.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 37),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
|
||||||
|
add("Visit the Lighthouse from Waterbirth island.",
|
||||||
|
new QuestRequirement(Quest.HORROR_FROM_THE_DEEP),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS, true));
|
||||||
|
add("Mine some gold at the Arzinian mine.",
|
||||||
|
new SkillRequirement(Skill.MINING, 40),
|
||||||
|
new QuestRequirement(Quest.BETWEEN_A_ROCK, true));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Teleport to Trollheim.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 61),
|
||||||
|
new QuestRequirement(Quest.EADGARS_RUSE));
|
||||||
|
add("Catch a Sabre-toothed Kyatt.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 55));
|
||||||
|
add("Mix a super defence potion in the Fremennik province.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 66));
|
||||||
|
add("Steal from the Keldagrim Gem Stall.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 75),
|
||||||
|
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
|
||||||
|
add("Craft a Fremennik shield on Neitiznot.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 56),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_ISLES));
|
||||||
|
add("Mine 5 Adamantite ores on Jatizso.",
|
||||||
|
new SkillRequirement(Skill.MINING, 70),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_ISLES));
|
||||||
|
add("Obtain 100% support from your kingdom subjects.",
|
||||||
|
new QuestRequirement(Quest.THRONE_OF_MISCELLANIA));
|
||||||
|
add("Teleport to Waterbirth Island.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 72),
|
||||||
|
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
|
||||||
|
add("Obtain the Blast Furnace Foreman's permission to use the Blast Furnace for free.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 60),
|
||||||
|
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Craft 56 astral runes at once.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 82),
|
||||||
|
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
|
||||||
|
add("Create a dragonstone amulet in the Neitiznot furnace.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 80),
|
||||||
|
new QuestRequirement(Quest.THE_FREMENNIK_ISLES, true));
|
||||||
|
add("Complete a lap of the Rellekka agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 80));
|
||||||
|
add("Kill each of the Godwars generals.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 70),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 70),
|
||||||
|
new SkillRequirement(Skill.HITPOINTS, 70),
|
||||||
|
new SkillRequirement(Skill.RANGED, 70),
|
||||||
|
new QuestRequirement(Quest.TROLL_STRONGHOLD));
|
||||||
|
add("Slay a Spiritual mage within the Godwars Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 83),
|
||||||
|
new QuestRequirement(Quest.TROLL_STRONGHOLD));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class KandarinDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public KandarinDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Catch a Mackerel at Catherby.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 16));
|
||||||
|
add("Plant some Jute seeds in the patch north of McGrubor's Wood.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 13));
|
||||||
|
add("Defeat one of each elemental in the workshop.",
|
||||||
|
new QuestRequirement(Quest.ELEMENTAL_WORKSHOP_I, true));
|
||||||
|
add("Cross the Coal truck log shortcut.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 20));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Complete a lap of the Barbarian agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 35),
|
||||||
|
new QuestRequirement(Quest.ALFRED_GRIMHANDS_BARCRAWL));
|
||||||
|
add("Create a Super Antipoison potion from scratch in the Seers/Catherby Area.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 48));
|
||||||
|
add("Enter the Ranging guild.",
|
||||||
|
new SkillRequirement(Skill.RANGED, 40));
|
||||||
|
add("Use the grapple shortcut to get from the water obelisk to Catherby shore.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 36),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 22),
|
||||||
|
new SkillRequirement(Skill.RANGED, 39));
|
||||||
|
add("Catch and cook a Bass in Catherby.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 46),
|
||||||
|
new SkillRequirement(Skill.COOKING, 43));
|
||||||
|
add("Teleport to Camelot.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 45));
|
||||||
|
add("String a Maple shortbow in Seers' Village bank.",
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 50));
|
||||||
|
add("Pick some Limpwurt root from the farming patch in Catherby.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 26));
|
||||||
|
add("Create a Mind helmet.",
|
||||||
|
new QuestRequirement(Quest.ELEMENTAL_WORKSHOP_II));
|
||||||
|
add("Kill a Fire Giant inside Baxtorian Waterfall.",
|
||||||
|
new QuestRequirement(Quest.WATERFALL_QUEST, true));
|
||||||
|
add("Steal from the chest in Hemenster.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 47));
|
||||||
|
add("Travel to McGrubor's Wood by Fairy Ring.",
|
||||||
|
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
|
||||||
|
add("Mine some coal near the coal trucks.",
|
||||||
|
new SkillRequirement(Skill.MINING, 30));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Catch a Leaping Sturgeon.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 70),
|
||||||
|
new SkillRequirement(Skill.AGILITY, 45),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 45));
|
||||||
|
add("Complete a lap of the Seers' Village agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 60));
|
||||||
|
add("Create a Yew Longbow from scratch around Seers' Village.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 60),
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 70),
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 10));
|
||||||
|
add("Enter the Seers' Village courthouse with piety turned on.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 70),
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 70),
|
||||||
|
new QuestRequirement(Quest.KINGS_RANSOM));
|
||||||
|
add("Charge a Water Orb.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 56));
|
||||||
|
add("Burn some Maple logs with a bow in Seers' Village.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 65));
|
||||||
|
add("Kill a Shadow Hound in the Shadow dungeon.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 53),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE, true));
|
||||||
|
add("Purchase and equip a granite body from Barbarian Assault.",
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 50),
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 50));
|
||||||
|
add("Have the Seers' estate agent decorate your house with Fancy Stone.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 50));
|
||||||
|
add("Smith an Adamant spear at Otto's Grotto.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 75),
|
||||||
|
new QuestRequirement(Quest.TAI_BWO_WANNAI_TRIO));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Pick some Dwarf weed from the herb patch at Catherby.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 79));
|
||||||
|
add("Fish and Cook 5 Sharks in Catherby using the Cooking gauntlets.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 76),
|
||||||
|
new SkillRequirement(Skill.COOKING, 80),
|
||||||
|
new QuestRequirement(Quest.FAMILY_CREST));
|
||||||
|
add("Mix a Stamina Mix on top of the Seers' Village bank.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 86),
|
||||||
|
new SkillRequirement(Skill.AGILITY, 60));
|
||||||
|
add("Smith a Rune Hasta at Otto's Grotto.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 90));
|
||||||
|
add("Construct a Pyre ship from Magic Logs.(Requires Chewed Bones.)",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 85),
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 85));
|
||||||
|
add("Teleport to Catherby.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 87),
|
||||||
|
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.OrRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class KaramjaDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public KaramjaDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Use the rope swing to travel to the small island north-west of Karamja, where the " +
|
||||||
|
"moss giants are.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 10));
|
||||||
|
add("Mine some gold from the rocks on the north-west peninsula of Karamja.",
|
||||||
|
new SkillRequirement(Skill.MINING, 40));
|
||||||
|
add("Explore Cairn Island to the west of Karamja.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 15));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Claim a ticket from the Agility Arena in Brimhaven.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 30));
|
||||||
|
add("Discover hidden wall in the dungeon below the volcano.",
|
||||||
|
new QuestRequirement(Quest.DRAGON_SLAYER, true));
|
||||||
|
add("Visit the Isle of Crandor via the dungeon below the volcano.",
|
||||||
|
new QuestRequirement(Quest.DRAGON_SLAYER, true));
|
||||||
|
add("Use Vigroy and Hajedy's cart service.",
|
||||||
|
new QuestRequirement(Quest.SHILO_VILLAGE));
|
||||||
|
add("Earn 100% favour in the village of Tai Bwo Wannai.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 10),
|
||||||
|
new QuestRequirement(Quest.JUNGLE_POTION));
|
||||||
|
add("Cook a spider on a stick.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 16));
|
||||||
|
add("Charter the Lady of the Waves from Cairn Isle to Port Khazard.",
|
||||||
|
new QuestRequirement(Quest.SHILO_VILLAGE));
|
||||||
|
add("Cut a log from a teak tree.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 35),
|
||||||
|
new QuestRequirement(Quest.JUNGLE_POTION));
|
||||||
|
add("Cut a log from a mahogany tree.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 50),
|
||||||
|
new QuestRequirement(Quest.JUNGLE_POTION));
|
||||||
|
add("Catch a karambwan.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 65),
|
||||||
|
new QuestRequirement(Quest.TAI_BWO_WANNAI_TRIO, true));
|
||||||
|
add("Exchange gems for a machete.",
|
||||||
|
new QuestRequirement(Quest.JUNGLE_POTION));
|
||||||
|
add("Use the gnome glider to travel to Karamja.",
|
||||||
|
new QuestRequirement(Quest.THE_GRAND_TREE));
|
||||||
|
add("Grow a healthy fruit tree in the patch near Brimhaven.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 27));
|
||||||
|
add("Trap a horned graahk.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 41));
|
||||||
|
add("Chop the vines to gain deeper access to Brimhaven Dungeon.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 10));
|
||||||
|
add("Cross the lava using the stepping stones within Brimhaven Dungeon.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 12));
|
||||||
|
add("Climb the stairs within Brimhaven Dungeon.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 10));
|
||||||
|
add("Charter a ship from the shipyard in the far east of Karamja.",
|
||||||
|
new QuestRequirement(Quest.THE_GRAND_TREE));
|
||||||
|
add("Mine a red topaz from a gem rock.",
|
||||||
|
new SkillRequirement(Skill.MINING, 40),
|
||||||
|
new OrRequirement(
|
||||||
|
new QuestRequirement(Quest.SHILO_VILLAGE),
|
||||||
|
new QuestRequirement(Quest.JUNGLE_POTION)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Craft some nature runes.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 44),
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Cook a karambwan thoroughly.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 30),
|
||||||
|
new QuestRequirement(Quest.TAI_BWO_WANNAI_TRIO));
|
||||||
|
add("Kill a deathwing in the dungeon under the Kharazi Jungle.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 15),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 50),
|
||||||
|
new SkillRequirement(Skill.AGILITY, 50),
|
||||||
|
new SkillRequirement(Skill.THIEVING, 50),
|
||||||
|
new SkillRequirement(Skill.MINING, 52),
|
||||||
|
new QuestRequirement(Quest.LEGENDS_QUEST));
|
||||||
|
add("Use the crossbow short cut south of the volcano.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 53),
|
||||||
|
new SkillRequirement(Skill.RANGED, 42),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 21));
|
||||||
|
add("Collect 5 palm leaves.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 15),
|
||||||
|
new QuestRequirement(Quest.LEGENDS_QUEST));
|
||||||
|
add("Be assigned a Slayer task by Duradel north of Shilo Village.",
|
||||||
|
new CombatLevelRequirement(100),
|
||||||
|
new SkillRequirement(Skill.SLAYER, 50),
|
||||||
|
new QuestRequirement(Quest.SHILO_VILLAGE));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Craft 56 Nature runes at once.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 91));
|
||||||
|
add("Check the health of a palm tree in Brimhaven.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 68));
|
||||||
|
add("Create an antivenom potion whilst standing in the horse shoe mine.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 87));
|
||||||
|
add("Check the health of your Calquat tree patch.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 72));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 William <https://github.com/monsterxsync>
|
||||||
|
* 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.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Favour;
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.FavourRequirement;
|
||||||
|
|
||||||
|
public class KourendDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public KourendDiaryRequirement()
|
||||||
|
{
|
||||||
|
//EASY
|
||||||
|
add("Mine some Iron at the Mount Karuulm mine.",
|
||||||
|
new SkillRequirement(Skill.MINING, 15));
|
||||||
|
add("Steal from a Hosidius Food Stall.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 25),
|
||||||
|
new FavourRequirement(Favour.HOSIDIUS, 15));
|
||||||
|
add("Browse the Warrens General Store.",
|
||||||
|
new QuestRequirement(Quest.THE_QUEEN_OF_THIEVES, true));
|
||||||
|
add("Enter your Player Owned House from Hosidius.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 25));
|
||||||
|
add("Create a Strength potion in the Lovakengj Pub.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 12));
|
||||||
|
add("Fish a Trout from the River Molch.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 20));
|
||||||
|
|
||||||
|
//MEDIUM
|
||||||
|
add("Travel to the Fairy Ring south of Mount Karuulm.",
|
||||||
|
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
|
||||||
|
add("Use Kharedst's memoirs to teleport to all five cities in Great Kourend.",
|
||||||
|
new QuestRequirement(Quest.THE_DEPTHS_OF_DESPAIR),
|
||||||
|
new QuestRequirement(Quest.THE_QUEEN_OF_THIEVES),
|
||||||
|
new QuestRequirement(Quest.TALE_OF_THE_RIGHTEOUS),
|
||||||
|
new QuestRequirement(Quest.THE_FORSAKEN_TOWER),
|
||||||
|
new QuestRequirement(Quest.THE_ASCENT_OF_ARCEUUS));
|
||||||
|
add("Mine some Volcanic sulphur.",
|
||||||
|
new SkillRequirement(Skill.MINING, 42));
|
||||||
|
add("Enter the Farming Guild.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 45));
|
||||||
|
add("Switch to the Necromancy Spellbook at Tyss.",
|
||||||
|
new FavourRequirement(Favour.ARCEUUS, 60));
|
||||||
|
add("Repair a Piscarilius crane.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 30));
|
||||||
|
add("Deliver some intelligence to Captain Ginea.",
|
||||||
|
new FavourRequirement(Favour.SHAYZIEN, 40));
|
||||||
|
add("Catch a Bluegill on Molch Island.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 43),
|
||||||
|
new SkillRequirement(Skill.HUNTER, 35));
|
||||||
|
add("Use the boulder leap in the Arceuus essence mine.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 49));
|
||||||
|
add("Subdue the Wintertodt.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 50));
|
||||||
|
add("Catch a Chinchompa in the Kourend Woodland.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 53),
|
||||||
|
new QuestRequirement(Quest.EAGLES_PEAK));
|
||||||
|
add("Chop some Mahogany logs north of the Farming Guild.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 50));
|
||||||
|
|
||||||
|
//HARD
|
||||||
|
add("Enter the Woodcutting Guild.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 60),
|
||||||
|
new FavourRequirement(Favour.HOSIDIUS, 75));
|
||||||
|
add("Smelt an Adamantite bar in The Forsaken Tower.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 70),
|
||||||
|
new QuestRequirement(Quest.THE_FORSAKEN_TOWER, true));
|
||||||
|
add("Kill a Lizardman Shaman in Molch.",
|
||||||
|
new FavourRequirement(Favour.SHAYZIEN, 100));
|
||||||
|
add("Mine some Lovakite.",
|
||||||
|
new SkillRequirement(Skill.MINING, 65),
|
||||||
|
new FavourRequirement(Favour.LOVAKENGJ, 30));
|
||||||
|
add("Plant some Logavano seeds at the Tithe Farm.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 74),
|
||||||
|
new FavourRequirement(Favour.HOSIDIUS, 100));
|
||||||
|
add("Teleport to Xeric's Heart using Xeric's Talisman.",
|
||||||
|
new QuestRequirement(Quest.ARCHITECTURAL_ALLIANCE));
|
||||||
|
add("Deliver an artefact to Captain Khaled.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 49),
|
||||||
|
new FavourRequirement(Favour.PISCARILIUS, 75));
|
||||||
|
add("Kill a Wyrm in the Karuulm Slayer Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 62));
|
||||||
|
add("Cast Monster Examine on a Troll south of Mount Quidamortem.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 66),
|
||||||
|
new QuestRequirement(Quest.DREAM_MENTOR));
|
||||||
|
|
||||||
|
//ELITE
|
||||||
|
add("Craft one or more Blood runes.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 77),
|
||||||
|
new SkillRequirement(Skill.MINING, 38),
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 38),
|
||||||
|
new FavourRequirement(Favour.ARCEUUS, 100));
|
||||||
|
add("Chop some Redwood logs.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 90),
|
||||||
|
new FavourRequirement(Favour.HOSIDIUS, 75));
|
||||||
|
add("Catch an Anglerfish and cook it whilst in Great Kourend.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 82),
|
||||||
|
new SkillRequirement(Skill.COOKING, 84),
|
||||||
|
new FavourRequirement(Favour.PISCARILIUS, 100));
|
||||||
|
add("Kill a Hydra in the Karuulm Slayer Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 95));
|
||||||
|
add("Create an Ape Atoll teleport tablet.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 90),
|
||||||
|
new SkillRequirement(Skill.MINING, 38),
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 38),
|
||||||
|
new FavourRequirement(Favour.ARCEUUS, 100));
|
||||||
|
add("Create your own Battlestaff from scratch within the Farming Guild.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 85),
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 40));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class LumbridgeDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public LumbridgeDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Complete a lap of the Draynor Village agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 10));
|
||||||
|
add("Slay a Cave bug beneath Lumbridge Swamp.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 7));
|
||||||
|
add("Have Sedridor teleport you to the Essence Mine.",
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Craft some water runes.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 5),
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Chop and burn some oak logs in Lumbridge.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 15),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 15));
|
||||||
|
add("Catch some Anchovies in Al Kharid.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 15));
|
||||||
|
add("Bake some Bread on the Lumbridge kitchen range.",
|
||||||
|
new QuestRequirement(Quest.COOKS_ASSISTANT));
|
||||||
|
add("Mine some Iron ore at the Al Kharid mine.",
|
||||||
|
new SkillRequirement(Skill.MINING, 15));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Complete a lap of the Al Kharid agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 20));
|
||||||
|
add("Grapple across the River Lum.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 8),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 19),
|
||||||
|
new SkillRequirement(Skill.RANGED, 37));
|
||||||
|
add("Purchase an upgraded device from Ava.",
|
||||||
|
new SkillRequirement(Skill.RANGED, 50),
|
||||||
|
new QuestRequirement(Quest.ANIMAL_MAGNETISM));
|
||||||
|
add("Travel to the Wizards' Tower by Fairy ring.",
|
||||||
|
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
|
||||||
|
add("Cast the teleport to Lumbridge spell.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 31));
|
||||||
|
add("Catch some Salmon in Lumbridge.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 30));
|
||||||
|
add("Craft a coif in the Lumbridge cow pen.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 38));
|
||||||
|
add("Chop some willow logs in Draynor Village.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 30));
|
||||||
|
add("Pickpocket Martin the Master Gardener.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 38));
|
||||||
|
add("Get a slayer task from Chaeldar.",
|
||||||
|
new CombatLevelRequirement(70),
|
||||||
|
new QuestRequirement(Quest.LOST_CITY));
|
||||||
|
add("Catch an Essence or Eclectic impling in Puro-Puro.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 42),
|
||||||
|
new QuestRequirement(Quest.LOST_CITY));
|
||||||
|
add("Craft some Lava runes at the fire altar in Al Kharid.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 23),
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Cast Bones to Peaches in Al Kharid palace.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 60));
|
||||||
|
add("Squeeze past the jutting wall on your way to the cosmic altar.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 46),
|
||||||
|
new QuestRequirement(Quest.LOST_CITY));
|
||||||
|
add("Craft 56 Cosmic runes simultaneously.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 59),
|
||||||
|
new QuestRequirement(Quest.LOST_CITY));
|
||||||
|
add("Travel from Lumbridge to Edgeville on a Waka Canoe.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 57));
|
||||||
|
add("Collect at least 100 Tears of Guthix in one visit.",
|
||||||
|
new QuestRequirement(Quest.TEARS_OF_GUTHIX));
|
||||||
|
add("Take the train from Dorgesh-Kaan to Keldagrim.",
|
||||||
|
new QuestRequirement(Quest.ANOTHER_SLICE_OF_HAM));
|
||||||
|
add("Purchase some Barrows gloves from the Lumbridge bank chest.",
|
||||||
|
new QuestRequirement(Quest.RECIPE_FOR_DISASTER));
|
||||||
|
add("Pick some Belladonna from the farming patch at Draynor Manor.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 63));
|
||||||
|
add("Light your mining helmet in the Lumbridge castle basement.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 65));
|
||||||
|
add("Recharge your prayer at Clan Wars with Smite activated.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 52));
|
||||||
|
add("Craft, string and enchant an Amulet of Power in Lumbridge.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 70),
|
||||||
|
new SkillRequirement(Skill.MAGIC, 57));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Steal from a Dorgesh-Kaan rich chest.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 78),
|
||||||
|
new QuestRequirement(Quest.DEATH_TO_THE_DORGESHUUN));
|
||||||
|
add("Pickpocket Movario on the Dorgesh-Kaan Agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 70),
|
||||||
|
new SkillRequirement(Skill.RANGED, 70),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 70),
|
||||||
|
new QuestRequirement(Quest.DEATH_TO_THE_DORGESHUUN));
|
||||||
|
add("Chop some magic logs at the Mage Training Arena.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 75));
|
||||||
|
add("Smith an Adamant platebody down Draynor sewer.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 88));
|
||||||
|
add("Craft 140 or more Water runes at once.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 76),
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.OrRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class MorytaniaDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public MorytaniaDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Craft any Snelm from scratch in Morytania.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 15));
|
||||||
|
add("Cook a thin Snail on the Port Phasmatys range.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 12));
|
||||||
|
add("Get a slayer task from Mazchna.",
|
||||||
|
new CombatLevelRequirement(20));
|
||||||
|
add("Kill a Banshee in the Slayer Tower.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 15));
|
||||||
|
add("Place a Scarecrow in the Morytania flower patch.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 23));
|
||||||
|
add("Kill a werewolf in its human form using the Wolfbane Dagger.",
|
||||||
|
new QuestRequirement(Quest.PRIEST_IN_PERIL));
|
||||||
|
add("Restore your prayer points at the nature altar.",
|
||||||
|
new QuestRequirement(Quest.NATURE_SPIRIT));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Catch a swamp lizard.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 29));
|
||||||
|
add("Complete a lap of the Canifis agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 40));
|
||||||
|
add("Obtain some Bark from a Hollow tree.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 45));
|
||||||
|
add("Kill a Terror Dog.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 40),
|
||||||
|
new QuestRequirement(Quest.LAIR_OF_TARN_RAZORLOR));
|
||||||
|
add("Complete a game of trouble brewing.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 40),
|
||||||
|
new QuestRequirement(Quest.CABIN_FEVER));
|
||||||
|
add("Make a batch of cannonballs at the Port Phasmatys furnace.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 35),
|
||||||
|
new QuestRequirement(Quest.DWARF_CANNON),
|
||||||
|
new QuestRequirement(Quest.GHOSTS_AHOY, true));
|
||||||
|
add("Kill a Fever Spider on Braindeath Island.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 42),
|
||||||
|
new QuestRequirement(Quest.RUM_DEAL));
|
||||||
|
add("Use an ectophial to return to Port Phasmatys.",
|
||||||
|
new QuestRequirement(Quest.GHOSTS_AHOY));
|
||||||
|
add("Mix a Guthix Balance potion while in Morytania.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 22),
|
||||||
|
new QuestRequirement(Quest.IN_AID_OF_THE_MYREQUE, true));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Enter the Kharyrll portal in your POH.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 66),
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 50),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE));
|
||||||
|
add("Climb the advanced spike chain within Slayer Tower.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 71));
|
||||||
|
add("Harvest some Watermelon from the Allotment patch on Harmony Island.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 47),
|
||||||
|
new QuestRequirement(Quest.THE_GREAT_BRAIN_ROBBERY, true));
|
||||||
|
add("Chop and burn some mahogany logs on Mos Le'Harmless.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 50),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 50),
|
||||||
|
new QuestRequirement(Quest.CABIN_FEVER));
|
||||||
|
add("Complete a temple trek with a hard companion.",
|
||||||
|
new QuestRequirement(Quest.IN_AID_OF_THE_MYREQUE));
|
||||||
|
add("Kill a Cave Horror.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 58),
|
||||||
|
new QuestRequirement(Quest.CABIN_FEVER));
|
||||||
|
add("Harvest some Bittercap Mushrooms from the patch in Canifis.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 53));
|
||||||
|
add("Pray at the Altar of Nature with Piety activated.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 70),
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 70),
|
||||||
|
new QuestRequirement(Quest.NATURE_SPIRIT),
|
||||||
|
new QuestRequirement(Quest.KINGS_RANSOM));
|
||||||
|
add("Use the shortcut to get to the bridge over the Salve.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 65));
|
||||||
|
add("Mine some Mithril ore in the Abandoned Mine.",
|
||||||
|
new SkillRequirement(Skill.MINING, 55),
|
||||||
|
new QuestRequirement(Quest.HAUNTED_MINE));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Catch a shark in Burgh de Rott with your bare hands.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 96),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 76),
|
||||||
|
new QuestRequirement(Quest.IN_AID_OF_THE_MYREQUE));
|
||||||
|
add("Cremate any Shade remains on a Magic or Redwood pyre.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 80),
|
||||||
|
new QuestRequirement(Quest.SHADES_OF_MORTTON));
|
||||||
|
add("Fertilize the Morytania herb patch using Lunar Magic.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 83),
|
||||||
|
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
|
||||||
|
add("Craft a Black dragonhide body in Canifis bank.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 84));
|
||||||
|
add("Kill an Abyssal demon in the Slayer Tower.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 85));
|
||||||
|
add("Loot the Barrows chest while wearing any complete barrows set.",
|
||||||
|
new SkillRequirement(Skill.DEFENCE, 70),
|
||||||
|
new OrRequirement(
|
||||||
|
new SkillRequirement(Skill.ATTACK, 70),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 70),
|
||||||
|
new SkillRequirement(Skill.RANGED, 70),
|
||||||
|
new SkillRequirement(Skill.MAGIC, 70)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestPointRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class VarrockDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public VarrockDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Have Aubury teleport you to the Essence mine.",
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Mine some Iron in the south east mining patch near Varrock.",
|
||||||
|
new SkillRequirement(Skill.MINING, 15));
|
||||||
|
add("Jump over the fence south of Varrock.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 13));
|
||||||
|
add("Spin a bowl on the pottery wheel and fire it in the oven in Barb Village.",
|
||||||
|
new SkillRequirement(Skill.CRAFTING, 8));
|
||||||
|
add("Craft some Earth runes.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 9));
|
||||||
|
add("Catch some trout in the River Lum at Barbarian Village.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 20));
|
||||||
|
add("Steal from the Tea stall in Varrock.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 5));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Enter the Champions' Guild.",
|
||||||
|
new QuestPointRequirement(32));
|
||||||
|
add("Select a colour for your kitten.",
|
||||||
|
new QuestRequirement(Quest.GARDEN_OF_TRANQUILLITY, true),
|
||||||
|
new QuestRequirement(Quest.GERTRUDES_CAT));
|
||||||
|
add("Use the spirit tree north of Varrock.",
|
||||||
|
new QuestRequirement(Quest.TREE_GNOME_VILLAGE));
|
||||||
|
add("Enter the Tolna dungeon after completing A Soul's Bane.",
|
||||||
|
new QuestRequirement(Quest.A_SOULS_BANE));
|
||||||
|
add("Teleport to the digsite using a Digsite pendant.",
|
||||||
|
new QuestRequirement(Quest.THE_DIG_SITE));
|
||||||
|
add("Cast the teleport to Varrock spell.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 25));
|
||||||
|
add("Get a Slayer task from Vannaka.",
|
||||||
|
new CombatLevelRequirement(40));
|
||||||
|
add("Pick a White tree fruit.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 25),
|
||||||
|
new QuestRequirement(Quest.GARDEN_OF_TRANQUILLITY));
|
||||||
|
add("Use the balloon to travel from Varrock.",
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 40),
|
||||||
|
new QuestRequirement(Quest.ENLIGHTENED_JOURNEY));
|
||||||
|
add("Complete a lap of the Varrock Agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 30));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Trade furs with the Fancy Dress Seller for a spottier cape and equip it.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 66));
|
||||||
|
add("Make a Waka Canoe near Edgeville.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 57));
|
||||||
|
add("Teleport to Paddewwa.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 54),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE));
|
||||||
|
add("Chop some yew logs in Varrock and burn them at the top of the Varrock church.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 60),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 60));
|
||||||
|
add("Have the Varrock estate agent decorate your house with Fancy Stone.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 50));
|
||||||
|
add("Collect at least 2 yew roots from the Tree patch in Varrock Palace.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 60),
|
||||||
|
new SkillRequirement(Skill.FARMING, 68));
|
||||||
|
add("Pray at the altar in Varrock palace with Smite active.",
|
||||||
|
new SkillRequirement(Skill.PRAYER, 52));
|
||||||
|
add("Squeeze through the obstacle pipe in Edgeville dungeon.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 51));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Create a super combat potion in Varrock west bank.",
|
||||||
|
new SkillRequirement(Skill.HERBLORE, 90),
|
||||||
|
new QuestRequirement(Quest.DRUIDIC_RITUAL));
|
||||||
|
add("Use Lunar magic to make 20 mahogany planks at the Lumberyard.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 86),
|
||||||
|
new QuestRequirement(Quest.DREAM_MENTOR));
|
||||||
|
add("Bake a summer pie in the Cooking Guild.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 95));
|
||||||
|
add("Smith and fletch ten rune darts within Varrock.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 89),
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 81),
|
||||||
|
new QuestRequirement(Quest.THE_TOURIST_TRAP));
|
||||||
|
add("Craft 100 or more earth runes simultaneously.",
|
||||||
|
new SkillRequirement(Skill.RUNECRAFT, 78),
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class WesternDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public WesternDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Catch a Copper Longtail.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 9));
|
||||||
|
add("Complete a novice game of Pest Control.",
|
||||||
|
new CombatLevelRequirement(40));
|
||||||
|
add("Mine some Iron Ore near Piscatoris.",
|
||||||
|
new SkillRequirement(Skill.MINING, 15));
|
||||||
|
add("Claim any Chompy bird hat from Rantz.",
|
||||||
|
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
|
||||||
|
add("Have Brimstail teleport you to the Essence mine.",
|
||||||
|
new QuestRequirement(Quest.RUNE_MYSTERIES));
|
||||||
|
add("Fletch an Oak shortbow from the Gnome Stronghold.",
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 20));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Take the agility shortcut from the Grand Tree to Otto's Grotto.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 37),
|
||||||
|
new QuestRequirement(Quest.TREE_GNOME_VILLAGE),
|
||||||
|
new QuestRequirement(Quest.THE_GRAND_TREE));
|
||||||
|
add("Travel to the Gnome Stronghold by Spirit Tree.",
|
||||||
|
new QuestRequirement(Quest.TREE_GNOME_VILLAGE));
|
||||||
|
add("Trap a Spined Larupia.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 31));
|
||||||
|
add("Fish some Bass on Ape Atoll.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 46),
|
||||||
|
new QuestRequirement(Quest.MONKEY_MADNESS_I, true));
|
||||||
|
add("Chop and burn some teak logs on Ape Atoll.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 35),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 35),
|
||||||
|
new QuestRequirement(Quest.MONKEY_MADNESS_I, true));
|
||||||
|
add("Complete an intermediate game of Pest Control.",
|
||||||
|
new CombatLevelRequirement(70));
|
||||||
|
add("Travel to the Feldip Hills by Gnome Glider.",
|
||||||
|
new QuestRequirement(Quest.ONE_SMALL_FAVOUR),
|
||||||
|
new QuestRequirement(Quest.THE_GRAND_TREE));
|
||||||
|
add("Claim a Chompy bird hat from Rantz after registering at least 125 kills.",
|
||||||
|
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
|
||||||
|
add("Travel from Eagles' Peak to the Feldip Hills by Eagle.",
|
||||||
|
new QuestRequirement(Quest.EAGLES_PEAK));
|
||||||
|
add("Make a Chocolate Bomb at the Grand Tree.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 42));
|
||||||
|
add("Complete a delivery for the Gnome Restaurant.",
|
||||||
|
new SkillRequirement(Skill.COOKING, 29));
|
||||||
|
add("Turn your small crystal seed into a Crystal saw.",
|
||||||
|
new QuestRequirement(Quest.THE_EYES_OF_GLOUPHRIE));
|
||||||
|
add("Mine some Gold ore underneath the Grand Tree.",
|
||||||
|
new SkillRequirement(Skill.MINING, 40),
|
||||||
|
new QuestRequirement(Quest.THE_GRAND_TREE));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Kill an Elf with a Crystal bow.",
|
||||||
|
new SkillRequirement(Skill.RANGED, 70),
|
||||||
|
new SkillRequirement(Skill.AGILITY, 56),
|
||||||
|
new QuestRequirement(Quest.ROVING_ELVES));
|
||||||
|
add("Catch and cook a Monkfish in Piscatoris.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 62),
|
||||||
|
new SkillRequirement(Skill.COOKING, 62),
|
||||||
|
new QuestRequirement(Quest.SWAN_SONG));
|
||||||
|
add("Complete a Veteran game of Pest Control.",
|
||||||
|
new CombatLevelRequirement(100));
|
||||||
|
add("Catch a Dashing Kebbit.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 69));
|
||||||
|
add("Complete a lap of the Ape Atoll agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 48),
|
||||||
|
new QuestRequirement(Quest.MONKEY_MADNESS_I));
|
||||||
|
add("Chop and burn some Mahogany logs on Ape Atoll.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 50),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 50),
|
||||||
|
new QuestRequirement(Quest.MONKEY_MADNESS_I));
|
||||||
|
add("Mine some Adamantite ore in Tirannwn.",
|
||||||
|
new SkillRequirement(Skill.MINING, 70),
|
||||||
|
new QuestRequirement(Quest.REGICIDE));
|
||||||
|
add("Check the health of your Palm tree in Lletya.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 68),
|
||||||
|
new QuestRequirement(Quest.MOURNINGS_END_PART_I, true));
|
||||||
|
add("Claim a Chompy bird hat from Rantz after registering at least 300 kills.",
|
||||||
|
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
|
||||||
|
add("Build an Isafdar painting in your POH Quest hall.",
|
||||||
|
new SkillRequirement(Skill.CONSTRUCTION, 65),
|
||||||
|
new QuestRequirement(Quest.ROVING_ELVES));
|
||||||
|
add("Kill Zulrah.",
|
||||||
|
new QuestRequirement(Quest.REGICIDE, true));
|
||||||
|
add("Teleport to Ape Atoll.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 64),
|
||||||
|
new QuestRequirement(Quest.RECIPE_FOR_DISASTER, true));
|
||||||
|
add("Pickpocket a Gnome.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 75),
|
||||||
|
new QuestRequirement(Quest.TREE_GNOME_VILLAGE));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Fletch a Magic Longbow in Tirannwn.",
|
||||||
|
new SkillRequirement(Skill.FLETCHING, 85),
|
||||||
|
new QuestRequirement(Quest.MOURNINGS_END_PART_I));
|
||||||
|
add("Kill the Thermonuclear Smoke devil (Does not require task).",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 93));
|
||||||
|
add("Have Prissy Scilla protect your Magic tree.",
|
||||||
|
new SkillRequirement(Skill.FARMING, 75));
|
||||||
|
add("Use the Elven overpass advanced cliffside shortcut.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 85),
|
||||||
|
new QuestRequirement(Quest.UNDERGROUND_PASS));
|
||||||
|
add("Claim a Chompy bird hat from Rantz after registering at least 1000 kills.",
|
||||||
|
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
|
||||||
|
add("Pickpocket an Elf.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 85),
|
||||||
|
new QuestRequirement(Quest.MOURNINGS_END_PART_I, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.achievementdiary.diaries;
|
||||||
|
|
||||||
|
import net.runelite.api.Quest;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.OrRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
|
||||||
|
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
|
||||||
|
|
||||||
|
public class WildernessDiaryRequirement extends GenericDiaryRequirement
|
||||||
|
{
|
||||||
|
public WildernessDiaryRequirement()
|
||||||
|
{
|
||||||
|
// EASY
|
||||||
|
add("Cast Low Alchemy at the Fountain of Rune.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 21));
|
||||||
|
add("Kill an Earth Warrior in the Wilderness beneath Edgeville.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 15));
|
||||||
|
add("Mine some Iron ore in the Wilderness.",
|
||||||
|
new SkillRequirement(Skill.MINING, 15));
|
||||||
|
add("Have the Mage of Zamorak teleport you to the Abyss.",
|
||||||
|
new QuestRequirement(Quest.ENTER_THE_ABYSS));
|
||||||
|
|
||||||
|
// MEDIUM
|
||||||
|
add("Mine some Mithril ore in the wilderness.",
|
||||||
|
new SkillRequirement(Skill.MINING, 55));
|
||||||
|
add("Chop some yew logs from a fallen Ent.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 61));
|
||||||
|
add("Enter the Wilderness Godwars Dungeon.",
|
||||||
|
new OrRequirement(
|
||||||
|
new SkillRequirement(Skill.AGILITY, 60),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 60)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add("Complete a lap of the Wilderness Agility course.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 52));
|
||||||
|
add("Charge an Earth Orb.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 60));
|
||||||
|
add("Kill a Bloodveld in the Wilderness Godwars Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 50));
|
||||||
|
add("Smith a Golden helmet in the Resource Area.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 50),
|
||||||
|
new QuestRequirement(Quest.BETWEEN_A_ROCK, true));
|
||||||
|
|
||||||
|
// HARD
|
||||||
|
add("Cast one of the 3 God spells against another player in the Wilderness.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 60),
|
||||||
|
new QuestRequirement(Quest.THE_MAGE_ARENA));
|
||||||
|
add("Charge an Air Orb.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 66));
|
||||||
|
add("Catch a Black Salamander in the Wilderness.",
|
||||||
|
new SkillRequirement(Skill.HUNTER, 67));
|
||||||
|
add("Smith an Adamant scimitar in the Resource Area.",
|
||||||
|
new SkillRequirement(Skill.SMITHING, 75));
|
||||||
|
add("Take the agility shortcut from Trollheim into the Wilderness.",
|
||||||
|
new SkillRequirement(Skill.AGILITY, 64),
|
||||||
|
new QuestRequirement(Quest.DEATH_PLATEAU));
|
||||||
|
add("Kill a Spiritual warrior in the Wilderness Godwars Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 68));
|
||||||
|
add("Fish some Raw Lava Eel in the Wilderness.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 53));
|
||||||
|
|
||||||
|
// ELITE
|
||||||
|
add("Teleport to Ghorrock.",
|
||||||
|
new SkillRequirement(Skill.MAGIC, 96),
|
||||||
|
new QuestRequirement(Quest.DESERT_TREASURE));
|
||||||
|
add("Fish and Cook a Dark Crab in the Resource Area.",
|
||||||
|
new SkillRequirement(Skill.FISHING, 85),
|
||||||
|
new SkillRequirement(Skill.COOKING, 90));
|
||||||
|
add("Smith a rune scimitar from scratch in the Resource Area.",
|
||||||
|
new SkillRequirement(Skill.MINING, 85),
|
||||||
|
new SkillRequirement(Skill.SMITHING, 90));
|
||||||
|
add("Steal from the Rogues' chest.",
|
||||||
|
new SkillRequirement(Skill.THIEVING, 84));
|
||||||
|
add("Slay a spiritual mage inside the wilderness Godwars Dungeon.",
|
||||||
|
new SkillRequirement(Skill.SLAYER, 83),
|
||||||
|
new OrRequirement(
|
||||||
|
new SkillRequirement(Skill.AGILITY, 60),
|
||||||
|
new SkillRequirement(Skill.STRENGTH, 60)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add("Cut and burn some magic logs in the Resource Area.",
|
||||||
|
new SkillRequirement(Skill.WOODCUTTING, 75),
|
||||||
|
new SkillRequirement(Skill.FIREMAKING, 75));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Alex Kolpa <https://github.com/AlexKolpa>
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.ui.overlay.infobox.Timer;
|
||||||
|
|
||||||
|
class AgilityArenaTimer extends Timer
|
||||||
|
{
|
||||||
|
AgilityArenaTimer(Plugin plugin, BufferedImage image)
|
||||||
|
{
|
||||||
|
super(1, ChronoUnit.MINUTES, image, plugin);
|
||||||
|
setTooltip("Time left until location changes");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Cas <https://github.com/casvandongen>
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import net.runelite.client.config.Config;
|
||||||
|
import net.runelite.client.config.ConfigGroup;
|
||||||
|
import net.runelite.client.config.ConfigItem;
|
||||||
|
import net.runelite.client.config.ConfigSection;
|
||||||
|
import net.runelite.client.config.Units;
|
||||||
|
|
||||||
|
@ConfigGroup("agility")
|
||||||
|
public interface AgilityConfig extends Config
|
||||||
|
{
|
||||||
|
@ConfigSection(
|
||||||
|
name = "Hallowed Sepulchre",
|
||||||
|
description = "Settings for Hallowed Sepulchre highlights",
|
||||||
|
position = 17
|
||||||
|
)
|
||||||
|
String sepulchreSection = "Hallowed Sepulchre";
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "showClickboxes",
|
||||||
|
name = "Show Clickboxes",
|
||||||
|
description = "Show agility course obstacle clickboxes",
|
||||||
|
position = 0
|
||||||
|
)
|
||||||
|
default boolean showClickboxes()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "showLapCount",
|
||||||
|
name = "Show Lap Count",
|
||||||
|
description = "Enable/disable the lap counter",
|
||||||
|
position = 1
|
||||||
|
)
|
||||||
|
default boolean showLapCount()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "lapTimeout",
|
||||||
|
name = "Hide Lap Count",
|
||||||
|
description = "Time until the lap counter hides/resets",
|
||||||
|
position = 2
|
||||||
|
)
|
||||||
|
@Units(Units.MINUTES)
|
||||||
|
default int lapTimeout()
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "lapsToLevel",
|
||||||
|
name = "Show Laps Until Goal",
|
||||||
|
description = "Show number of laps remaining until next goal is reached.",
|
||||||
|
position = 3
|
||||||
|
)
|
||||||
|
default boolean lapsToLevel()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "lapsPerHour",
|
||||||
|
name = "Show Laps Per Hour",
|
||||||
|
description = "Shows how many laps you can expect to complete per hour.",
|
||||||
|
position = 4
|
||||||
|
)
|
||||||
|
default boolean lapsPerHour()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "overlayColor",
|
||||||
|
name = "Overlay Color",
|
||||||
|
description = "Color of Agility overlay",
|
||||||
|
position = 5
|
||||||
|
)
|
||||||
|
default Color getOverlayColor()
|
||||||
|
{
|
||||||
|
return Color.GREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightMarks",
|
||||||
|
name = "Highlight Marks of Grace",
|
||||||
|
description = "Enable/disable the highlighting of retrievable Marks of Grace",
|
||||||
|
position = 6
|
||||||
|
)
|
||||||
|
default boolean highlightMarks()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "markHighlight",
|
||||||
|
name = "Mark Highlight Color",
|
||||||
|
description = "Color of highlighted Marks of Grace",
|
||||||
|
position = 7
|
||||||
|
)
|
||||||
|
default Color getMarkColor()
|
||||||
|
{
|
||||||
|
return Color.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightPortals",
|
||||||
|
name = "Highlight Portals",
|
||||||
|
description = "Enable/disable the highlighting of Prifddinas portals",
|
||||||
|
position = 8
|
||||||
|
)
|
||||||
|
default boolean highlightPortals()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "portalsHighlight",
|
||||||
|
name = "Portals Highlight Color",
|
||||||
|
description = "Color of highlighted Prifddinas portals",
|
||||||
|
position = 9
|
||||||
|
)
|
||||||
|
default Color getPortalsColor()
|
||||||
|
{
|
||||||
|
return Color.MAGENTA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightShortcuts",
|
||||||
|
name = "Highlight Agility Shortcuts",
|
||||||
|
description = "Enable/disable the highlighting of Agility shortcuts",
|
||||||
|
position = 10
|
||||||
|
)
|
||||||
|
default boolean highlightShortcuts()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "trapOverlay",
|
||||||
|
name = "Show Trap Overlay",
|
||||||
|
description = "Enable/disable the highlighting of traps on Agility courses",
|
||||||
|
position = 11
|
||||||
|
)
|
||||||
|
default boolean showTrapOverlay()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "trapHighlight",
|
||||||
|
name = "Trap Overlay Color",
|
||||||
|
description = "Color of Agility trap overlay",
|
||||||
|
position = 12
|
||||||
|
)
|
||||||
|
default Color getTrapColor()
|
||||||
|
{
|
||||||
|
return Color.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "agilityArenaNotifier",
|
||||||
|
name = "Agility Arena notifier",
|
||||||
|
description = "Notify on ticket location change in Agility Arena",
|
||||||
|
position = 13
|
||||||
|
)
|
||||||
|
default boolean notifyAgilityArena()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "agilityArenaTimer",
|
||||||
|
name = "Agility Arena timer",
|
||||||
|
description = "Configures whether Agility Arena timer is displayed",
|
||||||
|
position = 14
|
||||||
|
)
|
||||||
|
default boolean showAgilityArenaTimer()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightStick",
|
||||||
|
name = "Highlight Stick",
|
||||||
|
description = "Highlight the retrievable stick in the Werewolf Agility Course",
|
||||||
|
position = 15
|
||||||
|
)
|
||||||
|
default boolean highlightStick()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "stickHighlightColor",
|
||||||
|
name = "Stick Highlight Color",
|
||||||
|
description = "Color of highlighted stick",
|
||||||
|
position = 16
|
||||||
|
)
|
||||||
|
default Color stickHighlightColor()
|
||||||
|
{
|
||||||
|
return Color.RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightSepulchreNpcs",
|
||||||
|
name = "Highlight Projectiles",
|
||||||
|
description = "Highlights arrows and swords in the Sepulchre",
|
||||||
|
position = 17,
|
||||||
|
section = sepulchreSection
|
||||||
|
)
|
||||||
|
default boolean highlightSepulchreNpcs()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "sepulchreHighlightColor",
|
||||||
|
name = "Projectile Color",
|
||||||
|
description = "Overlay color for arrows and swords",
|
||||||
|
position = 18,
|
||||||
|
section = sepulchreSection
|
||||||
|
)
|
||||||
|
default Color sepulchreHighlightColor()
|
||||||
|
{
|
||||||
|
return Color.GREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightSepulchreObstacles",
|
||||||
|
name = "Highlight Obstacles",
|
||||||
|
description = "Highlights pillars and stairs in the Sepulchre",
|
||||||
|
position = 19,
|
||||||
|
section = sepulchreSection
|
||||||
|
)
|
||||||
|
default boolean highlightSepulchreObstacles()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "highlightSepulchreSkilling",
|
||||||
|
name = "Highlight Skill Challenges",
|
||||||
|
description = "Highlights skilling challenges in the Sepulchre",
|
||||||
|
position = 20,
|
||||||
|
section = sepulchreSection
|
||||||
|
)
|
||||||
|
default boolean highlightSepulchreSkilling()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||||
|
* Copyright (c) 2018, Cas <https://github.com/casvandongen>
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.NPC;
|
||||||
|
import net.runelite.api.NPCComposition;
|
||||||
|
import net.runelite.api.Perspective;
|
||||||
|
import net.runelite.api.Point;
|
||||||
|
import net.runelite.api.Tile;
|
||||||
|
import net.runelite.api.coords.LocalPoint;
|
||||||
|
import net.runelite.client.game.AgilityShortcut;
|
||||||
|
import net.runelite.client.ui.overlay.Overlay;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||||
|
|
||||||
|
class AgilityOverlay extends Overlay
|
||||||
|
{
|
||||||
|
private static final int MAX_DISTANCE = 2350;
|
||||||
|
private static final Color SHORTCUT_HIGH_LEVEL_COLOR = Color.ORANGE;
|
||||||
|
|
||||||
|
private final Client client;
|
||||||
|
private final AgilityPlugin plugin;
|
||||||
|
private final AgilityConfig config;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AgilityOverlay(Client client, AgilityPlugin plugin, AgilityConfig config)
|
||||||
|
{
|
||||||
|
super(plugin);
|
||||||
|
setPosition(OverlayPosition.DYNAMIC);
|
||||||
|
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||||
|
this.client = client;
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension render(Graphics2D graphics)
|
||||||
|
{
|
||||||
|
LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation();
|
||||||
|
Point mousePosition = client.getMouseCanvasPosition();
|
||||||
|
final List<Tile> marksOfGrace = plugin.getMarksOfGrace();
|
||||||
|
final Tile stickTile = plugin.getStickTile();
|
||||||
|
|
||||||
|
plugin.getObstacles().forEach((object, obstacle) ->
|
||||||
|
{
|
||||||
|
if (Obstacles.SHORTCUT_OBSTACLE_IDS.containsKey(object.getId()) && !config.highlightShortcuts() ||
|
||||||
|
Obstacles.TRAP_OBSTACLE_IDS.contains(object.getId()) && !config.showTrapOverlay() ||
|
||||||
|
Obstacles.COURSE_OBSTACLE_IDS.contains(object.getId()) && !config.showClickboxes() ||
|
||||||
|
Obstacles.SEPULCHRE_OBSTACLE_IDS.contains(object.getId()) && !config.highlightSepulchreObstacles() ||
|
||||||
|
Obstacles.SEPULCHRE_SKILL_OBSTACLE_IDS.contains(object.getId()) && !config.highlightSepulchreSkilling())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile tile = obstacle.getTile();
|
||||||
|
if (tile.getPlane() == client.getPlane()
|
||||||
|
&& object.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
|
||||||
|
{
|
||||||
|
// This assumes that the obstacle is not clickable.
|
||||||
|
if (Obstacles.TRAP_OBSTACLE_IDS.contains(object.getId()))
|
||||||
|
{
|
||||||
|
Polygon polygon = object.getCanvasTilePoly();
|
||||||
|
if (polygon != null)
|
||||||
|
{
|
||||||
|
OverlayUtil.renderPolygon(graphics, polygon, config.getTrapColor());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Shape objectClickbox = object.getClickbox();
|
||||||
|
if (objectClickbox != null)
|
||||||
|
{
|
||||||
|
AgilityShortcut agilityShortcut = obstacle.getShortcut();
|
||||||
|
Color configColor = agilityShortcut == null || agilityShortcut.getLevel() <= plugin.getAgilityLevel() ? config.getOverlayColor() : SHORTCUT_HIGH_LEVEL_COLOR;
|
||||||
|
if (config.highlightMarks() && !marksOfGrace.isEmpty())
|
||||||
|
{
|
||||||
|
configColor = config.getMarkColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Obstacles.PORTAL_OBSTACLE_IDS.contains(object.getId()))
|
||||||
|
{
|
||||||
|
if (config.highlightPortals())
|
||||||
|
{
|
||||||
|
configColor = config.getPortalsColor();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
||||||
|
{
|
||||||
|
graphics.setColor(configColor.darker());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
graphics.setColor(configColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.draw(objectClickbox);
|
||||||
|
graphics.setColor(new Color(configColor.getRed(), configColor.getGreen(), configColor.getBlue(), 50));
|
||||||
|
graphics.fill(objectClickbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (config.highlightMarks() && !marksOfGrace.isEmpty())
|
||||||
|
{
|
||||||
|
for (Tile markOfGraceTile : marksOfGrace)
|
||||||
|
{
|
||||||
|
highlightTile(graphics, playerLocation, markOfGraceTile, config.getMarkColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stickTile != null && config.highlightStick())
|
||||||
|
{
|
||||||
|
highlightTile(graphics, playerLocation, stickTile, config.stickHighlightColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<NPC> npcs = plugin.getNpcs();
|
||||||
|
if (!npcs.isEmpty() && config.highlightSepulchreNpcs())
|
||||||
|
{
|
||||||
|
Color color = config.sepulchreHighlightColor();
|
||||||
|
for (NPC npc : npcs)
|
||||||
|
{
|
||||||
|
NPCComposition npcComposition = npc.getComposition();
|
||||||
|
int size = npcComposition.getSize();
|
||||||
|
LocalPoint lp = npc.getLocalLocation();
|
||||||
|
|
||||||
|
Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size);
|
||||||
|
if (tilePoly != null)
|
||||||
|
{
|
||||||
|
OverlayUtil.renderPolygon(graphics, tilePoly, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void highlightTile(Graphics2D graphics, LocalPoint playerLocation, Tile tile, Color color)
|
||||||
|
{
|
||||||
|
if (tile.getPlane() == client.getPlane() && tile.getItemLayer() != null
|
||||||
|
&& tile.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
|
||||||
|
{
|
||||||
|
final Polygon poly = tile.getItemLayer().getCanvasTilePoly();
|
||||||
|
|
||||||
|
if (poly != null)
|
||||||
|
{
|
||||||
|
OverlayUtil.renderPolygon(graphics, poly, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,500 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.agility;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.ItemID;
|
||||||
|
import static net.runelite.api.ItemID.AGILITY_ARENA_TICKET;
|
||||||
|
import net.runelite.api.MenuAction;
|
||||||
|
import net.runelite.api.NPC;
|
||||||
|
import net.runelite.api.NullNpcID;
|
||||||
|
import net.runelite.api.Player;
|
||||||
|
import static net.runelite.api.Skill.AGILITY;
|
||||||
|
import net.runelite.api.Tile;
|
||||||
|
import net.runelite.api.TileItem;
|
||||||
|
import net.runelite.api.TileObject;
|
||||||
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
import net.runelite.api.events.DecorativeObjectChanged;
|
||||||
|
import net.runelite.api.events.DecorativeObjectDespawned;
|
||||||
|
import net.runelite.api.events.DecorativeObjectSpawned;
|
||||||
|
import net.runelite.api.events.GameObjectChanged;
|
||||||
|
import net.runelite.api.events.GameObjectDespawned;
|
||||||
|
import net.runelite.api.events.GameObjectSpawned;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
|
import net.runelite.api.events.GameTick;
|
||||||
|
import net.runelite.api.events.GroundObjectChanged;
|
||||||
|
import net.runelite.api.events.GroundObjectDespawned;
|
||||||
|
import net.runelite.api.events.GroundObjectSpawned;
|
||||||
|
import net.runelite.api.events.ItemDespawned;
|
||||||
|
import net.runelite.api.events.ItemSpawned;
|
||||||
|
import net.runelite.api.events.NpcDespawned;
|
||||||
|
import net.runelite.api.events.NpcSpawned;
|
||||||
|
import net.runelite.api.events.StatChanged;
|
||||||
|
import net.runelite.api.events.WallObjectChanged;
|
||||||
|
import net.runelite.api.events.WallObjectDespawned;
|
||||||
|
import net.runelite.api.events.WallObjectSpawned;
|
||||||
|
import net.runelite.client.Notifier;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.events.ConfigChanged;
|
||||||
|
import net.runelite.client.events.OverlayMenuClicked;
|
||||||
|
import net.runelite.client.game.AgilityShortcut;
|
||||||
|
import net.runelite.client.game.ItemManager;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDependency;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.plugins.xptracker.XpTrackerPlugin;
|
||||||
|
import net.runelite.client.plugins.xptracker.XpTrackerService;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayManager;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||||
|
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||||
|
|
||||||
|
@PluginDescriptor(
|
||||||
|
name = "Agility",
|
||||||
|
description = "Show helpful information about agility courses and obstacles",
|
||||||
|
tags = {"grace", "marks", "overlay", "shortcuts", "skilling", "traps", "sepulchre"}
|
||||||
|
)
|
||||||
|
@PluginDependency(XpTrackerPlugin.class)
|
||||||
|
@Slf4j
|
||||||
|
public class AgilityPlugin extends Plugin
|
||||||
|
{
|
||||||
|
private static final int AGILITY_ARENA_REGION_ID = 11157;
|
||||||
|
private static final Set<Integer> SEPULCHRE_NPCS = ImmutableSet.of(
|
||||||
|
NullNpcID.NULL_9672, NullNpcID.NULL_9673, NullNpcID.NULL_9674, // arrows
|
||||||
|
NullNpcID.NULL_9669, NullNpcID.NULL_9670, NullNpcID.NULL_9671 // swords
|
||||||
|
);
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Map<TileObject, Obstacle> obstacles = new HashMap<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final List<Tile> marksOfGrace = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Set<NPC> npcs = new HashSet<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private OverlayManager overlayManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AgilityOverlay agilityOverlay;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private LapCounterOverlay lapCounterOverlay;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Notifier notifier;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private InfoBoxManager infoBoxManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AgilityConfig config;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ItemManager itemManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private XpTrackerService xpTrackerService;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private AgilitySession session;
|
||||||
|
|
||||||
|
private int lastAgilityXp;
|
||||||
|
private WorldPoint lastArenaTicketPosition;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private int agilityLevel;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
private Tile stickTile;
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
AgilityConfig getConfig(ConfigManager configManager)
|
||||||
|
{
|
||||||
|
return configManager.getConfig(AgilityConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp() throws Exception
|
||||||
|
{
|
||||||
|
overlayManager.add(agilityOverlay);
|
||||||
|
overlayManager.add(lapCounterOverlay);
|
||||||
|
agilityLevel = client.getBoostedSkillLevel(AGILITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown() throws Exception
|
||||||
|
{
|
||||||
|
overlayManager.remove(agilityOverlay);
|
||||||
|
overlayManager.remove(lapCounterOverlay);
|
||||||
|
marksOfGrace.clear();
|
||||||
|
obstacles.clear();
|
||||||
|
session = null;
|
||||||
|
agilityLevel = 0;
|
||||||
|
stickTile = null;
|
||||||
|
npcs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onOverlayMenuClicked(OverlayMenuClicked overlayMenuClicked)
|
||||||
|
{
|
||||||
|
OverlayMenuEntry overlayMenuEntry = overlayMenuClicked.getEntry();
|
||||||
|
if (overlayMenuEntry.getMenuAction() == MenuAction.RUNELITE_OVERLAY
|
||||||
|
&& overlayMenuClicked.getOverlay() == lapCounterOverlay
|
||||||
|
&& overlayMenuClicked.getEntry().getOption().equals(LapCounterOverlay.AGILITY_RESET))
|
||||||
|
{
|
||||||
|
session = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameStateChanged(GameStateChanged event)
|
||||||
|
{
|
||||||
|
switch (event.getGameState())
|
||||||
|
{
|
||||||
|
case HOPPING:
|
||||||
|
case LOGIN_SCREEN:
|
||||||
|
session = null;
|
||||||
|
lastArenaTicketPosition = null;
|
||||||
|
removeAgilityArenaTimer();
|
||||||
|
npcs.clear();
|
||||||
|
break;
|
||||||
|
case LOADING:
|
||||||
|
marksOfGrace.clear();
|
||||||
|
obstacles.clear();
|
||||||
|
stickTile = null;
|
||||||
|
break;
|
||||||
|
case LOGGED_IN:
|
||||||
|
if (!isInAgilityArena())
|
||||||
|
{
|
||||||
|
lastArenaTicketPosition = null;
|
||||||
|
removeAgilityArenaTimer();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onConfigChanged(ConfigChanged event)
|
||||||
|
{
|
||||||
|
if (!config.showAgilityArenaTimer())
|
||||||
|
{
|
||||||
|
removeAgilityArenaTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatChanged(StatChanged statChanged)
|
||||||
|
{
|
||||||
|
if (statChanged.getSkill() != AGILITY)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
agilityLevel = statChanged.getBoostedLevel();
|
||||||
|
|
||||||
|
if (!config.showLapCount())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine how much EXP was actually gained
|
||||||
|
int agilityXp = client.getSkillExperience(AGILITY);
|
||||||
|
int skillGained = agilityXp - lastAgilityXp;
|
||||||
|
lastAgilityXp = agilityXp;
|
||||||
|
|
||||||
|
// Get course
|
||||||
|
Courses course = Courses.getCourse(client.getLocalPlayer().getWorldLocation().getRegionID());
|
||||||
|
if (course == null
|
||||||
|
|| (course.getCourseEndWorldPoints().length == 0
|
||||||
|
? Math.abs(course.getLastObstacleXp() - skillGained) > 1
|
||||||
|
: Arrays.stream(course.getCourseEndWorldPoints()).noneMatch(wp -> wp.equals(client.getLocalPlayer().getWorldLocation()))))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session != null && session.getCourse() == course)
|
||||||
|
{
|
||||||
|
session.incrementLapCount(client, xpTrackerService);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session = new AgilitySession(course);
|
||||||
|
// New course found, reset lap count and set new course
|
||||||
|
session.resetLapCount();
|
||||||
|
session.incrementLapCount(client, xpTrackerService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onItemSpawned(ItemSpawned itemSpawned)
|
||||||
|
{
|
||||||
|
if (obstacles.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final TileItem item = itemSpawned.getItem();
|
||||||
|
final Tile tile = itemSpawned.getTile();
|
||||||
|
|
||||||
|
if (item.getId() == ItemID.MARK_OF_GRACE)
|
||||||
|
{
|
||||||
|
marksOfGrace.add(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getId() == ItemID.STICK)
|
||||||
|
{
|
||||||
|
stickTile = tile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onItemDespawned(ItemDespawned itemDespawned)
|
||||||
|
{
|
||||||
|
final TileItem item = itemDespawned.getItem();
|
||||||
|
final Tile tile = itemDespawned.getTile();
|
||||||
|
|
||||||
|
marksOfGrace.remove(tile);
|
||||||
|
|
||||||
|
if (item.getId() == ItemID.STICK && stickTile == tile)
|
||||||
|
{
|
||||||
|
stickTile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameTick(GameTick tick)
|
||||||
|
{
|
||||||
|
if (isInAgilityArena())
|
||||||
|
{
|
||||||
|
// Hint arrow has no plane, and always returns the current plane
|
||||||
|
WorldPoint newTicketPosition = client.getHintArrowPoint();
|
||||||
|
WorldPoint oldTickPosition = lastArenaTicketPosition;
|
||||||
|
|
||||||
|
lastArenaTicketPosition = newTicketPosition;
|
||||||
|
|
||||||
|
if (oldTickPosition != null && newTicketPosition != null
|
||||||
|
&& (oldTickPosition.getX() != newTicketPosition.getX() || oldTickPosition.getY() != newTicketPosition.getY()))
|
||||||
|
{
|
||||||
|
log.debug("Ticked position moved from {} to {}", oldTickPosition, newTicketPosition);
|
||||||
|
|
||||||
|
if (config.notifyAgilityArena())
|
||||||
|
{
|
||||||
|
notifier.notify("Ticket location changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showAgilityArenaTimer())
|
||||||
|
{
|
||||||
|
showNewAgilityArenaTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInAgilityArena()
|
||||||
|
{
|
||||||
|
Player local = client.getLocalPlayer();
|
||||||
|
if (local == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPoint location = local.getWorldLocation();
|
||||||
|
return location.getRegionID() == AGILITY_ARENA_REGION_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeAgilityArenaTimer()
|
||||||
|
{
|
||||||
|
infoBoxManager.removeIf(infoBox -> infoBox instanceof AgilityArenaTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showNewAgilityArenaTimer()
|
||||||
|
{
|
||||||
|
removeAgilityArenaTimer();
|
||||||
|
infoBoxManager.addInfoBox(new AgilityArenaTimer(this, itemManager.getImage(AGILITY_ARENA_TICKET)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameObjectSpawned(GameObjectSpawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), null, event.getGameObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameObjectChanged(GameObjectChanged event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getPrevious(), event.getGameObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameObjectDespawned(GameObjectDespawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getGameObject(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGroundObjectSpawned(GroundObjectSpawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), null, event.getGroundObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGroundObjectChanged(GroundObjectChanged event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getPrevious(), event.getGroundObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGroundObjectDespawned(GroundObjectDespawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getGroundObject(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onWallObjectSpawned(WallObjectSpawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), null, event.getWallObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onWallObjectChanged(WallObjectChanged event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getPrevious(), event.getWallObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onWallObjectDespawned(WallObjectDespawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getWallObject(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onDecorativeObjectSpawned(DecorativeObjectSpawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), null, event.getDecorativeObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onDecorativeObjectChanged(DecorativeObjectChanged event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getPrevious(), event.getDecorativeObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onDecorativeObjectDespawned(DecorativeObjectDespawned event)
|
||||||
|
{
|
||||||
|
onTileObject(event.getTile(), event.getDecorativeObject(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject)
|
||||||
|
{
|
||||||
|
obstacles.remove(oldObject);
|
||||||
|
|
||||||
|
if (newObject == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Obstacles.COURSE_OBSTACLE_IDS.contains(newObject.getId()) ||
|
||||||
|
Obstacles.PORTAL_OBSTACLE_IDS.contains(newObject.getId()) ||
|
||||||
|
(Obstacles.TRAP_OBSTACLE_IDS.contains(newObject.getId())
|
||||||
|
&& Obstacles.TRAP_OBSTACLE_REGIONS.contains(newObject.getWorldLocation().getRegionID())) ||
|
||||||
|
Obstacles.SEPULCHRE_OBSTACLE_IDS.contains(newObject.getId()) ||
|
||||||
|
Obstacles.SEPULCHRE_SKILL_OBSTACLE_IDS.contains(newObject.getId()))
|
||||||
|
{
|
||||||
|
obstacles.put(newObject, new Obstacle(tile, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Obstacles.SHORTCUT_OBSTACLE_IDS.containsKey(newObject.getId()))
|
||||||
|
{
|
||||||
|
AgilityShortcut closestShortcut = null;
|
||||||
|
int distance = -1;
|
||||||
|
|
||||||
|
// Find the closest shortcut to this object
|
||||||
|
for (AgilityShortcut shortcut : Obstacles.SHORTCUT_OBSTACLE_IDS.get(newObject.getId()))
|
||||||
|
{
|
||||||
|
if (!shortcut.matches(newObject))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shortcut.getWorldLocation() == null)
|
||||||
|
{
|
||||||
|
closestShortcut = shortcut;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int newDistance = shortcut.getWorldLocation().distanceTo2D(newObject.getWorldLocation());
|
||||||
|
if (closestShortcut == null || newDistance < distance)
|
||||||
|
{
|
||||||
|
closestShortcut = shortcut;
|
||||||
|
distance = newDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closestShortcut != null)
|
||||||
|
{
|
||||||
|
obstacles.put(newObject, new Obstacle(tile, closestShortcut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onNpcSpawned(NpcSpawned npcSpawned)
|
||||||
|
{
|
||||||
|
NPC npc = npcSpawned.getNpc();
|
||||||
|
|
||||||
|
if (SEPULCHRE_NPCS.contains(npc.getId()))
|
||||||
|
{
|
||||||
|
npcs.add(npc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onNpcDespawned(NpcDespawned npcDespawned)
|
||||||
|
{
|
||||||
|
NPC npc = npcDespawned.getNpc();
|
||||||
|
npcs.remove(npc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import com.google.common.collect.EvictingQueue;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.plugins.xptracker.XpTrackerService;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
class AgilitySession
|
||||||
|
{
|
||||||
|
private final Courses course;
|
||||||
|
private Instant lastLapCompleted;
|
||||||
|
private int totalLaps;
|
||||||
|
private int lapsTillGoal;
|
||||||
|
private final EvictingQueue<Duration> lastLapTimes = EvictingQueue.create(30);
|
||||||
|
private int lapsPerHour;
|
||||||
|
|
||||||
|
AgilitySession(Courses course)
|
||||||
|
{
|
||||||
|
this.course = course;
|
||||||
|
}
|
||||||
|
|
||||||
|
void incrementLapCount(Client client, XpTrackerService xpTrackerService)
|
||||||
|
{
|
||||||
|
calculateLapsPerHour();
|
||||||
|
|
||||||
|
++totalLaps;
|
||||||
|
|
||||||
|
final int currentExp = client.getSkillExperience(Skill.AGILITY);
|
||||||
|
final int goalXp = xpTrackerService.getEndGoalXp(Skill.AGILITY);
|
||||||
|
final int goalRemainingXp = goalXp - currentExp;
|
||||||
|
double courseTotalExp = course.getTotalXp();
|
||||||
|
if (course == Courses.PYRAMID)
|
||||||
|
{
|
||||||
|
// agility pyramid has a bonus exp drop on the last obstacle that scales with player level and caps at 1000
|
||||||
|
// the bonus is not already accounted for in the total exp number in the courses enum
|
||||||
|
courseTotalExp += Math.min(300 + 8 * client.getRealSkillLevel(Skill.AGILITY), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
lapsTillGoal = goalRemainingXp > 0 ? (int) Math.ceil(goalRemainingXp / courseTotalExp) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculateLapsPerHour()
|
||||||
|
{
|
||||||
|
Instant now = Instant.now();
|
||||||
|
|
||||||
|
if (lastLapCompleted != null)
|
||||||
|
{
|
||||||
|
Duration timeSinceLastLap = Duration.between(lastLapCompleted, now);
|
||||||
|
|
||||||
|
if (!timeSinceLastLap.isNegative())
|
||||||
|
{
|
||||||
|
lastLapTimes.add(timeSinceLastLap);
|
||||||
|
|
||||||
|
Duration sum = Duration.ZERO;
|
||||||
|
for (Duration lapTime : lastLapTimes)
|
||||||
|
{
|
||||||
|
sum = sum.plus(lapTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Duration averageLapTime = sum.dividedBy(lastLapTimes.size());
|
||||||
|
lapsPerHour = (int) (Duration.ofHours(1).toMillis() / averageLapTime.toMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLapCompleted = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetLapCount()
|
||||||
|
{
|
||||||
|
totalLaps = 0;
|
||||||
|
lapsTillGoal = 0;
|
||||||
|
lastLapTimes.clear();
|
||||||
|
lapsPerHour = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
|
||||||
|
enum Courses
|
||||||
|
{
|
||||||
|
GNOME(86.5, 46, 9781),
|
||||||
|
DRAYNOR(120.0, 79, 12338),
|
||||||
|
AL_KHARID(180.0, 0, 13105, new WorldPoint(3299, 3194, 0)),
|
||||||
|
PYRAMID(722.0, 0, 13356, new WorldPoint(3364, 2830, 0)),
|
||||||
|
VARROCK(238.0, 125, 12853),
|
||||||
|
PENGUIN(540.0, 65, 10559),
|
||||||
|
BARBARIAN(139.5, 60, 10039),
|
||||||
|
CANIFIS(240.0, 175, 13878),
|
||||||
|
APE_ATOLL(580.0, 300, 11050),
|
||||||
|
FALADOR(440, 180, 12084),
|
||||||
|
WILDERNESS(571.0, 499, 11837),
|
||||||
|
WEREWOLF(730.0, 380, 14234),
|
||||||
|
SEERS(570.0, 435, 10806),
|
||||||
|
POLLNIVNEACH(890.0, 540, 13358),
|
||||||
|
RELLEKA(780.0, 475, 10553),
|
||||||
|
PRIFDDINAS(1337.0, 1037, 12895),
|
||||||
|
ARDOUGNE(793.0, 529, 10547);
|
||||||
|
|
||||||
|
private final static Map<Integer, Courses> coursesByRegion;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final double totalXp;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final int lastObstacleXp;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final int regionId;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final WorldPoint[] courseEndWorldPoints;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
ImmutableMap.Builder<Integer, Courses> builder = new ImmutableMap.Builder<>();
|
||||||
|
|
||||||
|
for (Courses course : values())
|
||||||
|
{
|
||||||
|
builder.put(course.regionId, course);
|
||||||
|
}
|
||||||
|
|
||||||
|
coursesByRegion = builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Courses(double totalXp, int lastObstacleXp, int regionId, WorldPoint... courseEndWorldPoints)
|
||||||
|
{
|
||||||
|
this.totalXp = totalXp;
|
||||||
|
this.lastObstacleXp = lastObstacleXp;
|
||||||
|
this.regionId = regionId;
|
||||||
|
this.courseEndWorldPoints = courseEndWorldPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Courses getCourse(int regionId)
|
||||||
|
{
|
||||||
|
return coursesByRegion.get(regionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY;
|
||||||
|
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||||
|
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPanel;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||||
|
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||||
|
|
||||||
|
class LapCounterOverlay extends OverlayPanel
|
||||||
|
{
|
||||||
|
static final String AGILITY_RESET = "Reset";
|
||||||
|
|
||||||
|
private final AgilityPlugin plugin;
|
||||||
|
private final AgilityConfig config;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private LapCounterOverlay(AgilityPlugin plugin, AgilityConfig config)
|
||||||
|
{
|
||||||
|
super(plugin);
|
||||||
|
setPosition(OverlayPosition.TOP_LEFT);
|
||||||
|
setPriority(OverlayPriority.LOW);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.config = config;
|
||||||
|
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Agility overlay"));
|
||||||
|
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY, AGILITY_RESET, "Agility overlay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension render(Graphics2D graphics)
|
||||||
|
{
|
||||||
|
AgilitySession session = plugin.getSession();
|
||||||
|
|
||||||
|
if (!config.showLapCount() ||
|
||||||
|
session == null ||
|
||||||
|
session.getLastLapCompleted() == null ||
|
||||||
|
session.getCourse() == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Duration lapTimeout = Duration.ofMinutes(config.lapTimeout());
|
||||||
|
Duration sinceLap = Duration.between(session.getLastLapCompleted(), Instant.now());
|
||||||
|
|
||||||
|
if (sinceLap.compareTo(lapTimeout) >= 0)
|
||||||
|
{
|
||||||
|
// timeout session
|
||||||
|
session.setLastLapCompleted(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelComponent.getChildren().add(LineComponent.builder()
|
||||||
|
.left("Total Laps:")
|
||||||
|
.right(Integer.toString(session.getTotalLaps()))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
if (config.lapsToLevel() && session.getLapsTillGoal() > 0)
|
||||||
|
{
|
||||||
|
panelComponent.getChildren().add(LineComponent.builder()
|
||||||
|
.left("Laps until goal:")
|
||||||
|
.right(Integer.toString(session.getLapsTillGoal()))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.lapsPerHour() && session.getLapsPerHour() > 0)
|
||||||
|
{
|
||||||
|
panelComponent.getChildren().add(LineComponent.builder()
|
||||||
|
.left("Laps per hour:")
|
||||||
|
.right(Integer.toString(session.getLapsPerHour()))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.render(graphics);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, MrGroggle
|
||||||
|
* 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 HOLDER 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.agility;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
import net.runelite.api.Tile;
|
||||||
|
import net.runelite.client.game.AgilityShortcut;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@AllArgsConstructor
|
||||||
|
class Obstacle
|
||||||
|
{
|
||||||
|
private final Tile tile;
|
||||||
|
@Nullable
|
||||||
|
private final AgilityShortcut shortcut;
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.agility;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import static net.runelite.api.NullObjectID.*;
|
||||||
|
import static net.runelite.api.ObjectID.*;
|
||||||
|
import net.runelite.client.game.AgilityShortcut;
|
||||||
|
|
||||||
|
class Obstacles
|
||||||
|
{
|
||||||
|
static final Set<Integer> COURSE_OBSTACLE_IDS = ImmutableSet.of(
|
||||||
|
// Gnome
|
||||||
|
OBSTACLE_NET_23134, TREE_BRANCH_23559, TREE_BRANCH_23560, OBSTACLE_NET_23135, OBSTACLE_PIPE_23138,
|
||||||
|
OBSTACLE_PIPE_23139, LOG_BALANCE_23145, BALANCING_ROPE_23557,
|
||||||
|
// Brimhaven
|
||||||
|
PLANK_3572, PLANK_3571, PLANK_3570, ROPE_SWING, PILLAR_3578, LOW_WALL, LOG_BALANCE, LOG_BALANCE_3557,
|
||||||
|
BALANCING_LEDGE_3561, BALANCING_LEDGE, MONKEY_BARS_3564, BALANCING_ROPE, HAND_HOLDS_3583,
|
||||||
|
// Draynor
|
||||||
|
ROUGH_WALL, TIGHTROPE, TIGHTROPE_11406, NARROW_WALL, WALL_11630, GAP_11631, CRATE_11632, STILE_7527,
|
||||||
|
// Al-Kharid
|
||||||
|
ROUGH_WALL_11633, TIGHTROPE_14398, CABLE, ZIP_LINE_14403, TROPICAL_TREE_14404, ROOF_TOP_BEAMS,
|
||||||
|
TIGHTROPE_14409, GAP_14399,
|
||||||
|
// Pyramid
|
||||||
|
STAIRS_10857, LOW_WALL_10865, LEDGE_10860, PLANK_10868, GAP_10882, LEDGE_10886, STAIRS_10857, GAP_10884,
|
||||||
|
GAP_10859, GAP_10861, LOW_WALL_10865, GAP_10859, LEDGE_10888, PLANK_10868, CLIMBING_ROCKS_10851, DOORWAY_10855,
|
||||||
|
// Varrock
|
||||||
|
ROUGH_WALL_14412, CLOTHES_LINE, GAP_14414, WALL_14832, GAP_14833, GAP_14834, GAP_14835, LEDGE_14836, EDGE,
|
||||||
|
// Penguin
|
||||||
|
STEPPING_STONE_21120, STEPPING_STONE_21126, STEPPING_STONE_21128, STEPPING_STONE_21129,
|
||||||
|
STEPPING_STONE_21130, STEPPING_STONE_21131, STEPPING_STONE_21132, STEPPING_STONE_21133,
|
||||||
|
ICICLES, ICE, ICE_21149, ICE_21150, ICE_21151, ICE_21152, ICE_21153, ICE_21154, ICE_21155, ICE_21156, GATE_21172,
|
||||||
|
// Barbarian
|
||||||
|
ROPESWING_23131, LOG_BALANCE_23144, OBSTACLE_NET_20211, BALANCING_LEDGE_23547, LADDER_16682, CRUMBLING_WALL_1948,
|
||||||
|
// Canifis
|
||||||
|
TALL_TREE_14843, GAP_14844, GAP_14845, GAP_14848, GAP_14846, POLEVAULT, GAP_14847, GAP_14897,
|
||||||
|
// Ape atoll
|
||||||
|
STEPPING_STONE_15412, TROPICAL_TREE_15414, MONKEYBARS_15417, SKULL_SLOPE_15483, ROPE_15487, TROPICAL_TREE_16062,
|
||||||
|
// Falador
|
||||||
|
ROUGH_WALL_14898, TIGHTROPE_14899, HAND_HOLDS_14901, GAP_14903, GAP_14904, TIGHTROPE_14905,
|
||||||
|
TIGHTROPE_14911, GAP_14919, LEDGE_14920, LEDGE_14921, LEDGE_14922, LEDGE_14923, LEDGE_14924, EDGE_14925,
|
||||||
|
// Wilderness
|
||||||
|
OBSTACLE_PIPE_23137, ROPESWING_23132, STEPPING_STONE_23556, LOG_BALANCE_23542, ROCKS_23640,
|
||||||
|
// Seers
|
||||||
|
WALL_14927, GAP_14928, TIGHTROPE_14932, GAP_14929, GAP_14930, EDGE_14931,
|
||||||
|
// Dorgesh-Kaan
|
||||||
|
CABLE_22569, CABLE_22572, LADDER_22564, JUTTING_WALL_22552, TUNNEL_22557, PYLON_22664,
|
||||||
|
CONSOLE, BOILER_22635, STAIRS_22650, STAIRS_22651, STAIRS_22609, STAIRS_22608,
|
||||||
|
// Pollniveach
|
||||||
|
BASKET_14935, MARKET_STALL_14936, BANNER_14937, GAP_14938, TREE_14939, ROUGH_WALL_14940,
|
||||||
|
MONKEYBARS, TREE_14944, DRYING_LINE,
|
||||||
|
// Rellaka
|
||||||
|
ROUGH_WALL_14946, GAP_14947, TIGHTROPE_14987, GAP_14990, GAP_14991, TIGHTROPE_14992, PILE_OF_FISH,
|
||||||
|
// Ardougne
|
||||||
|
WOODEN_BEAMS, GAP_15609, PLANK_26635, GAP_15610, GAP_15611, STEEP_ROOF, GAP_15612,
|
||||||
|
// Meiyerditch
|
||||||
|
NULL_12945, ROCK_17958, ROCK_17959, ROCK_17960, BOAT_17961, NULL_18122, NULL_18124, WALL_RUBBLE,
|
||||||
|
WALL_RUBBLE_18038, FLOORBOARDS, FLOORBOARDS_18071, FLOORBOARDS_18072, FLOORBOARDS_18073, NULL_18129, NULL_18130,
|
||||||
|
WALL_18078, NULL_18132, NULL_18133, NULL_18083, TUNNEL_18085, SHELF_18086, SHELF_18087, WALL_18088,
|
||||||
|
FLOORBOARDS_18089, FLOORBOARDS_18090, DOOR_18091, FLOORBOARDS_18093, FLOORBOARDS_18094, SHELF_18095,
|
||||||
|
SHELF_18096, FLOORBOARDS_18097, FLOORBOARDS_18098, WASHING_LINE_18099, WASHING_LINE_18100,
|
||||||
|
NULL_18135, NULL_18136, SHELF_18105, SHELF_18106, SHELF_18107, SHELF_18108, FLOORBOARDS_18109,
|
||||||
|
FLOORBOARDS_18110, FLOORBOARDS_18112, FLOORBOARDS_18111, FLOORBOARDS_18114, FLOORBOARDS_18113,
|
||||||
|
NULL_18116, FLOORBOARDS_18117, FLOORBOARDS_18118, STAIRS_DOWN, WALL_17980, BARRICADE_18054, LADDER_17999,
|
||||||
|
LADDER_18000, LADDER_18001, LADDER_18002, ROCKY_SURFACE, WALL_39172, WALL_39173,
|
||||||
|
// Werewolf
|
||||||
|
STEPPING_STONE_11643, HURDLE, HURDLE_11639, HURDLE_11640, PIPE_11657, SKULL_SLOPE, ZIP_LINE,
|
||||||
|
ZIP_LINE_11645, ZIP_LINE_11646,
|
||||||
|
// Prifddinas
|
||||||
|
LADDER_36221, TIGHTROPE_36225, CHIMNEY_36227, ROOF_EDGE, DARK_HOLE_36229, LADDER_36231, LADDER_36232,
|
||||||
|
ROPE_BRIDGE_36233, TIGHTROPE_36234, ROPE_BRIDGE_36235, TIGHTROPE_36236, TIGHTROPE_36237, DARK_HOLE_36238
|
||||||
|
);
|
||||||
|
|
||||||
|
static final Set<Integer> PORTAL_OBSTACLE_IDS = ImmutableSet.of(
|
||||||
|
// Prifddinas portals
|
||||||
|
NULL_36241, NULL_36242, NULL_36243, NULL_36244, NULL_36245, NULL_36246
|
||||||
|
);
|
||||||
|
|
||||||
|
static final Multimap<Integer, AgilityShortcut> SHORTCUT_OBSTACLE_IDS;
|
||||||
|
|
||||||
|
static final Set<Integer> TRAP_OBSTACLE_IDS = ImmutableSet.of(
|
||||||
|
// Agility pyramid
|
||||||
|
NULL_3550, NULL_10872, NULL_10873
|
||||||
|
);
|
||||||
|
|
||||||
|
static final List<Integer> TRAP_OBSTACLE_REGIONS = ImmutableList.of(12105, 13356);
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
final ImmutableMultimap.Builder<Integer, AgilityShortcut> builder = ImmutableMultimap.builder();
|
||||||
|
for (final AgilityShortcut item : AgilityShortcut.values())
|
||||||
|
{
|
||||||
|
for (int obstacle : item.getObstacleIds())
|
||||||
|
{
|
||||||
|
builder.put(obstacle, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SHORTCUT_OBSTACLE_IDS = builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Set<Integer> SEPULCHRE_OBSTACLE_IDS = ImmutableSet.of(
|
||||||
|
// Stairs and Platforms (and one Gate)
|
||||||
|
GATE_38460, PLATFORM_38455, PLATFORM_38456, PLATFORM_38457, PLATFORM_38458, PLATFORM_38459,
|
||||||
|
PLATFORM_38470, PLATFORM_38477, STAIRS_38462, STAIRS_38463, STAIRS_38464, STAIRS_38465,
|
||||||
|
STAIRS_38466, STAIRS_38467, STAIRS_38468, STAIRS_38469, STAIRS_38471, STAIRS_38472,
|
||||||
|
STAIRS_38473, STAIRS_38474, STAIRS_38475, STAIRS_38476
|
||||||
|
);
|
||||||
|
|
||||||
|
static final Set<Integer> SEPULCHRE_SKILL_OBSTACLE_IDS = ImmutableSet.of(
|
||||||
|
// Grapple, Portal, and Bridge skill obstacles
|
||||||
|
// They are multilocs, thus we use the NullObjectID
|
||||||
|
NULL_39524, NULL_39525, NULL_39526, NULL_39527, NULL_39528, NULL_39533
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
|
||||||
|
* 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.ammo;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.ui.overlay.infobox.Counter;
|
||||||
|
import net.runelite.client.util.QuantityFormatter;
|
||||||
|
|
||||||
|
class AmmoCounter extends Counter
|
||||||
|
{
|
||||||
|
@Getter
|
||||||
|
private final int itemID;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
AmmoCounter(Plugin plugin, int itemID, int count, String name, BufferedImage image)
|
||||||
|
{
|
||||||
|
super(image, plugin, count);
|
||||||
|
this.itemID = itemID;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
return QuantityFormatter.quantityToRSDecimalStack(getCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTooltip()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
|
||||||
|
* 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.ammo;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.EquipmentInventorySlot;
|
||||||
|
import net.runelite.api.InventoryID;
|
||||||
|
import net.runelite.api.Item;
|
||||||
|
import net.runelite.api.ItemComposition;
|
||||||
|
import net.runelite.api.ItemContainer;
|
||||||
|
import net.runelite.api.events.ItemContainerChanged;
|
||||||
|
import net.runelite.client.callback.ClientThread;
|
||||||
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.game.ItemManager;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||||
|
|
||||||
|
@PluginDescriptor(
|
||||||
|
name = "Ammo",
|
||||||
|
description = "Shows the current ammo the player has equipped",
|
||||||
|
tags = {"bolts", "darts", "chinchompa", "equipment"}
|
||||||
|
)
|
||||||
|
public class AmmoPlugin extends Plugin
|
||||||
|
{
|
||||||
|
@Inject
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ClientThread clientThread;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private InfoBoxManager infoBoxManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ItemManager itemManager;
|
||||||
|
|
||||||
|
private AmmoCounter counterBox;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp() throws Exception
|
||||||
|
{
|
||||||
|
clientThread.invokeLater(() ->
|
||||||
|
{
|
||||||
|
final ItemContainer container = client.getItemContainer(InventoryID.EQUIPMENT);
|
||||||
|
|
||||||
|
if (container != null)
|
||||||
|
{
|
||||||
|
checkInventory(container.getItems());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown() throws Exception
|
||||||
|
{
|
||||||
|
infoBoxManager.removeInfoBox(counterBox);
|
||||||
|
counterBox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onItemContainerChanged(ItemContainerChanged event)
|
||||||
|
{
|
||||||
|
if (event.getItemContainer() != client.getItemContainer(InventoryID.EQUIPMENT))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkInventory(event.getItemContainer().getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkInventory(final Item[] items)
|
||||||
|
{
|
||||||
|
// Check for weapon slot items. This overrides the ammo slot,
|
||||||
|
// as the player will use the thrown weapon (eg. chinchompas, knives, darts)
|
||||||
|
if (items.length > EquipmentInventorySlot.WEAPON.getSlotIdx())
|
||||||
|
{
|
||||||
|
final Item weapon = items[EquipmentInventorySlot.WEAPON.getSlotIdx()];
|
||||||
|
final ItemComposition weaponComp = itemManager.getItemComposition(weapon.getId());
|
||||||
|
if (weaponComp.isStackable())
|
||||||
|
{
|
||||||
|
updateInfobox(weapon, weaponComp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length <= EquipmentInventorySlot.AMMO.getSlotIdx())
|
||||||
|
{
|
||||||
|
removeInfobox();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Item ammo = items[EquipmentInventorySlot.AMMO.getSlotIdx()];
|
||||||
|
final ItemComposition comp = itemManager.getItemComposition(ammo.getId());
|
||||||
|
|
||||||
|
if (!comp.isStackable())
|
||||||
|
{
|
||||||
|
removeInfobox();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInfobox(ammo, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInfobox(final Item item, final ItemComposition comp)
|
||||||
|
{
|
||||||
|
if (counterBox != null && counterBox.getItemID() == item.getId())
|
||||||
|
{
|
||||||
|
counterBox.setCount(item.getQuantity());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeInfobox();
|
||||||
|
final BufferedImage image = itemManager.getImage(item.getId(), 5, false);
|
||||||
|
counterBox = new AmmoCounter(this, item.getId(), item.getQuantity(), comp.getName(), image);
|
||||||
|
infoBoxManager.addInfoBox(counterBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeInfobox()
|
||||||
|
{
|
||||||
|
infoBoxManager.removeInfoBox(counterBox);
|
||||||
|
counterBox = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Steve <steve.rs.dev@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.plugins.xpglobes;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
class XpGlobe
|
||||||
|
{
|
||||||
|
private Skill skill;
|
||||||
|
private int currentXp;
|
||||||
|
private int currentLevel;
|
||||||
|
private Instant time;
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Steve <steve.rs.dev@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.plugins.xpglobes;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import net.runelite.client.config.Alpha;
|
||||||
|
import net.runelite.client.config.Config;
|
||||||
|
import net.runelite.client.config.ConfigGroup;
|
||||||
|
import net.runelite.client.config.ConfigItem;
|
||||||
|
import net.runelite.client.config.Units;
|
||||||
|
|
||||||
|
@ConfigGroup("xpglobes")
|
||||||
|
public interface XpGlobesConfig extends Config
|
||||||
|
{
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "enableTooltips",
|
||||||
|
name = "Enable Tooltips",
|
||||||
|
description = "Configures whether or not to show tooltips",
|
||||||
|
position = 0
|
||||||
|
)
|
||||||
|
default boolean enableTooltips()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "showXpLeft",
|
||||||
|
name = "Show XP Left",
|
||||||
|
description = "Shows XP Left inside the globe tooltip box",
|
||||||
|
position = 1
|
||||||
|
)
|
||||||
|
default boolean showXpLeft()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "showActionsLeft",
|
||||||
|
name = "Show actions left",
|
||||||
|
description = "Shows the number of actions left inside the globe tooltip box",
|
||||||
|
position = 2
|
||||||
|
)
|
||||||
|
default boolean showActionsLeft()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "showXpHour",
|
||||||
|
name = "Show XP/hr",
|
||||||
|
description = "Shows XP per hour inside the globe tooltip box",
|
||||||
|
position = 3
|
||||||
|
)
|
||||||
|
default boolean showXpHour()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "hideMaxed",
|
||||||
|
name = "Hide maxed skills",
|
||||||
|
description = "Stop globes from showing up for level 99 skills",
|
||||||
|
position = 4
|
||||||
|
)
|
||||||
|
default boolean hideMaxed()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "enableCustomArcColor",
|
||||||
|
name = "Enable custom arc color",
|
||||||
|
description = "Enables the custom coloring of the globe's arc instead of using the skill's default color.",
|
||||||
|
position = 5
|
||||||
|
)
|
||||||
|
default boolean enableCustomArcColor()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Alpha
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "Progress arc color",
|
||||||
|
name = "Progress arc color",
|
||||||
|
description = "Change the color of the progress arc in the xp orb",
|
||||||
|
position = 6
|
||||||
|
)
|
||||||
|
default Color progressArcColor()
|
||||||
|
{
|
||||||
|
return Color.ORANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Alpha
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "Progress orb outline color",
|
||||||
|
name = "Progress orb outline color",
|
||||||
|
description = "Change the color of the progress orb outline",
|
||||||
|
position = 7
|
||||||
|
)
|
||||||
|
default Color progressOrbOutLineColor()
|
||||||
|
{
|
||||||
|
return Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Alpha
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "Progress orb background color",
|
||||||
|
name = "Progress orb background color",
|
||||||
|
description = "Change the color of the progress orb background",
|
||||||
|
position = 8
|
||||||
|
)
|
||||||
|
default Color progressOrbBackgroundColor()
|
||||||
|
{
|
||||||
|
return new Color(128, 128, 128, 127);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "Progress arc width",
|
||||||
|
name = "Progress arc width",
|
||||||
|
description = "Change the stroke width of the progress arc",
|
||||||
|
position = 9
|
||||||
|
)
|
||||||
|
@Units(Units.PIXELS)
|
||||||
|
default int progressArcStrokeWidth()
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "Orb size",
|
||||||
|
name = "Size of orbs",
|
||||||
|
description = "Change the size of the xp orbs",
|
||||||
|
position = 10
|
||||||
|
)
|
||||||
|
@Units(Units.PIXELS)
|
||||||
|
default int xpOrbSize()
|
||||||
|
{
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "Orb duration",
|
||||||
|
name = "Duration of orbs",
|
||||||
|
description = "Change the duration the xp orbs are visible",
|
||||||
|
position = 11
|
||||||
|
)
|
||||||
|
@Units(Units.SECONDS)
|
||||||
|
default int xpOrbDuration()
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "alignOrbsVertically",
|
||||||
|
name = "Vertical Orbs",
|
||||||
|
description = "Aligns the orbs vertically instead of horizontally.",
|
||||||
|
hidden = true
|
||||||
|
)
|
||||||
|
default boolean alignOrbsVertically()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "alignOrbsVertically",
|
||||||
|
name = "",
|
||||||
|
description = ""
|
||||||
|
)
|
||||||
|
void setAlignOrbsVertically(Boolean alignOrbsVertically);
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Steve <steve.rs.dev@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.plugins.xpglobes;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.Stroke;
|
||||||
|
import java.awt.geom.Arc2D;
|
||||||
|
import java.awt.geom.Ellipse2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY;
|
||||||
|
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||||
|
import net.runelite.api.Point;
|
||||||
|
import net.runelite.client.game.SkillIconManager;
|
||||||
|
import net.runelite.client.plugins.xptracker.XpActionType;
|
||||||
|
import net.runelite.client.plugins.xptracker.XpTrackerService;
|
||||||
|
import net.runelite.client.ui.SkillColor;
|
||||||
|
import net.runelite.client.ui.overlay.Overlay;
|
||||||
|
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||||
|
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||||
|
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||||
|
import net.runelite.client.ui.overlay.tooltip.Tooltip;
|
||||||
|
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
|
||||||
|
|
||||||
|
public class XpGlobesOverlay extends Overlay
|
||||||
|
{
|
||||||
|
private static final int MINIMUM_STEP = 10;
|
||||||
|
private static final int PROGRESS_RADIUS_START = 90;
|
||||||
|
private static final int PROGRESS_RADIUS_REMAINDER = 0;
|
||||||
|
private static final int TOOLTIP_RECT_SIZE_X = 150;
|
||||||
|
private static final Color DARK_OVERLAY_COLOR = new Color(0, 0, 0, 180);
|
||||||
|
static final String FLIP_ACTION = "Flip";
|
||||||
|
|
||||||
|
private final Client client;
|
||||||
|
private final XpGlobesPlugin plugin;
|
||||||
|
private final XpGlobesConfig config;
|
||||||
|
private final XpTrackerService xpTrackerService;
|
||||||
|
private final TooltipManager tooltipManager;
|
||||||
|
private final SkillIconManager iconManager;
|
||||||
|
private final Tooltip xpTooltip = new Tooltip(new PanelComponent());
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private XpGlobesOverlay(
|
||||||
|
Client client,
|
||||||
|
XpGlobesPlugin plugin,
|
||||||
|
XpGlobesConfig config,
|
||||||
|
XpTrackerService xpTrackerService,
|
||||||
|
SkillIconManager iconManager,
|
||||||
|
TooltipManager tooltipManager)
|
||||||
|
{
|
||||||
|
super(plugin);
|
||||||
|
this.iconManager = iconManager;
|
||||||
|
this.client = client;
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.config = config;
|
||||||
|
this.xpTrackerService = xpTrackerService;
|
||||||
|
this.tooltipManager = tooltipManager;
|
||||||
|
this.xpTooltip.getComponent().setPreferredSize(new Dimension(TOOLTIP_RECT_SIZE_X, 0));
|
||||||
|
setPosition(OverlayPosition.TOP_CENTER);
|
||||||
|
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "XP Globes overlay"));
|
||||||
|
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY, FLIP_ACTION, "XP Globes overlay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension render(Graphics2D graphics)
|
||||||
|
{
|
||||||
|
final List<XpGlobe> xpGlobes = plugin.getXpGlobes();
|
||||||
|
final int queueSize = xpGlobes.size();
|
||||||
|
if (queueSize == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int curDrawPosition = 0;
|
||||||
|
for (final XpGlobe xpGlobe : xpGlobes)
|
||||||
|
{
|
||||||
|
int startXp = xpTrackerService.getStartGoalXp(xpGlobe.getSkill());
|
||||||
|
int goalXp = xpTrackerService.getEndGoalXp(xpGlobe.getSkill());
|
||||||
|
if (config.alignOrbsVertically())
|
||||||
|
{
|
||||||
|
renderProgressCircle(graphics, xpGlobe, startXp, goalXp, 0, curDrawPosition, getBounds());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderProgressCircle(graphics, xpGlobe, startXp, goalXp, curDrawPosition, 0, getBounds());
|
||||||
|
}
|
||||||
|
curDrawPosition += MINIMUM_STEP + config.xpOrbSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get length of markers
|
||||||
|
final int markersLength = (queueSize * (config.xpOrbSize())) + ((MINIMUM_STEP) * (queueSize - 1));
|
||||||
|
if (config.alignOrbsVertically())
|
||||||
|
{
|
||||||
|
return new Dimension(config.xpOrbSize(), markersLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new Dimension(markersLength, config.xpOrbSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getSkillProgress(int startXp, int currentXp, int goalXp)
|
||||||
|
{
|
||||||
|
double xpGained = currentXp - startXp;
|
||||||
|
double xpGoal = goalXp - startXp;
|
||||||
|
|
||||||
|
return ((xpGained / xpGoal) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getSkillProgressRadius(int startXp, int currentXp, int goalXp)
|
||||||
|
{
|
||||||
|
return -(3.6 * getSkillProgress(startXp, currentXp, goalXp)); //arc goes backwards
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderProgressCircle(Graphics2D graphics, XpGlobe skillToDraw, int startXp, int goalXp, int x, int y, Rectangle bounds)
|
||||||
|
{
|
||||||
|
double radiusCurrentXp = getSkillProgressRadius(startXp, skillToDraw.getCurrentXp(), goalXp);
|
||||||
|
double radiusToGoalXp = 360; //draw a circle
|
||||||
|
|
||||||
|
Ellipse2D backgroundCircle = drawEllipse(graphics, x, y);
|
||||||
|
|
||||||
|
drawSkillImage(graphics, skillToDraw, x, y);
|
||||||
|
|
||||||
|
Point mouse = client.getMouseCanvasPosition();
|
||||||
|
int mouseX = mouse.getX() - bounds.x;
|
||||||
|
int mouseY = mouse.getY() - bounds.y;
|
||||||
|
|
||||||
|
// If mouse is hovering the globe
|
||||||
|
if (backgroundCircle.contains(mouseX, mouseY))
|
||||||
|
{
|
||||||
|
// Fill a darker overlay circle
|
||||||
|
graphics.setColor(DARK_OVERLAY_COLOR);
|
||||||
|
graphics.fill(backgroundCircle);
|
||||||
|
|
||||||
|
drawProgressLabel(graphics, skillToDraw, startXp, goalXp, x, y);
|
||||||
|
|
||||||
|
if (config.enableTooltips())
|
||||||
|
{
|
||||||
|
drawTooltip(skillToDraw, goalXp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
|
||||||
|
|
||||||
|
drawProgressArc(
|
||||||
|
graphics,
|
||||||
|
x, y,
|
||||||
|
config.xpOrbSize(), config.xpOrbSize(),
|
||||||
|
PROGRESS_RADIUS_REMAINDER, radiusToGoalXp,
|
||||||
|
5,
|
||||||
|
config.progressOrbOutLineColor()
|
||||||
|
);
|
||||||
|
drawProgressArc(
|
||||||
|
graphics,
|
||||||
|
x, y,
|
||||||
|
config.xpOrbSize(), config.xpOrbSize(),
|
||||||
|
PROGRESS_RADIUS_START, radiusCurrentXp,
|
||||||
|
config.progressArcStrokeWidth(),
|
||||||
|
config.enableCustomArcColor() ? config.progressArcColor() : SkillColor.find(skillToDraw.getSkill()).getColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawProgressLabel(Graphics2D graphics, XpGlobe globe, int startXp, int goalXp, int x, int y)
|
||||||
|
{
|
||||||
|
if (goalXp <= globe.getCurrentXp())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to int just to limit the decimal cases
|
||||||
|
String progress = (int) (getSkillProgress(startXp, globe.getCurrentXp(), goalXp)) + "%";
|
||||||
|
|
||||||
|
final FontMetrics metrics = graphics.getFontMetrics();
|
||||||
|
int drawX = x + (config.xpOrbSize() / 2) - (metrics.stringWidth(progress) / 2);
|
||||||
|
int drawY = y + (config.xpOrbSize() / 2) + (metrics.getHeight() / 2);
|
||||||
|
|
||||||
|
OverlayUtil.renderTextLocation(graphics, new Point(drawX, drawY), progress, Color.WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawProgressArc(Graphics2D graphics, int x, int y, int w, int h, double radiusStart, double radiusEnd, int strokeWidth, Color color)
|
||||||
|
{
|
||||||
|
Stroke stroke = graphics.getStroke();
|
||||||
|
graphics.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
|
||||||
|
graphics.setColor(color);
|
||||||
|
graphics.draw(new Arc2D.Double(
|
||||||
|
x, y,
|
||||||
|
w, h,
|
||||||
|
radiusStart, radiusEnd,
|
||||||
|
Arc2D.OPEN));
|
||||||
|
graphics.setStroke(stroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ellipse2D drawEllipse(Graphics2D graphics, int x, int y)
|
||||||
|
{
|
||||||
|
graphics.setColor(config.progressOrbBackgroundColor());
|
||||||
|
Ellipse2D ellipse = new Ellipse2D.Double(x, y, config.xpOrbSize(), config.xpOrbSize());
|
||||||
|
graphics.fill(ellipse);
|
||||||
|
graphics.draw(ellipse);
|
||||||
|
return ellipse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawSkillImage(Graphics2D graphics, XpGlobe xpGlobe, int x, int y)
|
||||||
|
{
|
||||||
|
BufferedImage skillImage = iconManager.getSkillImage(xpGlobe.getSkill());
|
||||||
|
|
||||||
|
if (skillImage == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.drawImage(
|
||||||
|
skillImage,
|
||||||
|
x + (config.xpOrbSize() / 2) - (skillImage.getWidth() / 2),
|
||||||
|
y + (config.xpOrbSize() / 2) - (skillImage.getHeight() / 2),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawTooltip(XpGlobe mouseOverSkill, int goalXp)
|
||||||
|
{
|
||||||
|
// reset the timer on XpGlobe to prevent it from disappearing while hovered over it
|
||||||
|
mouseOverSkill.setTime(Instant.now());
|
||||||
|
|
||||||
|
String skillName = mouseOverSkill.getSkill().getName();
|
||||||
|
String skillLevel = Integer.toString(mouseOverSkill.getCurrentLevel());
|
||||||
|
|
||||||
|
DecimalFormat decimalFormat = new DecimalFormat("###,###,###");
|
||||||
|
String skillCurrentXp = decimalFormat.format(mouseOverSkill.getCurrentXp());
|
||||||
|
|
||||||
|
final PanelComponent xpTooltip = (PanelComponent) this.xpTooltip.getComponent();
|
||||||
|
xpTooltip.getChildren().clear();
|
||||||
|
|
||||||
|
xpTooltip.getChildren().add(LineComponent.builder()
|
||||||
|
.left(skillName)
|
||||||
|
.right(skillLevel)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
xpTooltip.getChildren().add(LineComponent.builder()
|
||||||
|
.left("Current XP:")
|
||||||
|
.leftColor(Color.ORANGE)
|
||||||
|
.right(skillCurrentXp)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
if (goalXp > mouseOverSkill.getCurrentXp())
|
||||||
|
{
|
||||||
|
XpActionType xpActionType = xpTrackerService.getActionType(mouseOverSkill.getSkill());
|
||||||
|
|
||||||
|
if (config.showActionsLeft())
|
||||||
|
{
|
||||||
|
int actionsLeft = xpTrackerService.getActionsLeft(mouseOverSkill.getSkill());
|
||||||
|
if (actionsLeft != Integer.MAX_VALUE)
|
||||||
|
{
|
||||||
|
String actionsLeftString = decimalFormat.format(actionsLeft);
|
||||||
|
xpTooltip.getChildren().add(LineComponent.builder()
|
||||||
|
.left(xpActionType.getLabel() + " left:")
|
||||||
|
.leftColor(Color.ORANGE)
|
||||||
|
.right(actionsLeftString)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showXpLeft())
|
||||||
|
{
|
||||||
|
int xpLeft = goalXp - mouseOverSkill.getCurrentXp();
|
||||||
|
String skillXpToLvl = decimalFormat.format(xpLeft);
|
||||||
|
xpTooltip.getChildren().add(LineComponent.builder()
|
||||||
|
.left("XP left:")
|
||||||
|
.leftColor(Color.ORANGE)
|
||||||
|
.right(skillXpToLvl)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showXpHour())
|
||||||
|
{
|
||||||
|
int xpHr = xpTrackerService.getXpHr(mouseOverSkill.getSkill());
|
||||||
|
if (xpHr != 0)
|
||||||
|
{
|
||||||
|
String xpHrString = decimalFormat.format(xpHr);
|
||||||
|
xpTooltip.getChildren().add(LineComponent.builder()
|
||||||
|
.left("XP per hour:")
|
||||||
|
.leftColor(Color.ORANGE)
|
||||||
|
.right(xpHrString)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltipManager.add(this.xpTooltip);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Steve <steve.rs.dev@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.plugins.xpglobes;
|
||||||
|
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.Experience;
|
||||||
|
import net.runelite.api.MenuAction;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
|
import net.runelite.api.events.StatChanged;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.events.OverlayMenuClicked;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDependency;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.plugins.xptracker.XpTrackerPlugin;
|
||||||
|
import net.runelite.client.task.Schedule;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayManager;
|
||||||
|
|
||||||
|
@PluginDescriptor(
|
||||||
|
name = "XP Globes",
|
||||||
|
description = "Show XP globes for the respective skill when gaining XP",
|
||||||
|
tags = {"experience", "levels", "overlay"},
|
||||||
|
enabledByDefault = false
|
||||||
|
)
|
||||||
|
@PluginDependency(XpTrackerPlugin.class)
|
||||||
|
public class XpGlobesPlugin extends Plugin
|
||||||
|
{
|
||||||
|
private static final int MAXIMUM_SHOWN_GLOBES = 5;
|
||||||
|
|
||||||
|
private XpGlobe[] globeCache = new XpGlobe[Skill.values().length - 1]; //overall does not trigger xp change event
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final List<XpGlobe> xpGlobes = new ArrayList<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private XpGlobesConfig config;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private OverlayManager overlayManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private XpGlobesOverlay overlay;
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
XpGlobesConfig getConfig(ConfigManager configManager)
|
||||||
|
{
|
||||||
|
return configManager.getConfig(XpGlobesConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp() throws Exception
|
||||||
|
{
|
||||||
|
overlayManager.add(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown() throws Exception
|
||||||
|
{
|
||||||
|
overlayManager.remove(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatChanged(StatChanged statChanged)
|
||||||
|
{
|
||||||
|
Skill skill = statChanged.getSkill();
|
||||||
|
int currentXp = statChanged.getXp();
|
||||||
|
int currentLevel = statChanged.getLevel();
|
||||||
|
int skillIdx = skill.ordinal();
|
||||||
|
XpGlobe cachedGlobe = globeCache[skillIdx];
|
||||||
|
|
||||||
|
// StatChanged event occurs when stats drain/boost; check we have an change to actual xp
|
||||||
|
if (cachedGlobe != null && (cachedGlobe.getCurrentXp() >= currentXp))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hideMaxed() && currentLevel >= Experience.MAX_REAL_LEVEL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedGlobe != null)
|
||||||
|
{
|
||||||
|
cachedGlobe.setSkill(skill);
|
||||||
|
cachedGlobe.setCurrentXp(currentXp);
|
||||||
|
cachedGlobe.setCurrentLevel(currentLevel);
|
||||||
|
cachedGlobe.setTime(Instant.now());
|
||||||
|
addXpGlobe(cachedGlobe);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// dont draw non cached globes, this is triggered on login to setup all of the initial values
|
||||||
|
globeCache[skillIdx] = new XpGlobe(skill, currentXp, currentLevel, Instant.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addXpGlobe(XpGlobe xpGlobe)
|
||||||
|
{
|
||||||
|
// insert the globe, ordered by skill, if it isn't already in the list to be drawn
|
||||||
|
int idx = Collections.binarySearch(xpGlobes, xpGlobe, Comparator.comparing(XpGlobe::getSkill));
|
||||||
|
if (idx < 0)
|
||||||
|
{
|
||||||
|
xpGlobes.add(-idx - 1, xpGlobe);
|
||||||
|
|
||||||
|
// remove the oldest globe if there are too many
|
||||||
|
if (xpGlobes.size() > MAXIMUM_SHOWN_GLOBES)
|
||||||
|
{
|
||||||
|
xpGlobes.stream()
|
||||||
|
.min(Comparator.comparing(XpGlobe::getTime))
|
||||||
|
.ifPresent(xpGlobes::remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schedule(
|
||||||
|
period = 1,
|
||||||
|
unit = ChronoUnit.SECONDS
|
||||||
|
)
|
||||||
|
public void removeExpiredXpGlobes()
|
||||||
|
{
|
||||||
|
if (!xpGlobes.isEmpty())
|
||||||
|
{
|
||||||
|
Instant expireTime = Instant.now()
|
||||||
|
.minusSeconds(config.xpOrbDuration());
|
||||||
|
xpGlobes.removeIf(globe -> globe.getTime().isBefore(expireTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetGlobeState()
|
||||||
|
{
|
||||||
|
xpGlobes.clear();
|
||||||
|
globeCache = new XpGlobe[Skill.values().length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onOverlayMenuClicked(final OverlayMenuClicked event)
|
||||||
|
{
|
||||||
|
if (!(event.getEntry().getMenuAction() == MenuAction.RUNELITE_OVERLAY
|
||||||
|
&& event.getOverlay() == overlay))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getEntry().getOption().equals(XpGlobesOverlay.FLIP_ACTION))
|
||||||
|
{
|
||||||
|
config.setAlignOrbsVertically(!config.alignOrbsVertically());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameStateChanged(GameStateChanged event)
|
||||||
|
{
|
||||||
|
switch (event.getGameState())
|
||||||
|
{
|
||||||
|
case HOPPING:
|
||||||
|
case LOGGING_IN:
|
||||||
|
resetGlobeState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.plugins.xptracker;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
class XpAction
|
||||||
|
{
|
||||||
|
private int actions = 0;
|
||||||
|
private boolean actionsHistoryInitialized = false;
|
||||||
|
private int[] actionExps = new int[10];
|
||||||
|
private int actionExpIndex = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.plugins.xptracker;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum XpActionType
|
||||||
|
{
|
||||||
|
EXPERIENCE("Actions"),
|
||||||
|
ACTOR_HEALTH("Kills");
|
||||||
|
|
||||||
|
private final String label;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Dasgust <dasgust@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.plugins.xptracker;
|
||||||
|
|
||||||
|
public enum XpGoalTimeType
|
||||||
|
{
|
||||||
|
DAYS,
|
||||||
|
HOURS,
|
||||||
|
SHORT
|
||||||
|
}
|
||||||
@@ -0,0 +1,347 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||||
|
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
|
||||||
|
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Experience;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.api.WorldType;
|
||||||
|
import net.runelite.client.game.SkillIconManager;
|
||||||
|
import net.runelite.client.ui.ColorScheme;
|
||||||
|
import net.runelite.client.ui.DynamicGridLayout;
|
||||||
|
import net.runelite.client.ui.FontManager;
|
||||||
|
import net.runelite.client.ui.SkillColor;
|
||||||
|
import net.runelite.client.ui.components.MouseDragEventForwarder;
|
||||||
|
import net.runelite.client.ui.components.ProgressBar;
|
||||||
|
import net.runelite.client.util.ColorUtil;
|
||||||
|
import net.runelite.client.util.LinkBrowser;
|
||||||
|
import net.runelite.client.util.QuantityFormatter;
|
||||||
|
|
||||||
|
class XpInfoBox extends JPanel
|
||||||
|
{
|
||||||
|
static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00");
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
TWO_DECIMAL_FORMAT.setRoundingMode(RoundingMode.DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
private static final String HTML_TOOL_TIP_TEMPLATE =
|
||||||
|
"<html>%s %s done<br/>"
|
||||||
|
+ "%s %s/hr<br/>"
|
||||||
|
+ "%s %s</html>";
|
||||||
|
private static final String HTML_LABEL_TEMPLATE =
|
||||||
|
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
|
||||||
|
|
||||||
|
private static final String REMOVE_STATE = "Remove from canvas";
|
||||||
|
private static final String ADD_STATE = "Add to canvas";
|
||||||
|
|
||||||
|
// Instance members
|
||||||
|
private final JComponent panel;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
private final Skill skill;
|
||||||
|
|
||||||
|
/* The tracker's wrapping container */
|
||||||
|
private final JPanel container = new JPanel();
|
||||||
|
|
||||||
|
/* Contains the skill icon and the stats panel */
|
||||||
|
private final JPanel headerPanel = new JPanel();
|
||||||
|
|
||||||
|
/* Contains all the skill information (exp gained, per hour, etc) */
|
||||||
|
private final JPanel statsPanel = new JPanel();
|
||||||
|
|
||||||
|
private final ProgressBar progressBar = new ProgressBar();
|
||||||
|
|
||||||
|
private final JLabel topLeftStat = new JLabel();
|
||||||
|
private final JLabel bottomLeftStat = new JLabel();
|
||||||
|
private final JLabel topRightStat = new JLabel();
|
||||||
|
private final JLabel bottomRightStat = new JLabel();
|
||||||
|
private final JMenuItem pauseSkill = new JMenuItem("Pause");
|
||||||
|
private final JMenuItem canvasItem = new JMenuItem(ADD_STATE);
|
||||||
|
|
||||||
|
private final XpTrackerConfig xpTrackerConfig;
|
||||||
|
|
||||||
|
private boolean paused = false;
|
||||||
|
|
||||||
|
XpInfoBox(XpTrackerPlugin xpTrackerPlugin, XpTrackerConfig xpTrackerConfig, Client client, JComponent panel, Skill skill, SkillIconManager iconManager)
|
||||||
|
{
|
||||||
|
this.xpTrackerConfig = xpTrackerConfig;
|
||||||
|
this.panel = panel;
|
||||||
|
this.skill = skill;
|
||||||
|
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
setBorder(new EmptyBorder(5, 0, 0, 0));
|
||||||
|
|
||||||
|
container.setLayout(new BorderLayout());
|
||||||
|
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
|
||||||
|
// Create open xp tracker menu
|
||||||
|
final JMenuItem openXpTracker = new JMenuItem("Open Wise Old Man");
|
||||||
|
openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(
|
||||||
|
client.getLocalPlayer(), skill, client.getWorldType().contains(WorldType.LEAGUE))));
|
||||||
|
|
||||||
|
// Create reset menu
|
||||||
|
final JMenuItem reset = new JMenuItem("Reset");
|
||||||
|
reset.addActionListener(e -> xpTrackerPlugin.resetSkillState(skill));
|
||||||
|
|
||||||
|
// Create reset others menu
|
||||||
|
final JMenuItem resetOthers = new JMenuItem("Reset others");
|
||||||
|
resetOthers.addActionListener(e -> xpTrackerPlugin.resetOtherSkillState(skill));
|
||||||
|
|
||||||
|
// Create reset others menu
|
||||||
|
pauseSkill.addActionListener(e -> xpTrackerPlugin.pauseSkill(skill, !paused));
|
||||||
|
|
||||||
|
// Create popup menu
|
||||||
|
final JPopupMenu popupMenu = new JPopupMenu();
|
||||||
|
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
|
popupMenu.add(openXpTracker);
|
||||||
|
popupMenu.add(reset);
|
||||||
|
popupMenu.add(resetOthers);
|
||||||
|
popupMenu.add(pauseSkill);
|
||||||
|
popupMenu.add(canvasItem);
|
||||||
|
popupMenu.addPopupMenuListener(new PopupMenuListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent)
|
||||||
|
{
|
||||||
|
canvasItem.setText(xpTrackerPlugin.hasOverlay(skill) ? REMOVE_STATE : ADD_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent popupMenuEvent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuCanceled(PopupMenuEvent popupMenuEvent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
canvasItem.addActionListener(e ->
|
||||||
|
{
|
||||||
|
if (canvasItem.getText().equals(REMOVE_STATE))
|
||||||
|
{
|
||||||
|
xpTrackerPlugin.removeOverlay(skill);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xpTrackerPlugin.addOverlay(skill);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill)));
|
||||||
|
skillIcon.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
|
skillIcon.setVerticalAlignment(SwingConstants.CENTER);
|
||||||
|
skillIcon.setPreferredSize(new Dimension(35, 35));
|
||||||
|
|
||||||
|
headerPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
headerPanel.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
statsPanel.setLayout(new DynamicGridLayout(2, 2));
|
||||||
|
statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
statsPanel.setBorder(new EmptyBorder(9, 2, 9, 2));
|
||||||
|
|
||||||
|
|
||||||
|
topLeftStat.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
bottomLeftStat.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
topRightStat.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
bottomRightStat.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
|
||||||
|
statsPanel.add(topLeftStat); // top left
|
||||||
|
statsPanel.add(topRightStat); // top right
|
||||||
|
statsPanel.add(bottomLeftStat); // bottom left
|
||||||
|
statsPanel.add(bottomRightStat); // bottom right
|
||||||
|
|
||||||
|
headerPanel.add(skillIcon, BorderLayout.WEST);
|
||||||
|
headerPanel.add(statsPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JPanel progressWrapper = new JPanel();
|
||||||
|
progressWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
progressWrapper.setLayout(new BorderLayout());
|
||||||
|
progressWrapper.setBorder(new EmptyBorder(0, 7, 7, 7));
|
||||||
|
|
||||||
|
progressBar.setMaximumValue(100);
|
||||||
|
progressBar.setBackground(new Color(61, 56, 49));
|
||||||
|
progressBar.setForeground(SkillColor.find(skill).getColor());
|
||||||
|
progressBar.setDimmedText("Paused");
|
||||||
|
|
||||||
|
progressWrapper.add(progressBar, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
container.add(headerPanel, BorderLayout.NORTH);
|
||||||
|
container.add(progressWrapper, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
container.setComponentPopupMenu(popupMenu);
|
||||||
|
progressBar.setComponentPopupMenu(popupMenu);
|
||||||
|
|
||||||
|
// forward mouse drag events to parent panel for drag and drop reordering
|
||||||
|
MouseDragEventForwarder mouseDragEventForwarder = new MouseDragEventForwarder(panel);
|
||||||
|
container.addMouseListener(mouseDragEventForwarder);
|
||||||
|
container.addMouseMotionListener(mouseDragEventForwarder);
|
||||||
|
progressBar.addMouseListener(mouseDragEventForwarder);
|
||||||
|
progressBar.addMouseMotionListener(mouseDragEventForwarder);
|
||||||
|
|
||||||
|
add(container, BorderLayout.NORTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
canvasItem.setText(ADD_STATE);
|
||||||
|
panel.remove(this);
|
||||||
|
panel.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(boolean updated, boolean paused, XpSnapshotSingle xpSnapshotSingle)
|
||||||
|
{
|
||||||
|
SwingUtilities.invokeLater(() -> rebuildAsync(updated, paused, xpSnapshotSingle));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildAsync(boolean updated, boolean skillPaused, XpSnapshotSingle xpSnapshotSingle)
|
||||||
|
{
|
||||||
|
if (updated)
|
||||||
|
{
|
||||||
|
if (getParent() != panel)
|
||||||
|
{
|
||||||
|
panel.add(this);
|
||||||
|
panel.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xpTrackerConfig.prioritizeRecentXpSkills())
|
||||||
|
{
|
||||||
|
panel.setComponentZOrder(this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
paused = skillPaused;
|
||||||
|
|
||||||
|
// Update progress bar
|
||||||
|
progressBar.setValue((int) xpSnapshotSingle.getSkillProgressToGoal());
|
||||||
|
progressBar.setCenterLabel(xpTrackerConfig.progressBarLabel().getValueFunc().apply(xpSnapshotSingle));
|
||||||
|
progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel());
|
||||||
|
progressBar.setRightLabel(xpSnapshotSingle.getEndGoalXp() == Experience.MAX_SKILL_XP
|
||||||
|
? "200M"
|
||||||
|
: "Lvl. " + xpSnapshotSingle.getEndLevel());
|
||||||
|
|
||||||
|
// Add intermediate level positions to progressBar
|
||||||
|
if (xpTrackerConfig.showIntermediateLevels() && xpSnapshotSingle.getEndLevel() - xpSnapshotSingle.getStartLevel() > 1)
|
||||||
|
{
|
||||||
|
final List<Integer> positions = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int level = xpSnapshotSingle.getStartLevel() + 1; level < xpSnapshotSingle.getEndLevel(); level++)
|
||||||
|
{
|
||||||
|
double relativeStartExperience = Experience.getXpForLevel(level) - xpSnapshotSingle.getStartGoalXp();
|
||||||
|
double relativeEndExperience = xpSnapshotSingle.getEndGoalXp() - xpSnapshotSingle.getStartGoalXp();
|
||||||
|
positions.add((int) (relativeStartExperience / relativeEndExperience * 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.setPositions(positions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progressBar.setPositions(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
XpProgressBarLabel tooltipLabel = xpTrackerConfig.progressBarTooltipLabel();
|
||||||
|
|
||||||
|
progressBar.setToolTipText(String.format(
|
||||||
|
HTML_TOOL_TIP_TEMPLATE,
|
||||||
|
xpSnapshotSingle.getActionsInSession(),
|
||||||
|
xpSnapshotSingle.getActionType().getLabel(),
|
||||||
|
xpSnapshotSingle.getActionsPerHour(),
|
||||||
|
xpSnapshotSingle.getActionType().getLabel(),
|
||||||
|
tooltipLabel.getValueFunc().apply(xpSnapshotSingle),
|
||||||
|
tooltipLabel == XpProgressBarLabel.PERCENTAGE ? "of goal" : "till goal lvl"));
|
||||||
|
|
||||||
|
progressBar.setDimmed(skillPaused);
|
||||||
|
|
||||||
|
progressBar.repaint();
|
||||||
|
}
|
||||||
|
else if (!paused && skillPaused)
|
||||||
|
{
|
||||||
|
// React to the skill state now being paused
|
||||||
|
progressBar.setDimmed(true);
|
||||||
|
progressBar.repaint();
|
||||||
|
paused = true;
|
||||||
|
pauseSkill.setText("Unpause");
|
||||||
|
}
|
||||||
|
else if (paused && !skillPaused)
|
||||||
|
{
|
||||||
|
// React to the skill being unpaused (without update)
|
||||||
|
progressBar.setDimmed(false);
|
||||||
|
progressBar.repaint();
|
||||||
|
paused = false;
|
||||||
|
pauseSkill.setText("Pause");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update information labels
|
||||||
|
// Update exp per hour separately, every time (not only when there's an update)
|
||||||
|
topLeftStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel1(), xpSnapshotSingle));
|
||||||
|
topRightStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel2(), xpSnapshotSingle));
|
||||||
|
bottomLeftStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel3(), xpSnapshotSingle));
|
||||||
|
bottomRightStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel4(), xpSnapshotSingle));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String htmlLabel(XpPanelLabel panelLabel, XpSnapshotSingle xpSnapshotSingle)
|
||||||
|
{
|
||||||
|
String key = panelLabel.getActionKey(xpSnapshotSingle) + ": ";
|
||||||
|
String value = panelLabel.getValueFunc().apply(xpSnapshotSingle);
|
||||||
|
return htmlLabel(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String htmlLabel(String key, int value)
|
||||||
|
{
|
||||||
|
String valueStr = QuantityFormatter.quantityToRSDecimalStack(value, true);
|
||||||
|
return htmlLabel(key, valueStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String htmlLabel(String key, String valueStr)
|
||||||
|
{
|
||||||
|
return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Jasper Ketelaar <Jasper0781@gmail.com>
|
||||||
|
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.Experience;
|
||||||
|
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.client.ui.FontManager;
|
||||||
|
import net.runelite.client.ui.SkillColor;
|
||||||
|
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPanel;
|
||||||
|
import net.runelite.client.ui.overlay.components.ComponentOrientation;
|
||||||
|
import net.runelite.client.ui.overlay.components.ImageComponent;
|
||||||
|
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||||
|
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||||
|
import net.runelite.client.ui.overlay.components.ProgressBarComponent;
|
||||||
|
import net.runelite.client.ui.overlay.components.SplitComponent;
|
||||||
|
|
||||||
|
class XpInfoBoxOverlay extends OverlayPanel
|
||||||
|
{
|
||||||
|
private static final int BORDER_SIZE = 2;
|
||||||
|
private static final int XP_AND_PROGRESS_BAR_GAP = 2;
|
||||||
|
private static final int XP_AND_ICON_GAP = 4;
|
||||||
|
private static final Rectangle XP_AND_ICON_COMPONENT_BORDER = new Rectangle(2, 1, 4, 0);
|
||||||
|
|
||||||
|
private final PanelComponent iconXpSplitPanel = new PanelComponent();
|
||||||
|
private final XpTrackerPlugin plugin;
|
||||||
|
private final XpTrackerConfig config;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
private final Skill skill;
|
||||||
|
private final BufferedImage icon;
|
||||||
|
|
||||||
|
XpInfoBoxOverlay(
|
||||||
|
XpTrackerPlugin plugin,
|
||||||
|
XpTrackerConfig config,
|
||||||
|
Skill skill,
|
||||||
|
BufferedImage icon)
|
||||||
|
{
|
||||||
|
super(plugin);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.config = config;
|
||||||
|
this.skill = skill;
|
||||||
|
this.icon = icon;
|
||||||
|
panelComponent.setBorder(new Rectangle(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE));
|
||||||
|
panelComponent.setGap(new Point(0, XP_AND_PROGRESS_BAR_GAP));
|
||||||
|
iconXpSplitPanel.setBorder(XP_AND_ICON_COMPONENT_BORDER);
|
||||||
|
iconXpSplitPanel.setBackgroundColor(null);
|
||||||
|
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "XP Tracker overlay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension render(Graphics2D graphics)
|
||||||
|
{
|
||||||
|
iconXpSplitPanel.getChildren().clear();
|
||||||
|
|
||||||
|
//Setting the font to rs small font so that the overlay isn't huge
|
||||||
|
graphics.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
|
||||||
|
final XpSnapshotSingle snapshot = plugin.getSkillSnapshot(skill);
|
||||||
|
|
||||||
|
final String leftStr = config.onScreenDisplayMode().getActionKey(snapshot);
|
||||||
|
final String rightNum = config.onScreenDisplayMode().getValueFunc().apply(snapshot);
|
||||||
|
|
||||||
|
final LineComponent xpLine = LineComponent.builder()
|
||||||
|
.left(leftStr + ":")
|
||||||
|
.right(rightNum)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final String bottomLeftStr = config.onScreenDisplayModeBottom().getActionKey(snapshot);
|
||||||
|
final String bottomRightNum = config.onScreenDisplayModeBottom().getValueFunc().apply(snapshot);
|
||||||
|
|
||||||
|
final LineComponent xpLineBottom = LineComponent.builder()
|
||||||
|
.left(bottomLeftStr + ":")
|
||||||
|
.right(bottomRightNum)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final SplitComponent xpSplit = SplitComponent.builder()
|
||||||
|
.first(xpLine)
|
||||||
|
.second(xpLineBottom)
|
||||||
|
.orientation(ComponentOrientation.VERTICAL)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final ImageComponent imageComponent = new ImageComponent(icon);
|
||||||
|
final SplitComponent iconXpSplit = SplitComponent.builder()
|
||||||
|
.first(imageComponent)
|
||||||
|
.second(xpSplit)
|
||||||
|
.orientation(ComponentOrientation.HORIZONTAL)
|
||||||
|
.gap(new Point(XP_AND_ICON_GAP, 0))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
iconXpSplitPanel.getChildren().add(iconXpSplit);
|
||||||
|
|
||||||
|
final ProgressBarComponent progressBarComponent = new ProgressBarComponent();
|
||||||
|
|
||||||
|
progressBarComponent.setBackgroundColor(new Color(61, 56, 49));
|
||||||
|
progressBarComponent.setForegroundColor(SkillColor.find(skill).getColor());
|
||||||
|
|
||||||
|
progressBarComponent.setLeftLabel(String.valueOf(snapshot.getStartLevel()));
|
||||||
|
progressBarComponent.setRightLabel(snapshot.getEndGoalXp() == Experience.MAX_SKILL_XP
|
||||||
|
? "200M"
|
||||||
|
: String.valueOf(snapshot.getEndLevel()));
|
||||||
|
|
||||||
|
progressBarComponent.setValue(snapshot.getSkillProgressToGoal());
|
||||||
|
|
||||||
|
panelComponent.getChildren().add(iconXpSplitPanel);
|
||||||
|
panelComponent.getChildren().add(progressBarComponent);
|
||||||
|
|
||||||
|
return super.render(graphics);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return super.getName() + skill.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Cameron <moberg@tuta.io>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import net.runelite.api.Actor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.api.WorldType;
|
||||||
|
import net.runelite.client.game.SkillIconManager;
|
||||||
|
import net.runelite.client.ui.ColorScheme;
|
||||||
|
import net.runelite.client.ui.FontManager;
|
||||||
|
import net.runelite.client.ui.PluginPanel;
|
||||||
|
import net.runelite.client.ui.components.DragAndDropReorderPane;
|
||||||
|
import net.runelite.client.ui.components.PluginErrorPanel;
|
||||||
|
import net.runelite.client.util.LinkBrowser;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
class XpPanel extends PluginPanel
|
||||||
|
{
|
||||||
|
private final Map<Skill, XpInfoBox> infoBoxes = new HashMap<>();
|
||||||
|
|
||||||
|
private final JLabel overallExpGained = new JLabel(XpInfoBox.htmlLabel("Gained: ", 0));
|
||||||
|
private final JLabel overallExpHour = new JLabel(XpInfoBox.htmlLabel("Per hour: ", 0));
|
||||||
|
|
||||||
|
private final JPanel overallPanel = new JPanel();
|
||||||
|
|
||||||
|
/* This displays the "No exp gained" text */
|
||||||
|
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
|
||||||
|
|
||||||
|
XpPanel(XpTrackerPlugin xpTrackerPlugin, XpTrackerConfig xpTrackerConfig, Client client, SkillIconManager iconManager)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
setBorder(new EmptyBorder(6, 6, 6, 6));
|
||||||
|
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
final JPanel layoutPanel = new JPanel();
|
||||||
|
BoxLayout boxLayout = new BoxLayout(layoutPanel, BoxLayout.Y_AXIS);
|
||||||
|
layoutPanel.setLayout(boxLayout);
|
||||||
|
add(layoutPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
overallPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
overallPanel.setLayout(new BorderLayout());
|
||||||
|
overallPanel.setVisible(false); // this will only become visible when the player gets exp
|
||||||
|
|
||||||
|
// Create open xp tracker menu
|
||||||
|
final JMenuItem openXpTracker = new JMenuItem("Open Wise Old Man");
|
||||||
|
openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(
|
||||||
|
client.getLocalPlayer(), Skill.OVERALL, client.getWorldType().contains(WorldType.LEAGUE))));
|
||||||
|
|
||||||
|
// Create reset all menu
|
||||||
|
final JMenuItem reset = new JMenuItem("Reset All");
|
||||||
|
reset.addActionListener(e -> xpTrackerPlugin.resetAndInitState());
|
||||||
|
|
||||||
|
// Create pause all menu
|
||||||
|
final JMenuItem pauseAll = new JMenuItem("Pause All");
|
||||||
|
pauseAll.addActionListener(e -> xpTrackerPlugin.pauseAllSkills(true));
|
||||||
|
|
||||||
|
// Create unpause all menu
|
||||||
|
final JMenuItem unpauseAll = new JMenuItem("Unpause All");
|
||||||
|
unpauseAll.addActionListener(e -> xpTrackerPlugin.pauseAllSkills(false));
|
||||||
|
|
||||||
|
|
||||||
|
// Create popup menu
|
||||||
|
final JPopupMenu popupMenu = new JPopupMenu();
|
||||||
|
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
|
popupMenu.add(openXpTracker);
|
||||||
|
popupMenu.add(reset);
|
||||||
|
popupMenu.add(pauseAll);
|
||||||
|
popupMenu.add(unpauseAll);
|
||||||
|
overallPanel.setComponentPopupMenu(popupMenu);
|
||||||
|
|
||||||
|
final JLabel overallIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(Skill.OVERALL)));
|
||||||
|
|
||||||
|
final JPanel overallInfo = new JPanel();
|
||||||
|
overallInfo.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
overallInfo.setLayout(new GridLayout(2, 1));
|
||||||
|
overallInfo.setBorder(new EmptyBorder(0, 10, 0, 0));
|
||||||
|
|
||||||
|
overallExpGained.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
overallExpHour.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
|
||||||
|
overallInfo.add(overallExpGained);
|
||||||
|
overallInfo.add(overallExpHour);
|
||||||
|
|
||||||
|
overallPanel.add(overallIcon, BorderLayout.WEST);
|
||||||
|
overallPanel.add(overallInfo, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
final JComponent infoBoxPanel = new DragAndDropReorderPane();
|
||||||
|
|
||||||
|
layoutPanel.add(overallPanel);
|
||||||
|
layoutPanel.add(infoBoxPanel);
|
||||||
|
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
if (skill == Skill.OVERALL)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
infoBoxes.put(skill, new XpInfoBox(xpTrackerPlugin, xpTrackerConfig, client, infoBoxPanel, skill, iconManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
errorPanel.setContent("Exp trackers", "You have not gained experience yet.");
|
||||||
|
add(errorPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String buildXpTrackerUrl(final Actor player, final Skill skill, boolean leagueWorld)
|
||||||
|
{
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
final String host = leagueWorld ? "trailblazer.wiseoldman.net" : "wiseoldman.net";
|
||||||
|
|
||||||
|
return new HttpUrl.Builder()
|
||||||
|
.scheme("https")
|
||||||
|
.host(host)
|
||||||
|
.addPathSegment("players")
|
||||||
|
.addPathSegment(player.getName())
|
||||||
|
.addPathSegment("gained")
|
||||||
|
.addPathSegment("skilling")
|
||||||
|
.addQueryParameter("metric", skill.getName().toLowerCase())
|
||||||
|
.addQueryParameter("period", "week")
|
||||||
|
.build()
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetAllInfoBoxes()
|
||||||
|
{
|
||||||
|
infoBoxes.forEach((skill, xpInfoBox) -> xpInfoBox.reset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetSkill(Skill skill)
|
||||||
|
{
|
||||||
|
XpInfoBox xpInfoBox = infoBoxes.get(skill);
|
||||||
|
if (xpInfoBox != null)
|
||||||
|
{
|
||||||
|
xpInfoBox.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSkillExperience(boolean updated, boolean paused, Skill skill, XpSnapshotSingle xpSnapshotSingle)
|
||||||
|
{
|
||||||
|
final XpInfoBox xpInfoBox = infoBoxes.get(skill);
|
||||||
|
|
||||||
|
if (xpInfoBox != null)
|
||||||
|
{
|
||||||
|
xpInfoBox.update(updated, paused, xpSnapshotSingle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTotal(XpSnapshotSingle xpSnapshotTotal)
|
||||||
|
{
|
||||||
|
// if player has gained exp and hasn't switched displays yet, hide error panel and show overall info
|
||||||
|
if (xpSnapshotTotal.getXpGainedInSession() > 0 && !overallPanel.isVisible())
|
||||||
|
{
|
||||||
|
overallPanel.setVisible(true);
|
||||||
|
remove(errorPanel);
|
||||||
|
}
|
||||||
|
else if (xpSnapshotTotal.getXpGainedInSession() == 0 && overallPanel.isVisible())
|
||||||
|
{
|
||||||
|
overallPanel.setVisible(false);
|
||||||
|
add(errorPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(() -> rebuildAsync(xpSnapshotTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildAsync(XpSnapshotSingle xpSnapshotTotal)
|
||||||
|
{
|
||||||
|
overallExpGained.setText(XpInfoBox.htmlLabel("Gained: ", xpSnapshotTotal.getXpGainedInSession()));
|
||||||
|
overallExpHour.setText(XpInfoBox.htmlLabel("Per hour: ", xpSnapshotTotal.getXpPerHour()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.client.util.QuantityFormatter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum XpPanelLabel
|
||||||
|
{
|
||||||
|
TIME_TO_LEVEL("TTL", XpSnapshotSingle::getTimeTillGoalShort),
|
||||||
|
|
||||||
|
XP_GAINED("XP Gained", snap -> format(snap.getXpGainedInSession())),
|
||||||
|
XP_HOUR("XP/hr", snap -> format(snap.getXpPerHour())),
|
||||||
|
XP_LEFT("XP Left", snap -> format(snap.getXpRemainingToGoal())),
|
||||||
|
|
||||||
|
ACTIONS_LEFT("Actions", snap -> format(snap.getActionsRemainingToGoal())),
|
||||||
|
ACTIONS_HOUR("Actions/hr", snap -> format(snap.getActionsPerHour())),
|
||||||
|
ACTIONS_DONE("Actions Done", snap -> format(snap.getActionsInSession())),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
private final Function<XpSnapshotSingle, String> valueFunc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the action key label based on if the Action type is an xp drop or kill
|
||||||
|
*
|
||||||
|
* @param snapshot
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getActionKey(XpSnapshotSingle snapshot)
|
||||||
|
{
|
||||||
|
String actionKey = key;
|
||||||
|
if (snapshot.getActionType() == XpActionType.ACTOR_HEALTH)
|
||||||
|
{
|
||||||
|
return actionKey.replace("Action", "Kill");
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(int val)
|
||||||
|
{
|
||||||
|
return QuantityFormatter.quantityToRSDecimalStack(val, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.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.plugins.xptracker;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
class XpPauseState
|
||||||
|
{
|
||||||
|
// Internal state
|
||||||
|
private final Map<Skill, XpPauseStateSingle> skillPauses = new EnumMap<>(Skill.class);
|
||||||
|
private boolean cachedIsLoggedIn = false;
|
||||||
|
|
||||||
|
boolean pauseSkill(Skill skill)
|
||||||
|
{
|
||||||
|
return findPauseState(skill).manualPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean unpauseSkill(Skill skill)
|
||||||
|
{
|
||||||
|
return findPauseState(skill).unpause();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPaused(Skill skill)
|
||||||
|
{
|
||||||
|
return findPauseState(skill).isPaused();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tickXp(Skill skill, long currentXp, int pauseAfterMinutes)
|
||||||
|
{
|
||||||
|
final XpPauseStateSingle state = findPauseState(skill);
|
||||||
|
|
||||||
|
if (state.getXp() != currentXp)
|
||||||
|
{
|
||||||
|
state.xpChanged(currentXp);
|
||||||
|
}
|
||||||
|
else if (pauseAfterMinutes > 0)
|
||||||
|
{
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
final int pauseAfterMillis = pauseAfterMinutes * 60 * 1000;
|
||||||
|
final long lastChangeMillis = state.getLastChangeMillis();
|
||||||
|
// When config.pauseSkillAfter is 0, it is effectively disabled
|
||||||
|
if (lastChangeMillis != 0 && (now - lastChangeMillis) >= pauseAfterMillis)
|
||||||
|
{
|
||||||
|
state.timeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tickLogout(boolean pauseOnLogout, boolean loggedIn)
|
||||||
|
{
|
||||||
|
// Deduplicated login and logout calls
|
||||||
|
if (!cachedIsLoggedIn && loggedIn)
|
||||||
|
{
|
||||||
|
cachedIsLoggedIn = true;
|
||||||
|
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
findPauseState(skill).login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cachedIsLoggedIn && !loggedIn)
|
||||||
|
{
|
||||||
|
cachedIsLoggedIn = false;
|
||||||
|
|
||||||
|
// If configured, then let the pause state know to pause with reason: logout
|
||||||
|
if (pauseOnLogout)
|
||||||
|
{
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
findPauseState(skill).logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private XpPauseStateSingle findPauseState(Skill skill)
|
||||||
|
{
|
||||||
|
return skillPauses.computeIfAbsent(skill, XpPauseStateSingle::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.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.plugins.xptracker;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
class XpPauseStateSingle
|
||||||
|
{
|
||||||
|
@Getter
|
||||||
|
private final Skill skill;
|
||||||
|
private final Set<XpPauseReason> pauseReasons = EnumSet.noneOf(XpPauseReason.class);
|
||||||
|
@Getter
|
||||||
|
private long lastChangeMillis;
|
||||||
|
@Getter
|
||||||
|
private long xp;
|
||||||
|
|
||||||
|
boolean isPaused()
|
||||||
|
{
|
||||||
|
return !pauseReasons.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean login()
|
||||||
|
{
|
||||||
|
return pauseReasons.remove(XpPauseReason.PAUSED_LOGOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean logout()
|
||||||
|
{
|
||||||
|
return pauseReasons.add(XpPauseReason.PAUSED_LOGOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean timeout()
|
||||||
|
{
|
||||||
|
return pauseReasons.add(XpPauseReason.PAUSED_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean manualPause()
|
||||||
|
{
|
||||||
|
return pauseReasons.add(XpPauseReason.PAUSE_MANUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean xpChanged(long xp)
|
||||||
|
{
|
||||||
|
this.xp = xp;
|
||||||
|
this.lastChangeMillis = System.currentTimeMillis();
|
||||||
|
return clearAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean unpause()
|
||||||
|
{
|
||||||
|
this.lastChangeMillis = System.currentTimeMillis();
|
||||||
|
return clearAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean clearAll()
|
||||||
|
{
|
||||||
|
if (pauseReasons.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseReasons.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum XpPauseReason
|
||||||
|
{
|
||||||
|
PAUSE_MANUAL,
|
||||||
|
PAUSED_LOGOUT,
|
||||||
|
PAUSED_TIMEOUT
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static net.runelite.client.plugins.xptracker.XpInfoBox.TWO_DECIMAL_FORMAT;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum XpProgressBarLabel
|
||||||
|
{
|
||||||
|
PERCENTAGE((snap) -> TWO_DECIMAL_FORMAT.format(snap.getSkillProgressToGoal()) + "%"),
|
||||||
|
TIME_TO_LEVEL(XpSnapshotSingle::getTimeTillGoal),
|
||||||
|
HOURS_TO_LEVEL(XpSnapshotSingle::getTimeTillGoalHours)
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Function<XpSnapshotSingle, String> valueFunc;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.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.plugins.xptracker;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
class XpSnapshotSingle
|
||||||
|
{
|
||||||
|
private XpActionType actionType;
|
||||||
|
private int startLevel;
|
||||||
|
private int endLevel;
|
||||||
|
private int startGoalXp;
|
||||||
|
private int endGoalXp;
|
||||||
|
private int xpGainedInSession;
|
||||||
|
private int xpRemainingToGoal;
|
||||||
|
private int xpPerHour;
|
||||||
|
private double skillProgressToGoal;
|
||||||
|
private int actionsInSession;
|
||||||
|
private int actionsRemainingToGoal;
|
||||||
|
private int actionsPerHour;
|
||||||
|
private String timeTillGoal;
|
||||||
|
private String timeTillGoalHours;
|
||||||
|
private String timeTillGoalShort;
|
||||||
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.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.plugins.xptracker;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import net.runelite.api.NPC;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal state for the XpTrackerPlugin
|
||||||
|
*
|
||||||
|
* Note: This class's operations are not currently synchronized.
|
||||||
|
* It is intended to be called by the XpTrackerPlugin on the client thread.
|
||||||
|
*/
|
||||||
|
class XpState
|
||||||
|
{
|
||||||
|
private static final double DEFAULT_XP_MODIFIER = 4.0;
|
||||||
|
private static final double SHARED_XP_MODIFIER = DEFAULT_XP_MODIFIER / 3.0;
|
||||||
|
private final Map<Skill, XpStateSingle> xpSkills = new EnumMap<>(Skill.class);
|
||||||
|
private NPC interactedNPC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys all internal state, however any XpSnapshotSingle or XpSnapshotTotal remain unaffected.
|
||||||
|
*/
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
xpSkills.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets a single skill
|
||||||
|
* @param skill Skill to reset
|
||||||
|
* @param currentXp Current XP to set to, if unknown set to -1
|
||||||
|
*/
|
||||||
|
void resetSkill(Skill skill, long currentXp)
|
||||||
|
{
|
||||||
|
xpSkills.remove(skill);
|
||||||
|
xpSkills.put(skill, new XpStateSingle(skill, currentXp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a skill with the current known XP.
|
||||||
|
* When the result of this operation is XpUpdateResult.UPDATED, the UI should be updated accordingly.
|
||||||
|
* This is to distinguish events that reload all the skill's current values (such as world hopping)
|
||||||
|
* and also first-login when the skills are not initialized (the start XP will be -1 in this case).
|
||||||
|
* @param skill Skill to update
|
||||||
|
* @param currentXp Current known XP for this skill
|
||||||
|
* @param goalStartXp Possible XP start goal
|
||||||
|
* @param goalEndXp Possible XP end goal
|
||||||
|
* @return Whether or not the skill has been initialized, there was no change, or it has been updated
|
||||||
|
*/
|
||||||
|
XpUpdateResult updateSkill(Skill skill, long currentXp, int goalStartXp, int goalEndXp)
|
||||||
|
{
|
||||||
|
XpStateSingle state = getSkill(skill);
|
||||||
|
|
||||||
|
if (state.getStartXp() == -1)
|
||||||
|
{
|
||||||
|
if (currentXp >= 0)
|
||||||
|
{
|
||||||
|
initializeSkill(skill, currentXp);
|
||||||
|
return XpUpdateResult.INITIALIZED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return XpUpdateResult.NO_CHANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long startXp = state.getStartXp();
|
||||||
|
int gainedXp = state.getXpGained();
|
||||||
|
|
||||||
|
if (startXp + gainedXp > currentXp)
|
||||||
|
{
|
||||||
|
// Reinitialize with lesser currentXp, this can happen with negative xp lamps
|
||||||
|
initializeSkill(skill, currentXp);
|
||||||
|
return XpUpdateResult.INITIALIZED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return state.update(currentXp, goalStartXp, goalEndXp) ? XpUpdateResult.UPDATED : XpUpdateResult.NO_CHANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getCombatXPModifier(Skill skill)
|
||||||
|
{
|
||||||
|
if (skill == Skill.HITPOINTS)
|
||||||
|
{
|
||||||
|
return SHARED_XP_MODIFIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_XP_MODIFIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates skill with average actions based on currently interacted NPC.
|
||||||
|
* @param skill experience gained skill
|
||||||
|
* @param npc currently interacted NPC
|
||||||
|
* @param npcHealth health of currently interacted NPC
|
||||||
|
*/
|
||||||
|
void updateNpcExperience(Skill skill, NPC npc, Integer npcHealth, int xpModifier)
|
||||||
|
{
|
||||||
|
if (npc == null || npc.getCombatLevel() <= 0 || npcHealth == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final XpStateSingle state = getSkill(skill);
|
||||||
|
final int actionExp = (int) (npcHealth * getCombatXPModifier(skill) * xpModifier);
|
||||||
|
final XpAction action = state.getXpAction(XpActionType.ACTOR_HEALTH);
|
||||||
|
|
||||||
|
if (action.isActionsHistoryInitialized())
|
||||||
|
{
|
||||||
|
action.getActionExps()[action.getActionExpIndex()] = actionExp;
|
||||||
|
|
||||||
|
if (interactedNPC != npc)
|
||||||
|
{
|
||||||
|
action.setActionExpIndex((action.getActionExpIndex() + 1) % action.getActionExps().length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// So we have a decent average off the bat, lets populate all values with what we see.
|
||||||
|
for (int i = 0; i < action.getActionExps().length; i++)
|
||||||
|
{
|
||||||
|
action.getActionExps()[i] = actionExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
action.setActionsHistoryInitialized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
interactedNPC = npc;
|
||||||
|
state.setActionType(XpActionType.ACTOR_HEALTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update number of actions performed for skill (e.g amount of kills in this case) if last interacted
|
||||||
|
* NPC died
|
||||||
|
* @param skill skill to update actions for
|
||||||
|
* @param npc npc that just died
|
||||||
|
* @param npcHealth max health of npc that just died
|
||||||
|
* @return UPDATED in case new kill was successfully added
|
||||||
|
*/
|
||||||
|
XpUpdateResult updateNpcKills(Skill skill, NPC npc, Integer npcHealth)
|
||||||
|
{
|
||||||
|
XpStateSingle state = getSkill(skill);
|
||||||
|
|
||||||
|
if (state.getXpGained() <= 0 || npcHealth == null || npc != interactedNPC)
|
||||||
|
{
|
||||||
|
return XpUpdateResult.NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
final XpAction xpAction = state.getXpAction(XpActionType.ACTOR_HEALTH);
|
||||||
|
xpAction.setActions(xpAction.getActions() + 1);
|
||||||
|
return xpAction.isActionsHistoryInitialized() ? XpUpdateResult.UPDATED : XpUpdateResult.NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick(Skill skill, long delta)
|
||||||
|
{
|
||||||
|
getSkill(skill).tick(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefully initialize a skill with a known start XP from the current XP.
|
||||||
|
* This is used in resetAndInitState by the plugin. It should not result in showing the XP in the UI.
|
||||||
|
* @param skill Skill to initialize
|
||||||
|
* @param currentXp Current known XP for the skill
|
||||||
|
*/
|
||||||
|
void initializeSkill(Skill skill, long currentXp)
|
||||||
|
{
|
||||||
|
xpSkills.put(skill, new XpStateSingle(skill, currentXp));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isInitialized(Skill skill)
|
||||||
|
{
|
||||||
|
XpStateSingle xpStateSingle = xpSkills.get(skill);
|
||||||
|
return xpStateSingle != null && xpStateSingle.getStartXp() != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
XpStateSingle getSkill(Skill skill)
|
||||||
|
{
|
||||||
|
return xpSkills.computeIfAbsent(skill, (s) -> new XpStateSingle(s, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain an immutable snapshot of the provided skill
|
||||||
|
* intended for use with the UI which operates on another thread
|
||||||
|
* @param skill Skill to obtain the snapshot for
|
||||||
|
* @return An immutable snapshot of the specified skill for this session since first login or last reset
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
XpSnapshotSingle getSkillSnapshot(Skill skill)
|
||||||
|
{
|
||||||
|
return getSkill(skill).snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain an immutable snapshot of the provided skill
|
||||||
|
* intended for use with the UI which operates on another thread
|
||||||
|
* @return An immutable snapshot of total information for this session since first login or last reset
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
XpSnapshotSingle getTotalSnapshot()
|
||||||
|
{
|
||||||
|
return getSkill(Skill.OVERALL).snapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Cameron <moberg@tuta.io>
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.com>
|
||||||
|
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.Experience;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
class XpStateSingle
|
||||||
|
{
|
||||||
|
private final Skill skill;
|
||||||
|
private final Map<XpActionType, XpAction> actions = new HashMap<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private long startXp;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private int xpGained = 0;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private XpActionType actionType = XpActionType.EXPERIENCE;
|
||||||
|
|
||||||
|
private long skillTime = 0;
|
||||||
|
private int startLevelExp = 0;
|
||||||
|
private int endLevelExp = 0;
|
||||||
|
|
||||||
|
XpStateSingle(Skill skill, long startXp)
|
||||||
|
{
|
||||||
|
this.skill = skill;
|
||||||
|
this.startXp = startXp;
|
||||||
|
}
|
||||||
|
|
||||||
|
XpAction getXpAction(final XpActionType type)
|
||||||
|
{
|
||||||
|
actions.putIfAbsent(type, new XpAction());
|
||||||
|
return actions.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
long getCurrentXp()
|
||||||
|
{
|
||||||
|
return startXp + xpGained;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getActionsHr()
|
||||||
|
{
|
||||||
|
return toHourly(getXpAction(actionType).getActions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int toHourly(int value)
|
||||||
|
{
|
||||||
|
return (int) ((1.0 / (getTimeElapsedInSeconds() / 3600.0)) * value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getTimeElapsedInSeconds()
|
||||||
|
{
|
||||||
|
// If the skill started just now, we can divide by near zero, this results in odd behavior.
|
||||||
|
// To prevent that, pretend the skill has been active for a minute (60 seconds)
|
||||||
|
// This will create a lower estimate for the first minute,
|
||||||
|
// but it isn't ridiculous like saying 2 billion XP per hour.
|
||||||
|
return Math.max(60, skillTime / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getXpRemaining()
|
||||||
|
{
|
||||||
|
return endLevelExp - (int) getCurrentXp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getActionsRemaining()
|
||||||
|
{
|
||||||
|
final XpAction action = getXpAction(actionType);
|
||||||
|
|
||||||
|
if (action.isActionsHistoryInitialized())
|
||||||
|
{
|
||||||
|
long xpRemaining = getXpRemaining() * action.getActionExps().length;
|
||||||
|
long totalActionXp = 0;
|
||||||
|
|
||||||
|
for (int actionXp : action.getActionExps())
|
||||||
|
{
|
||||||
|
totalActionXp += actionXp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's not divide by zero (or negative)
|
||||||
|
if (totalActionXp > 0)
|
||||||
|
{
|
||||||
|
// Make sure to account for the very last action at the end
|
||||||
|
long remainder = xpRemaining % totalActionXp;
|
||||||
|
long quotient = xpRemaining / totalActionXp;
|
||||||
|
return Math.toIntExact(quotient + (remainder > 0 ? 1 : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getSkillProgress()
|
||||||
|
{
|
||||||
|
double xpGained = getCurrentXp() - startLevelExp;
|
||||||
|
double xpGoal = endLevelExp - startLevelExp;
|
||||||
|
return (xpGained / xpGoal) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getSecondsTillLevel()
|
||||||
|
{
|
||||||
|
// Java 8 doesn't have good duration / period objects to represent spans of time that can be formatted
|
||||||
|
// Rather than importing another dependency like joda time (which is practically built into java 10)
|
||||||
|
// below will be a custom formatter that handles spans larger than 1 day
|
||||||
|
long seconds = getTimeElapsedInSeconds();
|
||||||
|
|
||||||
|
if (seconds <= 0 || xpGained <= 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// formula is xpRemaining / xpPerSecond
|
||||||
|
// xpPerSecond being xpGained / seconds
|
||||||
|
// This can be simplified so division is only done once and we can work in whole numbers!
|
||||||
|
return (getXpRemaining() * seconds) / xpGained;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTimeTillLevel(XpGoalTimeType goalTimeType)
|
||||||
|
{
|
||||||
|
long remainingSeconds = getSecondsTillLevel();
|
||||||
|
if (remainingSeconds < 0)
|
||||||
|
{
|
||||||
|
return "\u221e";
|
||||||
|
}
|
||||||
|
|
||||||
|
long durationDays = remainingSeconds / (24 * 60 * 60);
|
||||||
|
long durationHours = (remainingSeconds % (24 * 60 * 60)) / (60 * 60);
|
||||||
|
long durationHoursTotal = remainingSeconds / (60 * 60);
|
||||||
|
long durationMinutes = (remainingSeconds % (60 * 60)) / 60;
|
||||||
|
long durationSeconds = remainingSeconds % 60;
|
||||||
|
|
||||||
|
switch (goalTimeType)
|
||||||
|
{
|
||||||
|
case DAYS:
|
||||||
|
if (durationDays > 1)
|
||||||
|
{
|
||||||
|
return String.format("%d days %02d:%02d:%02d", durationDays, durationHours, durationMinutes, durationSeconds);
|
||||||
|
}
|
||||||
|
else if (durationDays == 1)
|
||||||
|
{
|
||||||
|
return String.format("1 day %02d:%02d:%02d", durationHours, durationMinutes, durationSeconds);
|
||||||
|
}
|
||||||
|
case HOURS:
|
||||||
|
if (durationHoursTotal > 1)
|
||||||
|
{
|
||||||
|
return String.format("%d hours %02d:%02d", durationHoursTotal, durationMinutes, durationSeconds);
|
||||||
|
}
|
||||||
|
else if (durationHoursTotal == 1)
|
||||||
|
{
|
||||||
|
return String.format("1 hour %02d:%02d", durationMinutes, durationSeconds);
|
||||||
|
}
|
||||||
|
case SHORT:
|
||||||
|
default:
|
||||||
|
// durationDays = 0 or durationHoursTotal = 0 or goalTimeType = SHORT if we got here.
|
||||||
|
// return time remaining in hh:mm:ss or mm:ss format where hh can be > 24
|
||||||
|
if (durationHoursTotal > 0)
|
||||||
|
{
|
||||||
|
return String.format("%02d:%02d:%02d", durationHoursTotal, durationMinutes, durationSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minutes and seconds will always be present
|
||||||
|
return String.format("%02d:%02d", durationMinutes, durationSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getXpHr()
|
||||||
|
{
|
||||||
|
return toHourly(xpGained);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean update(long currentXp, int goalStartXp, int goalEndXp)
|
||||||
|
{
|
||||||
|
if (startXp == -1)
|
||||||
|
{
|
||||||
|
log.warn("Attempted to update skill state " + skill + " but was not initialized with current xp");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long originalXp = xpGained + startXp;
|
||||||
|
int actionExp = (int) (currentXp - originalXp);
|
||||||
|
|
||||||
|
// No experience gained
|
||||||
|
if (actionExp == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update EXPERIENCE action
|
||||||
|
final XpAction action = getXpAction(XpActionType.EXPERIENCE);
|
||||||
|
|
||||||
|
if (action.isActionsHistoryInitialized())
|
||||||
|
{
|
||||||
|
action.getActionExps()[action.getActionExpIndex()] = actionExp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// populate all values in our action history array with this first value that we see
|
||||||
|
// so the average value of our action history starts out as this first value we see
|
||||||
|
for (int i = 0; i < action.getActionExps().length; i++)
|
||||||
|
{
|
||||||
|
action.getActionExps()[i] = actionExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
action.setActionsHistoryInitialized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
action.setActionExpIndex((action.getActionExpIndex() + 1) % action.getActionExps().length);
|
||||||
|
action.setActions(action.getActions() + 1);
|
||||||
|
|
||||||
|
// Calculate experience gained
|
||||||
|
xpGained = (int) (currentXp - startXp);
|
||||||
|
|
||||||
|
// Determine XP goals, overall has no goals
|
||||||
|
if (skill != Skill.OVERALL)
|
||||||
|
{
|
||||||
|
if (goalStartXp < 0 || currentXp > goalEndXp)
|
||||||
|
{
|
||||||
|
startLevelExp = Experience.getXpForLevel(Experience.getLevelForXp((int) currentXp));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
startLevelExp = goalStartXp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (goalEndXp <= 0 || currentXp > goalEndXp)
|
||||||
|
{
|
||||||
|
int currentLevel = Experience.getLevelForXp((int) currentXp);
|
||||||
|
endLevelExp = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL
|
||||||
|
? Experience.getXpForLevel(currentLevel + 1)
|
||||||
|
: Experience.MAX_SKILL_XP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
endLevelExp = goalEndXp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick(long delta)
|
||||||
|
{
|
||||||
|
// Don't tick skills that have not gained XP or have been reset.
|
||||||
|
if (xpGained <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skillTime += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
XpSnapshotSingle snapshot()
|
||||||
|
{
|
||||||
|
return XpSnapshotSingle.builder()
|
||||||
|
.startLevel(Experience.getLevelForXp(startLevelExp))
|
||||||
|
.endLevel(Experience.getLevelForXp(endLevelExp))
|
||||||
|
.xpGainedInSession(xpGained)
|
||||||
|
.xpRemainingToGoal(getXpRemaining())
|
||||||
|
.xpPerHour(getXpHr())
|
||||||
|
.skillProgressToGoal(getSkillProgress())
|
||||||
|
.actionType(actionType)
|
||||||
|
.actionsInSession(getXpAction(actionType).getActions())
|
||||||
|
.actionsRemainingToGoal(getActionsRemaining())
|
||||||
|
.actionsPerHour(getActionsHr())
|
||||||
|
.timeTillGoal(getTimeTillLevel(XpGoalTimeType.DAYS))
|
||||||
|
.timeTillGoalHours(getTimeTillLevel(XpGoalTimeType.HOURS))
|
||||||
|
.timeTillGoalShort(getTimeTillLevel(XpGoalTimeType.SHORT))
|
||||||
|
.startGoalXp(startLevelExp)
|
||||||
|
.endGoalXp(endLevelExp)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.com>
|
||||||
|
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||||
|
* 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.xptracker;
|
||||||
|
|
||||||
|
import net.runelite.client.config.Config;
|
||||||
|
import net.runelite.client.config.ConfigGroup;
|
||||||
|
import net.runelite.client.config.ConfigItem;
|
||||||
|
import net.runelite.client.config.ConfigSection;
|
||||||
|
import net.runelite.client.config.Units;
|
||||||
|
|
||||||
|
@ConfigGroup("xpTracker")
|
||||||
|
public interface XpTrackerConfig extends Config
|
||||||
|
{
|
||||||
|
@ConfigSection(
|
||||||
|
name = "Overlay",
|
||||||
|
description = "Canvas overlay options",
|
||||||
|
position = 99
|
||||||
|
)
|
||||||
|
String overlaySection = "overlay";
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 0,
|
||||||
|
keyName = "hideMaxed",
|
||||||
|
name = "Hide maxed skills",
|
||||||
|
description = "Stop globes from showing up for level 99 skills "
|
||||||
|
)
|
||||||
|
default boolean hideMaxed()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 1,
|
||||||
|
keyName = "logoutPausing",
|
||||||
|
name = "Pause on Logout",
|
||||||
|
description = "Configures whether skills should pause on logout"
|
||||||
|
)
|
||||||
|
default boolean pauseOnLogout()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 2,
|
||||||
|
keyName = "intermediateLevelMarkers",
|
||||||
|
name = "Show intermediate level markers",
|
||||||
|
description = "Marks intermediate levels on the progressbar"
|
||||||
|
)
|
||||||
|
default boolean showIntermediateLevels()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 3,
|
||||||
|
keyName = "pauseSkillAfter",
|
||||||
|
name = "Auto pause after",
|
||||||
|
description = "Configures how many minutes passes before pausing a skill while in game and there's no XP, 0 means disabled"
|
||||||
|
)
|
||||||
|
@Units(Units.MINUTES)
|
||||||
|
default int pauseSkillAfter()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 4,
|
||||||
|
keyName = "skillTabOverlayMenuOptions",
|
||||||
|
name = "Add skill tab canvas menu option",
|
||||||
|
description = "Configures whether a menu option to show/hide canvas XP trackers will be added to skills on the skill tab",
|
||||||
|
section = overlaySection
|
||||||
|
)
|
||||||
|
default boolean skillTabOverlayMenuOptions()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 5,
|
||||||
|
keyName = "onScreenDisplayMode",
|
||||||
|
name = "On-screen tracker display mode (top)",
|
||||||
|
description = "Configures the information displayed in the first line of on-screen XP overlays",
|
||||||
|
section = overlaySection
|
||||||
|
)
|
||||||
|
default XpPanelLabel onScreenDisplayMode()
|
||||||
|
{
|
||||||
|
return XpPanelLabel.XP_GAINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 6,
|
||||||
|
keyName = "onScreenDisplayModeBottom",
|
||||||
|
name = "On-screen tracker display mode (bottom)",
|
||||||
|
description = "Configures the information displayed in the second line of on-screen XP overlays",
|
||||||
|
section = overlaySection
|
||||||
|
)
|
||||||
|
default XpPanelLabel onScreenDisplayModeBottom()
|
||||||
|
{
|
||||||
|
return XpPanelLabel.XP_HOUR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 7,
|
||||||
|
keyName = "xpPanelLabel1",
|
||||||
|
name = "Top-left XP info label",
|
||||||
|
description = "Configures the information displayed in the top-left of XP info box"
|
||||||
|
)
|
||||||
|
default XpPanelLabel xpPanelLabel1()
|
||||||
|
{
|
||||||
|
return XpPanelLabel.XP_GAINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 8,
|
||||||
|
keyName = "xpPanelLabel2",
|
||||||
|
name = "Top-right XP info label",
|
||||||
|
description = "Configures the information displayed in the top-right of XP info box"
|
||||||
|
)
|
||||||
|
|
||||||
|
default XpPanelLabel xpPanelLabel2()
|
||||||
|
{
|
||||||
|
return XpPanelLabel.XP_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 9,
|
||||||
|
keyName = "xpPanelLabel3",
|
||||||
|
name = "Bottom-left XP info label",
|
||||||
|
description = "Configures the information displayed in the bottom-left of XP info box"
|
||||||
|
)
|
||||||
|
default XpPanelLabel xpPanelLabel3()
|
||||||
|
{
|
||||||
|
return XpPanelLabel.XP_HOUR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 10,
|
||||||
|
keyName = "xpPanelLabel4",
|
||||||
|
name = "Bottom-right XP info label",
|
||||||
|
description = "Configures the information displayed in the bottom-right of XP info box"
|
||||||
|
)
|
||||||
|
default XpPanelLabel xpPanelLabel4()
|
||||||
|
{
|
||||||
|
return XpPanelLabel.ACTIONS_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 11,
|
||||||
|
keyName = "progressBarLabel",
|
||||||
|
name = "Progress bar label",
|
||||||
|
description = "Configures the info box progress bar to show Time to goal or percentage complete"
|
||||||
|
)
|
||||||
|
default XpProgressBarLabel progressBarLabel()
|
||||||
|
{
|
||||||
|
return XpProgressBarLabel.PERCENTAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 12,
|
||||||
|
keyName = "progressBarTooltipLabel",
|
||||||
|
name = "Tooltip label",
|
||||||
|
description = "Configures the info box progress bar tooltip to show Time to goal or percentage complete"
|
||||||
|
)
|
||||||
|
default XpProgressBarLabel progressBarTooltipLabel()
|
||||||
|
{
|
||||||
|
return XpProgressBarLabel.TIME_TO_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
position = 13,
|
||||||
|
keyName = "prioritizeRecentXpSkills",
|
||||||
|
name = "Move recently trained skills to top",
|
||||||
|
description = "Configures whether skills should be organized by most recently gained xp"
|
||||||
|
)
|
||||||
|
default boolean prioritizeRecentXpSkills()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,749 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Cameron <moberg@tuta.io>
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.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.plugins.xptracker;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.Actor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Experience;
|
||||||
|
import net.runelite.api.GameState;
|
||||||
|
import net.runelite.api.MenuAction;
|
||||||
|
import net.runelite.api.MenuEntry;
|
||||||
|
import net.runelite.api.NPC;
|
||||||
|
import net.runelite.api.Player;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
import net.runelite.api.VarPlayer;
|
||||||
|
import net.runelite.api.WorldType;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
|
import net.runelite.api.events.GameTick;
|
||||||
|
import net.runelite.api.events.MenuEntryAdded;
|
||||||
|
import net.runelite.api.events.MenuOptionClicked;
|
||||||
|
import net.runelite.api.events.NpcDespawned;
|
||||||
|
import net.runelite.api.events.StatChanged;
|
||||||
|
import net.runelite.api.widgets.WidgetID;
|
||||||
|
import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.game.NPCManager;
|
||||||
|
import net.runelite.client.game.SkillIconManager;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import static net.runelite.client.plugins.xptracker.XpWorldType.NORMAL;
|
||||||
|
import net.runelite.client.task.Schedule;
|
||||||
|
import net.runelite.client.ui.ClientToolbar;
|
||||||
|
import net.runelite.client.ui.NavigationButton;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayManager;
|
||||||
|
import net.runelite.client.util.ImageUtil;
|
||||||
|
import net.runelite.client.util.Text;
|
||||||
|
import net.runelite.http.api.xp.XpClient;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
@PluginDescriptor(
|
||||||
|
name = "XP Tracker",
|
||||||
|
description = "Enable the XP Tracker panel",
|
||||||
|
tags = {"experience", "levels", "panel"}
|
||||||
|
)
|
||||||
|
@Slf4j
|
||||||
|
public class XpTrackerPlugin extends Plugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Amount of EXP that must be gained for an update to be submitted.
|
||||||
|
*/
|
||||||
|
private static final int XP_THRESHOLD = 10_000;
|
||||||
|
|
||||||
|
private static final String MENUOP_ADD_CANVAS_TRACKER = "Add to canvas";
|
||||||
|
private static final String MENUOP_REMOVE_CANVAS_TRACKER = "Remove from canvas";
|
||||||
|
|
||||||
|
static final List<Skill> COMBAT = ImmutableList.of(
|
||||||
|
Skill.ATTACK,
|
||||||
|
Skill.STRENGTH,
|
||||||
|
Skill.DEFENCE,
|
||||||
|
Skill.RANGED,
|
||||||
|
Skill.HITPOINTS,
|
||||||
|
Skill.MAGIC);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ClientToolbar clientToolbar;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SkillIconManager skillIconManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private XpTrackerConfig xpTrackerConfig;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private NPCManager npcManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private OverlayManager overlayManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private XpClient xpClient;
|
||||||
|
|
||||||
|
private NavigationButton navButton;
|
||||||
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
@VisibleForTesting
|
||||||
|
private XpPanel xpPanel;
|
||||||
|
private XpWorldType lastWorldType;
|
||||||
|
private String lastUsername;
|
||||||
|
private long lastTickMillis = 0;
|
||||||
|
private boolean fetchXp; // fetch lastXp for the online xp tracker
|
||||||
|
private long lastXp = 0;
|
||||||
|
private boolean initializeTracker;
|
||||||
|
|
||||||
|
private final XpState xpState = new XpState();
|
||||||
|
private final XpPauseState xpPauseState = new XpPauseState();
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
XpTrackerConfig provideConfig(ConfigManager configManager)
|
||||||
|
{
|
||||||
|
return configManager.getConfig(XpTrackerConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
XpClient provideXpClient(OkHttpClient okHttpClient)
|
||||||
|
{
|
||||||
|
return new XpClient(okHttpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(Binder binder)
|
||||||
|
{
|
||||||
|
binder.bind(XpTrackerService.class).to(XpTrackerServiceImpl.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp() throws Exception
|
||||||
|
{
|
||||||
|
xpPanel = new XpPanel(this, xpTrackerConfig, client, skillIconManager);
|
||||||
|
|
||||||
|
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/overall.png");
|
||||||
|
|
||||||
|
navButton = NavigationButton.builder()
|
||||||
|
.tooltip("XP Tracker")
|
||||||
|
.icon(icon)
|
||||||
|
.priority(2)
|
||||||
|
.panel(xpPanel)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
clientToolbar.addNavigation(navButton);
|
||||||
|
|
||||||
|
// Initialize the tracker & last xp if already logged in
|
||||||
|
fetchXp = true;
|
||||||
|
initializeTracker = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown() throws Exception
|
||||||
|
{
|
||||||
|
overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay);
|
||||||
|
xpState.reset();
|
||||||
|
clientToolbar.removeNavigation(navButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameStateChanged(GameStateChanged event)
|
||||||
|
{
|
||||||
|
GameState state = event.getGameState();
|
||||||
|
if (state == GameState.LOGGED_IN)
|
||||||
|
{
|
||||||
|
// LOGGED_IN is triggered between region changes too.
|
||||||
|
// Check that the username changed or the world type changed.
|
||||||
|
XpWorldType type = worldSetToType(client.getWorldType());
|
||||||
|
|
||||||
|
if (!Objects.equals(client.getUsername(), lastUsername) || lastWorldType != type)
|
||||||
|
{
|
||||||
|
// Reset
|
||||||
|
log.debug("World change: {} -> {}, {} -> {}",
|
||||||
|
lastUsername, client.getUsername(),
|
||||||
|
firstNonNull(lastWorldType, "<unknown>"),
|
||||||
|
firstNonNull(type, "<unknown>"));
|
||||||
|
|
||||||
|
lastUsername = client.getUsername();
|
||||||
|
// xp is not available until after login is finished, so fetch it on the next gametick
|
||||||
|
fetchXp = true;
|
||||||
|
lastWorldType = type;
|
||||||
|
resetState();
|
||||||
|
// Must be set from hitting the LOGGING_IN or HOPPING case below
|
||||||
|
assert initializeTracker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state == GameState.LOGGING_IN || state == GameState.HOPPING)
|
||||||
|
{
|
||||||
|
initializeTracker = true;
|
||||||
|
}
|
||||||
|
else if (state == GameState.LOGIN_SCREEN)
|
||||||
|
{
|
||||||
|
Player local = client.getLocalPlayer();
|
||||||
|
if (local == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String username = local.getName();
|
||||||
|
if (username == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalXp = client.getOverallExperience();
|
||||||
|
// Don't submit xptrack unless xp threshold is reached
|
||||||
|
if (Math.abs(totalXp - lastXp) > XP_THRESHOLD)
|
||||||
|
{
|
||||||
|
xpClient.update(username);
|
||||||
|
lastXp = totalXp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private XpWorldType worldSetToType(EnumSet<WorldType> types)
|
||||||
|
{
|
||||||
|
XpWorldType xpType = NORMAL;
|
||||||
|
for (WorldType type : types)
|
||||||
|
{
|
||||||
|
XpWorldType t = XpWorldType.of(type);
|
||||||
|
if (t != NORMAL)
|
||||||
|
{
|
||||||
|
xpType = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an overlay to the canvas for tracking a specific skill.
|
||||||
|
*
|
||||||
|
* @param skill the skill for which the overlay should be added
|
||||||
|
*/
|
||||||
|
void addOverlay(Skill skill)
|
||||||
|
{
|
||||||
|
removeOverlay(skill);
|
||||||
|
overlayManager.add(new XpInfoBoxOverlay(this, xpTrackerConfig, skill, skillIconManager.getSkillImage(skill)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an overlay from the overlayManager if it's present.
|
||||||
|
*
|
||||||
|
* @param skill the skill for which the overlay should be removed.
|
||||||
|
*/
|
||||||
|
void removeOverlay(Skill skill)
|
||||||
|
{
|
||||||
|
overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay && ((XpInfoBoxOverlay) e).getSkill() == skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is an overlay on the canvas for the skill.
|
||||||
|
*
|
||||||
|
* @param skill the skill which should have an overlay.
|
||||||
|
* @return true if the skill has an overlay.
|
||||||
|
*/
|
||||||
|
boolean hasOverlay(final Skill skill)
|
||||||
|
{
|
||||||
|
return overlayManager.anyMatch(o -> o instanceof XpInfoBoxOverlay && ((XpInfoBoxOverlay) o).getSkill() == skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset internal state and re-initialize all skills with XP currently cached by the RS client
|
||||||
|
* This is called by the user manually clicking resetSkillState in the UI.
|
||||||
|
* It reloads the current skills from the client after resetting internal state.
|
||||||
|
*/
|
||||||
|
void resetAndInitState()
|
||||||
|
{
|
||||||
|
resetState();
|
||||||
|
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
long currentXp;
|
||||||
|
if (skill == Skill.OVERALL)
|
||||||
|
{
|
||||||
|
currentXp = client.getOverallExperience();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentXp = client.getSkillExperience(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
xpState.initializeSkill(skill, currentXp);
|
||||||
|
removeOverlay(skill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw out everything, the user has chosen a different account or world type.
|
||||||
|
* This resets both the internal state and UI elements
|
||||||
|
*/
|
||||||
|
private void resetState()
|
||||||
|
{
|
||||||
|
xpState.reset();
|
||||||
|
xpPanel.resetAllInfoBoxes();
|
||||||
|
xpPanel.updateTotal(new XpSnapshotSingle.XpSnapshotSingleBuilder().build());
|
||||||
|
overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset an individual skill with the client's current known state of the skill
|
||||||
|
* Will also clear the skill from the UI.
|
||||||
|
* @param skill Skill to reset
|
||||||
|
*/
|
||||||
|
void resetSkillState(Skill skill)
|
||||||
|
{
|
||||||
|
int currentXp = client.getSkillExperience(skill);
|
||||||
|
xpState.resetSkill(skill, currentXp);
|
||||||
|
xpPanel.resetSkill(skill);
|
||||||
|
removeOverlay(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all skills except for the one provided
|
||||||
|
* @param skill Skill to ignore during reset
|
||||||
|
*/
|
||||||
|
void resetOtherSkillState(Skill skill)
|
||||||
|
{
|
||||||
|
for (Skill s : Skill.values())
|
||||||
|
{
|
||||||
|
// Overall is not reset from resetting individual skills
|
||||||
|
if (skill != s && s != Skill.OVERALL)
|
||||||
|
{
|
||||||
|
resetSkillState(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatChanged(StatChanged statChanged)
|
||||||
|
{
|
||||||
|
final Skill skill = statChanged.getSkill();
|
||||||
|
final int currentXp = statChanged.getXp();
|
||||||
|
final int currentLevel = statChanged.getLevel();
|
||||||
|
final VarPlayer startGoal = startGoalVarpForSkill(skill);
|
||||||
|
final VarPlayer endGoal = endGoalVarpForSkill(skill);
|
||||||
|
final int startGoalXp = startGoal != null ? client.getVar(startGoal) : -1;
|
||||||
|
final int endGoalXp = endGoal != null ? client.getVar(endGoal) : -1;
|
||||||
|
|
||||||
|
if (initializeTracker)
|
||||||
|
{
|
||||||
|
// This is the XP sync on login, wait until after login to begin counting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xpTrackerConfig.hideMaxed() && currentLevel >= Experience.MAX_REAL_LEVEL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final XpStateSingle state = xpState.getSkill(skill);
|
||||||
|
state.setActionType(XpActionType.EXPERIENCE);
|
||||||
|
|
||||||
|
final Actor interacting = client.getLocalPlayer().getInteracting();
|
||||||
|
if (interacting instanceof NPC && COMBAT.contains(skill))
|
||||||
|
{
|
||||||
|
final int xpModifier = worldSetToType(client.getWorldType()).modifier(client);;
|
||||||
|
final NPC npc = (NPC) interacting;
|
||||||
|
xpState.updateNpcExperience(skill, npc, npcManager.getHealth(npc.getId()), xpModifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp);
|
||||||
|
xpPanel.updateSkillExperience(updateResult == XpUpdateResult.UPDATED, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
|
||||||
|
|
||||||
|
// Also update the total experience
|
||||||
|
xpState.updateSkill(Skill.OVERALL, client.getOverallExperience(), -1, -1);
|
||||||
|
xpPanel.updateTotal(xpState.getTotalSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onNpcDespawned(NpcDespawned event)
|
||||||
|
{
|
||||||
|
final NPC npc = event.getNpc();
|
||||||
|
|
||||||
|
if (!npc.isDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Skill skill : COMBAT)
|
||||||
|
{
|
||||||
|
final XpUpdateResult updateResult = xpState.updateNpcKills(skill, npc, npcManager.getHealth(npc.getId()));
|
||||||
|
final boolean updated = XpUpdateResult.UPDATED.equals(updateResult);
|
||||||
|
xpPanel.updateSkillExperience(updated, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
|
||||||
|
}
|
||||||
|
|
||||||
|
xpPanel.updateTotal(xpState.getTotalSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameTick(GameTick event)
|
||||||
|
{
|
||||||
|
if (initializeTracker)
|
||||||
|
{
|
||||||
|
initializeTracker = false;
|
||||||
|
|
||||||
|
// Check for xp gained while logged out
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
if (skill == Skill.OVERALL || !xpState.isInitialized(skill))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
XpStateSingle skillState = xpState.getSkill(skill);
|
||||||
|
final int currentXp = client.getSkillExperience(skill);
|
||||||
|
if (skillState.getCurrentXp() != currentXp)
|
||||||
|
{
|
||||||
|
if (currentXp < skillState.getCurrentXp())
|
||||||
|
{
|
||||||
|
log.debug("Xp is going backwards! {} {} -> {}", skill, skillState.getCurrentXp(), currentXp);
|
||||||
|
resetState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Skill xp for {} changed when offline: {} -> {}", skill, skillState.getCurrentXp(), currentXp);
|
||||||
|
// Offset start xp for offline gains
|
||||||
|
long diff = currentXp - skillState.getCurrentXp();
|
||||||
|
skillState.setStartXp(skillState.getStartXp() + diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the tracker with the initial xp if not already initialized
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
if (skill == Skill.OVERALL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xpState.isInitialized(skill))
|
||||||
|
{
|
||||||
|
final int currentXp = client.getSkillExperience(skill);
|
||||||
|
// goal exps are not necessary for skill initialization
|
||||||
|
XpUpdateResult xpUpdateResult = xpState.updateSkill(skill, currentXp, -1, -1);
|
||||||
|
assert xpUpdateResult == XpUpdateResult.INITIALIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the overall xp
|
||||||
|
if (!xpState.isInitialized(Skill.OVERALL))
|
||||||
|
{
|
||||||
|
long overallXp = client.getOverallExperience();
|
||||||
|
log.debug("Initializing XP tracker with {} overall exp", overallXp);
|
||||||
|
xpState.initializeSkill(Skill.OVERALL, overallXp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchXp)
|
||||||
|
{
|
||||||
|
lastXp = client.getOverallExperience();
|
||||||
|
fetchXp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuildSkills();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onMenuEntryAdded(final MenuEntryAdded event)
|
||||||
|
{
|
||||||
|
int widgetID = event.getActionParam1();
|
||||||
|
|
||||||
|
if (TO_GROUP(widgetID) != WidgetID.SKILLS_GROUP_ID
|
||||||
|
|| !event.getOption().startsWith("View")
|
||||||
|
|| !xpTrackerConfig.skillTabOverlayMenuOptions())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get skill from menu option, eg. "View <col=ff981f>Attack</col> guide"
|
||||||
|
final String skillText = event.getOption().split(" ")[1];
|
||||||
|
final Skill skill = Skill.valueOf(Text.removeTags(skillText).toUpperCase());
|
||||||
|
|
||||||
|
MenuEntry[] menuEntries = client.getMenuEntries();
|
||||||
|
menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1);
|
||||||
|
|
||||||
|
MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry();
|
||||||
|
menuEntry.setTarget(skillText);
|
||||||
|
menuEntry.setOption(hasOverlay(skill) ? MENUOP_REMOVE_CANVAS_TRACKER : MENUOP_ADD_CANVAS_TRACKER);
|
||||||
|
menuEntry.setParam0(event.getActionParam0());
|
||||||
|
menuEntry.setParam1(widgetID);
|
||||||
|
menuEntry.setType(MenuAction.RUNELITE.getId());
|
||||||
|
|
||||||
|
client.setMenuEntries(menuEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onMenuOptionClicked(MenuOptionClicked event)
|
||||||
|
{
|
||||||
|
if (event.getMenuAction().getId() != MenuAction.RUNELITE.getId()
|
||||||
|
|| TO_GROUP(event.getWidgetId()) != WidgetID.SKILLS_GROUP_ID)
|
||||||
|
{
|
||||||
|
System.out.println("opcode " + event.getMenuAction());
|
||||||
|
System.out.println("id " + event.getMenuAction().getId());
|
||||||
|
System.out.println("group " + TO_GROUP(event.getWidgetId()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Skill skill;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
skill = Skill.valueOf(Text.removeTags(event.getMenuTarget()).toUpperCase());
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException ex)
|
||||||
|
{
|
||||||
|
log.debug(null, ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.getMenuOption())
|
||||||
|
{
|
||||||
|
case MENUOP_ADD_CANVAS_TRACKER:
|
||||||
|
addOverlay(skill);
|
||||||
|
break;
|
||||||
|
case MENUOP_REMOVE_CANVAS_TRACKER:
|
||||||
|
removeOverlay(skill);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println(event.getMenuOption());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XpStateSingle getSkillState(Skill skill)
|
||||||
|
{
|
||||||
|
return xpState.getSkill(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
XpSnapshotSingle getSkillSnapshot(Skill skill)
|
||||||
|
{
|
||||||
|
return xpState.getSkillSnapshot(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VarPlayer startGoalVarpForSkill(final Skill skill)
|
||||||
|
{
|
||||||
|
switch (skill)
|
||||||
|
{
|
||||||
|
case ATTACK:
|
||||||
|
return VarPlayer.ATTACK_GOAL_START;
|
||||||
|
case MINING:
|
||||||
|
return VarPlayer.MINING_GOAL_START;
|
||||||
|
case WOODCUTTING:
|
||||||
|
return VarPlayer.WOODCUTTING_GOAL_START;
|
||||||
|
case DEFENCE:
|
||||||
|
return VarPlayer.DEFENCE_GOAL_START;
|
||||||
|
case MAGIC:
|
||||||
|
return VarPlayer.MAGIC_GOAL_START;
|
||||||
|
case RANGED:
|
||||||
|
return VarPlayer.RANGED_GOAL_START;
|
||||||
|
case HITPOINTS:
|
||||||
|
return VarPlayer.HITPOINTS_GOAL_START;
|
||||||
|
case AGILITY:
|
||||||
|
return VarPlayer.AGILITY_GOAL_START;
|
||||||
|
case STRENGTH:
|
||||||
|
return VarPlayer.STRENGTH_GOAL_START;
|
||||||
|
case PRAYER:
|
||||||
|
return VarPlayer.PRAYER_GOAL_START;
|
||||||
|
case SLAYER:
|
||||||
|
return VarPlayer.SLAYER_GOAL_START;
|
||||||
|
case FISHING:
|
||||||
|
return VarPlayer.FISHING_GOAL_START;
|
||||||
|
case RUNECRAFT:
|
||||||
|
return VarPlayer.RUNECRAFT_GOAL_START;
|
||||||
|
case HERBLORE:
|
||||||
|
return VarPlayer.HERBLORE_GOAL_START;
|
||||||
|
case FIREMAKING:
|
||||||
|
return VarPlayer.FIREMAKING_GOAL_START;
|
||||||
|
case CONSTRUCTION:
|
||||||
|
return VarPlayer.CONSTRUCTION_GOAL_START;
|
||||||
|
case HUNTER:
|
||||||
|
return VarPlayer.HUNTER_GOAL_START;
|
||||||
|
case COOKING:
|
||||||
|
return VarPlayer.COOKING_GOAL_START;
|
||||||
|
case FARMING:
|
||||||
|
return VarPlayer.FARMING_GOAL_START;
|
||||||
|
case CRAFTING:
|
||||||
|
return VarPlayer.CRAFTING_GOAL_START;
|
||||||
|
case SMITHING:
|
||||||
|
return VarPlayer.SMITHING_GOAL_START;
|
||||||
|
case THIEVING:
|
||||||
|
return VarPlayer.THIEVING_GOAL_START;
|
||||||
|
case FLETCHING:
|
||||||
|
return VarPlayer.FLETCHING_GOAL_START;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VarPlayer endGoalVarpForSkill(final Skill skill)
|
||||||
|
{
|
||||||
|
switch (skill)
|
||||||
|
{
|
||||||
|
case ATTACK:
|
||||||
|
return VarPlayer.ATTACK_GOAL_END;
|
||||||
|
case MINING:
|
||||||
|
return VarPlayer.MINING_GOAL_END;
|
||||||
|
case WOODCUTTING:
|
||||||
|
return VarPlayer.WOODCUTTING_GOAL_END;
|
||||||
|
case DEFENCE:
|
||||||
|
return VarPlayer.DEFENCE_GOAL_END;
|
||||||
|
case MAGIC:
|
||||||
|
return VarPlayer.MAGIC_GOAL_END;
|
||||||
|
case RANGED:
|
||||||
|
return VarPlayer.RANGED_GOAL_END;
|
||||||
|
case HITPOINTS:
|
||||||
|
return VarPlayer.HITPOINTS_GOAL_END;
|
||||||
|
case AGILITY:
|
||||||
|
return VarPlayer.AGILITY_GOAL_END;
|
||||||
|
case STRENGTH:
|
||||||
|
return VarPlayer.STRENGTH_GOAL_END;
|
||||||
|
case PRAYER:
|
||||||
|
return VarPlayer.PRAYER_GOAL_END;
|
||||||
|
case SLAYER:
|
||||||
|
return VarPlayer.SLAYER_GOAL_END;
|
||||||
|
case FISHING:
|
||||||
|
return VarPlayer.FISHING_GOAL_END;
|
||||||
|
case RUNECRAFT:
|
||||||
|
return VarPlayer.RUNECRAFT_GOAL_END;
|
||||||
|
case HERBLORE:
|
||||||
|
return VarPlayer.HERBLORE_GOAL_END;
|
||||||
|
case FIREMAKING:
|
||||||
|
return VarPlayer.FIREMAKING_GOAL_END;
|
||||||
|
case CONSTRUCTION:
|
||||||
|
return VarPlayer.CONSTRUCTION_GOAL_END;
|
||||||
|
case HUNTER:
|
||||||
|
return VarPlayer.HUNTER_GOAL_END;
|
||||||
|
case COOKING:
|
||||||
|
return VarPlayer.COOKING_GOAL_END;
|
||||||
|
case FARMING:
|
||||||
|
return VarPlayer.FARMING_GOAL_END;
|
||||||
|
case CRAFTING:
|
||||||
|
return VarPlayer.CRAFTING_GOAL_END;
|
||||||
|
case SMITHING:
|
||||||
|
return VarPlayer.SMITHING_GOAL_END;
|
||||||
|
case THIEVING:
|
||||||
|
return VarPlayer.THIEVING_GOAL_END;
|
||||||
|
case FLETCHING:
|
||||||
|
return VarPlayer.FLETCHING_GOAL_END;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schedule(
|
||||||
|
period = 1,
|
||||||
|
unit = ChronoUnit.SECONDS
|
||||||
|
)
|
||||||
|
public void tickSkillTimes()
|
||||||
|
{
|
||||||
|
// Adjust unpause states
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
long skillExperience;
|
||||||
|
if (skill == Skill.OVERALL)
|
||||||
|
{
|
||||||
|
skillExperience = client.getOverallExperience();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skillExperience = client.getSkillExperience(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
xpPauseState.tickXp(skill, skillExperience, xpTrackerConfig.pauseSkillAfter());
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean loggedIn;
|
||||||
|
switch (client.getGameState())
|
||||||
|
{
|
||||||
|
case LOGIN_SCREEN:
|
||||||
|
case LOGGING_IN:
|
||||||
|
case LOGIN_SCREEN_AUTHENTICATOR:
|
||||||
|
loggedIn = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
loggedIn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xpPauseState.tickLogout(xpTrackerConfig.pauseOnLogout(), loggedIn);
|
||||||
|
|
||||||
|
if (lastTickMillis == 0)
|
||||||
|
{
|
||||||
|
lastTickMillis = System.currentTimeMillis();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long nowMillis = System.currentTimeMillis();
|
||||||
|
final long tickDelta = nowMillis - lastTickMillis;
|
||||||
|
lastTickMillis = nowMillis;
|
||||||
|
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
if (!xpPauseState.isPaused(skill))
|
||||||
|
{
|
||||||
|
xpState.tick(skill, tickDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuildSkills();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebuildSkills()
|
||||||
|
{
|
||||||
|
// Rebuild calculated values like xp/hr in panel
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
xpPanel.updateSkillExperience(false, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
|
||||||
|
}
|
||||||
|
|
||||||
|
xpPanel.updateTotal(xpState.getTotalSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
|
void pauseSkill(Skill skill, boolean pause)
|
||||||
|
{
|
||||||
|
if (pause ? xpPauseState.pauseSkill(skill) : xpPauseState.unpauseSkill(skill))
|
||||||
|
{
|
||||||
|
xpPanel.updateSkillExperience(false, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pauseAllSkills(boolean pause)
|
||||||
|
{
|
||||||
|
for (Skill skill : Skill.values())
|
||||||
|
{
|
||||||
|
pauseSkill(skill, pause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.xptracker;
|
||||||
|
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
public interface XpTrackerService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the number of actions done
|
||||||
|
*/
|
||||||
|
int getActions(Skill skill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of actions per hour
|
||||||
|
*/
|
||||||
|
int getActionsHr(Skill skill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of actions remaining
|
||||||
|
*/
|
||||||
|
int getActionsLeft(Skill skill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the action type
|
||||||
|
*/
|
||||||
|
XpActionType getActionType(Skill skill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the amount of xp per hour
|
||||||
|
*/
|
||||||
|
int getXpHr(Skill skill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start goal XP
|
||||||
|
*/
|
||||||
|
int getStartGoalXp(Skill skill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the amount of XP left until goal level
|
||||||
|
*/
|
||||||
|
int getEndGoalXp(Skill skill);
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.xptracker;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import net.runelite.api.Skill;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class XpTrackerServiceImpl implements XpTrackerService
|
||||||
|
{
|
||||||
|
private final XpTrackerPlugin plugin;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
XpTrackerServiceImpl(XpTrackerPlugin plugin)
|
||||||
|
{
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getActions(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getActionsInSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getActionsHr(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getActionsPerHour();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getActionsLeft(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getActionsRemainingToGoal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XpActionType getActionType(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getActionType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getXpHr(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getXpPerHour();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStartGoalXp(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getStartGoalXp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEndGoalXp(Skill skill)
|
||||||
|
{
|
||||||
|
return plugin.getSkillSnapshot(skill).getEndGoalXp();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Levi <me@levischuck.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.plugins.xptracker;
|
||||||
|
|
||||||
|
enum XpUpdateResult
|
||||||
|
{
|
||||||
|
NO_CHANGE,
|
||||||
|
INITIALIZED,
|
||||||
|
UPDATED,
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, 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.client.plugins.xptracker;
|
||||||
|
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.Varbits;
|
||||||
|
import net.runelite.api.WorldType;
|
||||||
|
|
||||||
|
enum XpWorldType
|
||||||
|
{
|
||||||
|
NORMAL,
|
||||||
|
TOURNEY,
|
||||||
|
DMM
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
int modifier(Client client)
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
LEAGUE
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
int modifier(Client client)
|
||||||
|
{
|
||||||
|
if (client.getVar(Varbits.LEAGUE_RELIC_6) != 0)
|
||||||
|
{
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
if (client.getVar(Varbits.LEAGUE_RELIC_4) != 0)
|
||||||
|
{
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
if (client.getVar(Varbits.LEAGUE_RELIC_2) != 0)
|
||||||
|
{
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int modifier(Client client)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static XpWorldType of(WorldType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TOURNAMENT:
|
||||||
|
return TOURNEY;
|
||||||
|
case DEADMAN:
|
||||||
|
return DMM;
|
||||||
|
case LEAGUE:
|
||||||
|
return LEAGUE;
|
||||||
|
default:
|
||||||
|
return NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -186,7 +186,7 @@ public abstract class MenuMixin implements RSClient
|
|||||||
final int i = getMenuOptionCount() - 1;
|
final int i = getMenuOptionCount() - 1;
|
||||||
getMenuOptions()[i] = entry.getOption();
|
getMenuOptions()[i] = entry.getOption();
|
||||||
getMenuTargets()[i] = entry.getTarget();
|
getMenuTargets()[i] = entry.getTarget();
|
||||||
getMenuIdentifiers()[i] = entry.getType();
|
getMenuIdentifiers()[i] = entry.getIdentifier();
|
||||||
getMenuOpcodes()[i] = entry.getOpcode();
|
getMenuOpcodes()[i] = entry.getOpcode();
|
||||||
getMenuArguments1()[i] = entry.getActionParam0();
|
getMenuArguments1()[i] = entry.getActionParam0();
|
||||||
getMenuArguments2()[i] = entry.getActionParam1();
|
getMenuArguments2()[i] = entry.getActionParam1();
|
||||||
@@ -214,7 +214,7 @@ public abstract class MenuMixin implements RSClient
|
|||||||
|
|
||||||
tempMenuAction.setOption(entry.getOption());
|
tempMenuAction.setOption(entry.getOption());
|
||||||
tempMenuAction.setOpcode(entry.getOpcode());
|
tempMenuAction.setOpcode(entry.getOpcode());
|
||||||
tempMenuAction.setIdentifier(entry.getType());
|
tempMenuAction.setIdentifier(entry.getIdentifier());
|
||||||
tempMenuAction.setParam0(entry.getActionParam0());
|
tempMenuAction.setParam0(entry.getActionParam0());
|
||||||
tempMenuAction.setParam1(entry.getActionParam1());
|
tempMenuAction.setParam1(entry.getActionParam1());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -752,7 +752,7 @@ public abstract class RSClientMixin implements RSClient
|
|||||||
MenuEntry entry = entries[i] = new MenuEntry();
|
MenuEntry entry = entries[i] = new MenuEntry();
|
||||||
entry.setOption(menuOptions[i]);
|
entry.setOption(menuOptions[i]);
|
||||||
entry.setTarget(menuTargets[i]);
|
entry.setTarget(menuTargets[i]);
|
||||||
entry.setType(menuIdentifiers[i]);
|
entry.setIdentifier(menuIdentifiers[i]);
|
||||||
entry.setOpcode(menuTypes[i]);
|
entry.setOpcode(menuTypes[i]);
|
||||||
entry.setActionParam0(params0[i]);
|
entry.setActionParam0(params0[i]);
|
||||||
entry.setActionParam1(params1[i]);
|
entry.setActionParam1(params1[i]);
|
||||||
@@ -783,7 +783,7 @@ public abstract class RSClientMixin implements RSClient
|
|||||||
|
|
||||||
menuOptions[count] = entry.getOption();
|
menuOptions[count] = entry.getOption();
|
||||||
menuTargets[count] = entry.getTarget();
|
menuTargets[count] = entry.getTarget();
|
||||||
menuIdentifiers[count] = entry.getType();
|
menuIdentifiers[count] = entry.getIdentifier();
|
||||||
menuTypes[count] = entry.getOpcode();
|
menuTypes[count] = entry.getOpcode();
|
||||||
params0[count] = entry.getActionParam0();
|
params0[count] = entry.getActionParam0();
|
||||||
params1[count] = entry.getActionParam1();
|
params1[count] = entry.getActionParam1();
|
||||||
@@ -830,7 +830,7 @@ public abstract class RSClientMixin implements RSClient
|
|||||||
{
|
{
|
||||||
options[oldCount] = event.getOption();
|
options[oldCount] = event.getOption();
|
||||||
targets[oldCount] = event.getTarget();
|
targets[oldCount] = event.getTarget();
|
||||||
identifiers[oldCount] = event.getType();
|
identifiers[oldCount] = event.getIdentifier();
|
||||||
opcodes[oldCount] = event.getOpcode();
|
opcodes[oldCount] = event.getOpcode();
|
||||||
arguments1[oldCount] = event.getActionParam0();
|
arguments1[oldCount] = event.getActionParam0();
|
||||||
arguments2[oldCount] = event.getActionParam1();
|
arguments2[oldCount] = event.getActionParam1();
|
||||||
@@ -1425,14 +1425,14 @@ public abstract class RSClientMixin implements RSClient
|
|||||||
{
|
{
|
||||||
client.getLogger().info(
|
client.getLogger().info(
|
||||||
"|MenuAction|: MenuOption={} MenuTarget={} Id={} Opcode={} Param0={} Param1={} CanvasX={} CanvasY={} Authentic={}",
|
"|MenuAction|: MenuOption={} MenuTarget={} Id={} Opcode={} Param0={} Param1={} CanvasX={} CanvasY={} Authentic={}",
|
||||||
menuOptionClicked.getOption(), menuOptionClicked.getTarget(), menuOptionClicked.getType(),
|
menuOptionClicked.getOption(), menuOptionClicked.getTarget(), menuOptionClicked.getIdentifier(),
|
||||||
menuOptionClicked.getOpcode(), menuOptionClicked.getActionParam0(), menuOptionClicked.getActionParam1(),
|
menuOptionClicked.getOpcode(), menuOptionClicked.getActionParam0(), menuOptionClicked.getActionParam1(),
|
||||||
canvasX, canvasY, authentic
|
canvasX, canvasY, authentic
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
copy$menuAction(menuOptionClicked.getActionParam0(), menuOptionClicked.getActionParam1(), menuOptionClicked.getOpcode(),
|
copy$menuAction(menuOptionClicked.getActionParam0(), menuOptionClicked.getActionParam1(), menuOptionClicked.getOpcode(),
|
||||||
menuOptionClicked.getType(), menuOptionClicked.getOption(), menuOptionClicked.getTarget(), canvasX, canvasY);
|
menuOptionClicked.getIdentifier(), menuOptionClicked.getOption(), menuOptionClicked.getTarget(), canvasX, canvasY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user