diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect1Definition.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/AudioEnvelopeLoader.java similarity index 57% rename from cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect1Definition.java rename to cache/src/main/java/net/runelite/cache/definitions/loaders/sound/AudioEnvelopeLoader.java index ecd9854892..084a8a9a86 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect1Definition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/AudioEnvelopeLoader.java @@ -22,34 +22,41 @@ * (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.cache.definitions.sound; +package net.runelite.cache.definitions.loaders.sound; -public class SoundEffect1Definition +import net.runelite.cache.definitions.sound.AudioEnvelopeDefinition; +import net.runelite.cache.io.InputStream; + +public class AudioEnvelopeLoader { - public SoundEffect2Definition field1173; - public SoundEffect2Definition field1174; - public SoundEffect2Definition field1175; - public int field1176 = 500; - public int[] field1177 = new int[] + public AudioEnvelopeDefinition load(InputStream in) { - 0, 0, 0, 0, 0 - }; - public SoundEffect2Definition field1178; - public int[] field1179 = new int[] + AudioEnvelopeDefinition audioEnvelope = new AudioEnvelopeDefinition(); + + load(audioEnvelope, in); + + return audioEnvelope; + } + + private void load(AudioEnvelopeDefinition audioEnvelope, InputStream in) { - 0, 0, 0, 0, 0 - }; - public int[] field1180 = new int[] + audioEnvelope.form = in.readUnsignedByte(); + audioEnvelope.start = in.readInt(); + audioEnvelope.end = in.readInt(); + this.loadSegments(audioEnvelope, in); + } + + final void loadSegments(AudioEnvelopeDefinition audioEnvelope, InputStream in) { - 0, 0, 0, 0, 0 - }; - public SoundEffect2Definition field1181; - public SoundEffect3Definition field1182; - public SoundEffect2Definition field1183; - public int field1184 = 100; - public SoundEffect2Definition field1186; - public int field1187 = 0; - public int field1188 = 0; - public SoundEffect2Definition field1192; - public SoundEffect2Definition field1193; -} + audioEnvelope.segments = in.readUnsignedByte(); + audioEnvelope.durations = new int[audioEnvelope.segments]; + audioEnvelope.phases = new int[audioEnvelope.segments]; + + for (int i = 0; i < audioEnvelope.segments; ++i) + { + audioEnvelope.durations[i] = in.readUnsignedShort(); + audioEnvelope.phases[i] = in.readUnsignedShort(); + } + + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java new file mode 100644 index 0000000000..f3d2326760 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, Adam + * 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.cache.definitions.loaders.sound; + +import net.runelite.cache.definitions.sound.InstrumentDefinition; +import net.runelite.cache.definitions.sound.AudioEnvelopeDefinition; +import net.runelite.cache.io.InputStream; + +public class InstrumentLoader +{ + private final AudioEnvelopeLoader aeLoader = new AudioEnvelopeLoader(); + private final SoundEffectLoader seLoader = new SoundEffectLoader(); + + public InstrumentDefinition load(InputStream in) + { + InstrumentDefinition instrument = new InstrumentDefinition(); + + load(instrument, in); + + return instrument; + } + + private void load(InstrumentDefinition instrument, InputStream in) + { + instrument.pitch = aeLoader.load(in); + instrument.volume = aeLoader.load(in); + int volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + instrument.pitchModifier = aeLoader.load(in); + instrument.pitchModifierAmplitude = aeLoader.load(in); + } + + volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + instrument.volumeMultiplier = aeLoader.load(in); + instrument.volumeMultiplierAmplitude = aeLoader.load(in); + } + + volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + instrument.release = aeLoader.load(in); + instrument.field1175 = aeLoader.load(in); + } + + for (int i = 0; i < 10; ++i) + { + int vol = in.readUnsignedShortSmart(); + if (vol == 0) + { + break; + } + + instrument.oscillatorVolume[i] = vol; + instrument.oscillatorPitch[i] = in.readShortSmart(); + instrument.oscillatorDelays[i] = in.readUnsignedShortSmart(); + } + + instrument.delayTime = in.readUnsignedShortSmart(); + instrument.delayDecay = in.readUnsignedShortSmart(); + instrument.duration = in.readUnsignedShort(); + instrument.offset = in.readUnsignedShort(); + instrument.filterEnvelope = new AudioEnvelopeDefinition(); + instrument.filter = seLoader.load(in, instrument.filterEnvelope); + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java deleted file mode 100644 index a5c8f16f78..0000000000 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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.cache.definitions.loaders.sound; - -import net.runelite.cache.definitions.sound.SoundEffect1Definition; -import net.runelite.cache.definitions.sound.SoundEffect2Definition; -import net.runelite.cache.io.InputStream; - -public class SoundEffect1Loader -{ - private final SoundEffect2Loader se2Loader = new SoundEffect2Loader(); - private final SoundEffect3Loader se3Loader = new SoundEffect3Loader(); - - public SoundEffect1Definition load(InputStream in) - { - SoundEffect1Definition se = new SoundEffect1Definition(); - - load(se, in); - - return se; - } - - private void load(SoundEffect1Definition se, InputStream var1) - { - se.field1181 = se2Loader.load(var1); - se.field1173 = se2Loader.load(var1); - int var2 = var1.readUnsignedByte(); - if (var2 != 0) - { - var1.setOffset(var1.getOffset() - 1); - se.field1174 = se2Loader.load(var1); - se.field1193 = se2Loader.load(var1); - } - - var2 = var1.readUnsignedByte(); - if (var2 != 0) - { - var1.setOffset(var1.getOffset() - 1); - se.field1183 = se2Loader.load(var1); - se.field1192 = se2Loader.load(var1); - } - - var2 = var1.readUnsignedByte(); - if (var2 != 0) - { - var1.setOffset(var1.getOffset() - 1); - se.field1178 = se2Loader.load(var1); - se.field1175 = se2Loader.load(var1); - } - - for (int var3 = 0; var3 < 10; ++var3) - { - int var4 = var1.readUnsignedShortSmart(); - if (var4 == 0) - { - break; - } - - se.field1180[var3] = var4; - se.field1179[var3] = var1.readShortSmart(); - se.field1177[var3] = var1.readUnsignedShortSmart(); - } - - se.field1187 = var1.readUnsignedShortSmart(); - se.field1184 = var1.readUnsignedShortSmart(); - se.field1176 = var1.readUnsignedShort(); - se.field1188 = var1.readUnsignedShort(); - se.field1186 = new SoundEffect2Definition(); - se.field1182 = se3Loader.load(var1, se.field1186); - } -} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java deleted file mode 100644 index ad7f733efb..0000000000 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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.cache.definitions.loaders.sound; - -import net.runelite.cache.definitions.sound.SoundEffect2Definition; -import net.runelite.cache.definitions.sound.SoundEffect3Definition; -import net.runelite.cache.io.InputStream; - -public class SoundEffect3Loader -{ - private final SoundEffect2Loader se2Loader = new SoundEffect2Loader(); - - public SoundEffect3Definition load(InputStream in, SoundEffect2Definition var2) - { - SoundEffect3Definition se = new SoundEffect3Definition(); - - load(se, in, var2); - - return se; - } - - private void load(SoundEffect3Definition se, InputStream var1, SoundEffect2Definition var2) - { - int var3 = var1.readUnsignedByte(); - se.field1155[0] = var3 >> 4; - se.field1155[1] = var3 & 15; - if (var3 != 0) - { - se.field1156[0] = var1.readUnsignedShort(); - se.field1156[1] = var1.readUnsignedShort(); - int var4 = var1.readUnsignedByte(); - - int var5; - int var6; - for (var5 = 0; var5 < 2; ++var5) - { - for (var6 = 0; var6 < se.field1155[var5]; ++var6) - { - se.field1154[var5][0][var6] = var1.readUnsignedShort(); - se.field1159[var5][0][var6] = var1.readUnsignedShort(); - } - } - - for (var5 = 0; var5 < 2; ++var5) - { - for (var6 = 0; var6 < se.field1155[var5]; ++var6) - { - if ((var4 & 1 << var5 * 4 << var6) != 0) - { - se.field1154[var5][1][var6] = var1.readUnsignedShort(); - se.field1159[var5][1][var6] = var1.readUnsignedShort(); - } - else - { - se.field1154[var5][1][var6] = se.field1154[var5][0][var6]; - se.field1159[var5][1][var6] = se.field1159[var5][0][var6]; - } - } - } - - if (var4 != 0 || se.field1156[1] != se.field1156[0]) - { - se2Loader.method1144(var2, var1); - } - } - else - { - int[] var7 = se.field1156; - se.field1156[1] = 0; - var7[0] = 0; - } - } -} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java index ddb6a4d9fc..2ee518d4ee 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java @@ -24,39 +24,70 @@ */ package net.runelite.cache.definitions.loaders.sound; +import net.runelite.cache.definitions.sound.AudioEnvelopeDefinition; import net.runelite.cache.definitions.sound.SoundEffectDefinition; -import net.runelite.cache.definitions.sound.SoundEffect1Definition; import net.runelite.cache.io.InputStream; public class SoundEffectLoader { - public SoundEffectDefinition load(byte[] b) + private final AudioEnvelopeLoader audioEnvelopeLoader = new AudioEnvelopeLoader(); + + public SoundEffectDefinition load(InputStream in, AudioEnvelopeDefinition audioEnvelope) { - SoundEffectDefinition se = new SoundEffectDefinition(); - InputStream in = new InputStream(b); + SoundEffectDefinition soundEffect = new SoundEffectDefinition(); - load(se, in); + load(soundEffect, audioEnvelope, in); - return se; + return soundEffect; } - private void load(SoundEffectDefinition se, InputStream var1) + private void load(SoundEffectDefinition soundEffect, AudioEnvelopeDefinition audioEnvelope, InputStream in) { - for (int var2 = 0; var2 < 10; ++var2) + int id = in.readUnsignedByte(); + soundEffect.pairs[0] = id >> 4; + soundEffect.pairs[1] = id & 15; + if (id != 0) { - int var3 = var1.readUnsignedByte(); - if (var3 != 0) + soundEffect.unity[0] = in.readUnsignedShort(); + soundEffect.unity[1] = in.readUnsignedShort(); + int track = in.readUnsignedByte(); + + for (int i = 0; i < 2; ++i) { - var1.setOffset(var1.getOffset() - 1); + for (int j = 0; j < soundEffect.pairs[i]; ++j) + { + soundEffect.phases[i][0][j] = in.readUnsignedShort(); + soundEffect.magnitudes[i][0][j] = in.readUnsignedShort(); + } + } - SoundEffect1Loader se1Loader = new SoundEffect1Loader(); - SoundEffect1Definition se1 = se1Loader.load(var1); + for (int i = 0; i < 2; ++i) + { + for (int j = 0; j < soundEffect.pairs[i]; ++j) + { + if ((track & 1 << i * 4 << j) != 0) + { + soundEffect.phases[i][1][j] = in.readUnsignedShort(); + soundEffect.magnitudes[i][1][j] = in.readUnsignedShort(); + } + else + { + soundEffect.phases[i][1][j] = soundEffect.phases[i][0][j]; + soundEffect.magnitudes[i][1][j] = soundEffect.magnitudes[i][0][j]; + } + } + } - se.field1008[var2] = se1; + if (track != 0 || soundEffect.unity[1] != soundEffect.unity[0]) + { + audioEnvelopeLoader.loadSegments(audioEnvelope, in); } } - - se.field1006 = var1.readUnsignedShort(); - se.field1009 = var1.readUnsignedShort(); + else + { + int[] clean = soundEffect.unity; + soundEffect.unity[1] = 0; + clean[0] = 0; + } } -} +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect2Loader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectTrackLoader.java similarity index 63% rename from cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect2Loader.java rename to cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectTrackLoader.java index d9ae3d3abb..3b991fdc70 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect2Loader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectTrackLoader.java @@ -24,39 +24,39 @@ */ package net.runelite.cache.definitions.loaders.sound; -import net.runelite.cache.definitions.sound.SoundEffect2Definition; +import net.runelite.cache.definitions.sound.SoundEffectTrackDefinition; +import net.runelite.cache.definitions.sound.InstrumentDefinition; import net.runelite.cache.io.InputStream; -public class SoundEffect2Loader +public class SoundEffectTrackLoader { - public SoundEffect2Definition load(InputStream in) + public SoundEffectTrackDefinition load(byte[] b) { - SoundEffect2Definition se = new SoundEffect2Definition(); + SoundEffectTrackDefinition soundEffect = new SoundEffectTrackDefinition(); + InputStream in = new InputStream(b); - load(se, in); + load(soundEffect, in); - return se; + return soundEffect; } - private void load(SoundEffect2Definition se, InputStream var1) + private void load(SoundEffectTrackDefinition soundEffect, InputStream in) { - se.field1087 = var1.readUnsignedByte(); - se.field1088 = var1.readInt(); - se.field1089 = var1.readInt(); - this.method1144(se, var1); - } - - final void method1144(SoundEffect2Definition se, InputStream var1) - { - se.field1092 = var1.readUnsignedByte(); - se.field1086 = new int[se.field1092]; - se.field1090 = new int[se.field1092]; - - for (int var2 = 0; var2 < se.field1092; ++var2) + for (int i = 0; i < 10; ++i) { - se.field1086[var2] = var1.readUnsignedShort(); - se.field1090[var2] = var1.readUnsignedShort(); + int volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + + InstrumentLoader instrumentLoader = new InstrumentLoader(); + InstrumentDefinition instrument = instrumentLoader.load(in); + + soundEffect.instruments[i] = instrument; + } } + soundEffect.start = in.readUnsignedShort(); + soundEffect.end = in.readUnsignedShort(); } -} +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/AudioEnvelopeDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/AudioEnvelopeDefinition.java new file mode 100644 index 0000000000..46d709f6a4 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/AudioEnvelopeDefinition.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017, Adam + * 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.cache.definitions.sound; + +public class AudioEnvelopeDefinition +{ + public int segments = 2; + public int[] durations = new int[2]; + public int[] phases = new int[2]; + public int start; + public int end; + public int form; + public int ticks; + public int phaseIndex; + public int step; + public int amplitude; + public int max; + + public AudioEnvelopeDefinition() + { + this.durations[0] = 0; + this.durations[1] = '\uffff'; + this.phases[0] = 0; + this.phases[1] = '\uffff'; + } + + public final int step(int var1) + { + if (this.max >= this.ticks) + { + this.amplitude = this.phases[this.phaseIndex++] << 15; + + if (this.phaseIndex >= this.segments) + { + this.phaseIndex = this.segments - 1; + } + + this.ticks = (int) ((double) this.durations[this.phaseIndex] / 65536.0 * (double) var1); + + if (this.ticks > this.max) + { + this.step = ((this.phases[this.phaseIndex] << 15) - this.amplitude) / (this.ticks - this.max); + } + } + this.amplitude += this.step; + ++this.max; + + return this.amplitude - this.step >> 15; + } + + public final void reset() + { + this.ticks = 0; + this.phaseIndex = 0; + this.step = 0; + this.amplitude = 0; + this.max = 0; + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java new file mode 100644 index 0000000000..fafaa7b8f8 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2017, Adam + * 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.cache.definitions.sound; + +import java.util.Random; + +public class InstrumentDefinition +{ + public AudioEnvelopeDefinition volume; + public AudioEnvelopeDefinition pitchModifier; + public AudioEnvelopeDefinition field1175; + public AudioEnvelopeDefinition release; + public AudioEnvelopeDefinition volumeMultiplier; + public AudioEnvelopeDefinition volumeMultiplierAmplitude; + public AudioEnvelopeDefinition pitchModifierAmplitude; + public AudioEnvelopeDefinition pitch; + + public int[] oscillatorDelays = new int[] + { + 0, 0, 0, 0, 0 + }; + public int[] oscillatorPitch = new int[] + { + 0, 0, 0, 0, 0 + }; + public int[] oscillatorVolume = new int[] + { + 0, 0, 0, 0, 0 + }; + + public SoundEffectDefinition filter; + public AudioEnvelopeDefinition filterEnvelope; + + static int[] samples = new int[220500]; + + static int[] NOISE = new int[32768]; + static int[] AUDIO_SINE = new int[32768]; + + static int[] phases = new int[5]; + static int[] delays = new int[5]; + + static int[] volumeSteps = new int[5]; + static int[] pitchSteps = new int[5]; + static int[] pitchBaseSteps = new int[5]; + + public int duration = 500; + public int delayDecay = 100; + public int delayTime = 0; + public int offset = 0; + + static + { + Random random = new Random(0); + + for (int i = 0; i < 32768; ++i) + { + InstrumentDefinition.NOISE[i] = (random.nextInt() & 2) - 1; + InstrumentDefinition.AUDIO_SINE[i] = (int) (Math.sin((double) i / 5215.1903) * 16384.0); + } + } + + public final int[] synthesize(int var1, int var2) + { + int var16; + int var15; + int var14; + int var11; + int var12; + int var13; + + InstrumentDefinition.method3854(samples, 0, var1); + + if (var2 < 10) + { + return samples; + } + + double var3 = (double) var1 / ((double) var2 + 0.0); + + this.pitch.reset(); + this.volume.reset(); + + int var5 = 0; + int var6 = 0; + int var7 = 0; + + if (this.pitchModifier != null) + { + this.pitchModifier.reset(); + this.pitchModifierAmplitude.reset(); + + var5 = (int) ((double) (this.pitchModifier.end - this.pitchModifier.start) * 32.768 / var3); + var6 = (int) ((double) this.pitchModifier.start * 32.768 / var3); + } + + int var8 = 0; + int var9 = 0; + int var10 = 0; + + if (this.volumeMultiplier != null) + { + this.volumeMultiplier.reset(); + this.volumeMultiplierAmplitude.reset(); + + var8 = (int) ((double) (this.volumeMultiplier.end - this.volumeMultiplier.start) * 32.768 / var3); + var9 = (int) ((double) this.volumeMultiplier.start * 32.768 / var3); + } + + for (var11 = 0; var11 < 5; ++var11) + { + if (this.oscillatorVolume[var11] == 0) + { + continue; + } + + InstrumentDefinition.phases[var11] = 0; + InstrumentDefinition.delays[var11] = (int) ((double) this.oscillatorDelays[var11] * var3); + InstrumentDefinition.volumeSteps[var11] = (this.oscillatorVolume[var11] << 14) / 100; + InstrumentDefinition.pitchSteps[var11] = (int) ((double) (this.pitch.end - this.pitch.start) * 32.768 * Math.pow(1.0057929410678534, this.oscillatorPitch[var11]) / var3); + InstrumentDefinition.pitchBaseSteps[var11] = (int) ((double) this.pitch.start * 32.768 / var3); + } + + for (var11 = 0; var11 < var1; ++var11) + { + var12 = this.pitch.step(var1); + var13 = this.volume.step(var1); + + if (this.pitchModifier != null) + { + var14 = this.pitchModifier.step(var1); + var15 = this.pitchModifierAmplitude.step(var1); + var12 += this.evaluateWave(var7, var15, this.pitchModifier.form) >> 1; + var7 = var7 + var6 + (var14 * var5 >> 16); + } + + if (this.volumeMultiplier != null) + { + var14 = this.volumeMultiplier.step(var1); + var15 = this.volumeMultiplierAmplitude.step(var1); + var13 = var13 * ((this.evaluateWave(var10, var15, this.volumeMultiplier.form) >> 1) + 32768) >> 15; + var10 = var10 + var9 + (var14 * var8 >> 16); + } + + for (var14 = 0; var14 < 5; ++var14) + { + if (this.oscillatorVolume[var14] == 0 || (var15 = delays[var14] + var11) >= var1) + { + continue; + } + + int[] arrn = samples; + int n = var15; + arrn[n] = arrn[n] + this.evaluateWave(phases[var14], var13 * volumeSteps[var14] >> 15, this.pitch.form); + int[] arrn2 = phases; + int n2 = var14; + arrn2[n2] = arrn2[n2] + ((var12 * pitchSteps[var14] >> 16) + pitchBaseSteps[var14]); + } + } + + if (this.release != null) + { + this.release.reset(); + this.field1175.reset(); + + var11 = 0; + boolean var20 = true; + + for (var14 = 0; var14 < var1; ++var14) + { + var15 = this.release.step(var1); + var16 = this.field1175.step(var1); + var12 = var20 ? (var15 * (this.release.end - this.release.start) >> 8) + this.release.start : (var16 * (this.release.end - this.release.start) >> 8) + this.release.start; + if ((var11 += 256) >= var12) + { + var11 = 0; + } + if (!var20) + { + continue; + } + InstrumentDefinition.samples[var14] = 0; + } + } + if (this.delayTime > 0 && this.delayDecay > 0) + { + for (var12 = var11 = (int) ((double) this.delayTime * var3); var12 < var1; ++var12) + { + int[] arrn = samples; + int n = var12; + + arrn[n] = arrn[n] + samples[var12 - var11] * this.delayDecay / 100; + } + } + if (this.filter.pairs[0] > 0 || this.filter.pairs[1] > 0) + { + this.filterEnvelope.reset(); + + var11 = this.filterEnvelope.step(var1 + 1); + var12 = this.filter.compute(0, (float) var11 / 65536.0f); + var13 = this.filter.compute(1, (float) var11 / 65536.0f); + + if (var1 >= var12 + var13) + { + int var17; + + var14 = 0; + var15 = var13; + + if (var13 > var1 - var12) + { + var15 = var1 - var12; + } + + while (var14 < var15) + { + var16 = (int) ((long) samples[var14 + var12] * (long) SoundEffectDefinition.fowardMultiplier >> 16); + + for (var17 = 0; var17 < var12; ++var17) + { + var16 += (int) ((long) samples[var14 + var12 - 1 - var17] * (long) SoundEffectDefinition.coefficients[0][var17] >> 16); + } + + for (var17 = 0; var17 < var14; ++var17) + { + var16 -= (int) ((long) samples[var14 - 1 - var17] * (long) SoundEffectDefinition.coefficients[1][var17] >> 16); + } + + InstrumentDefinition.samples[var14] = var16; + var11 = this.filterEnvelope.step(var1 + 1); + ++var14; + } + var15 = 128; + + do + { + int var18; + + if (var15 > var1 - var12) + { + var15 = var1 - var12; + } + + while (var14 < var15) + { + var17 = (int) ((long) samples[var14 + var12] * (long) SoundEffectDefinition.fowardMultiplier >> 16); + + for (var18 = 0; var18 < var12; ++var18) + { + var17 += (int) ((long) samples[var14 + var12 - 1 - var18] * (long) SoundEffectDefinition.coefficients[0][var18] >> 16); + } + + for (var18 = 0; var18 < var13; ++var18) + { + var17 -= (int) ((long) samples[var14 - 1 - var18] * (long) SoundEffectDefinition.coefficients[1][var18] >> 16); + } + + InstrumentDefinition.samples[var14] = var17; + var11 = this.filterEnvelope.step(var1 + 1); + ++var14; + } + if (var14 >= var1 - var12) + { + while (var14 < var1) + { + var17 = 0; + for (var18 = var14 + var12 - var1; var18 < var12; ++var18) + { + var17 += (int) ((long) samples[var14 + var12 - 1 - var18] + * (long) SoundEffectDefinition.coefficients[0][var18] >> 16); + } + for (var18 = 0; var18 < var13; ++var18) + { + var17 -= (int) ((long) samples[var14 - 1 - var18] + * (long) SoundEffectDefinition.coefficients[1][var18] >> 16); + } + InstrumentDefinition.samples[var14] = var17; + this.filterEnvelope.step(var1 + 1); + ++var14; + } + break; + } + var12 = this.filter.compute(0, (float) var11 / 65536.0f); + var13 = this.filter.compute(1, (float) var11 / 65536.0f); + var15 += 128; + } + while (true); + } + } + for (var11 = 0; var11 < var1; ++var11) + { + if (samples[var11] < -32768) + { + InstrumentDefinition.samples[var11] = -32768; + } + + if (samples[var11] <= 32767) + { + continue; + } + + InstrumentDefinition.samples[var11] = 32767; + } + + return samples; + } + + private static void method3854(int[] var1, int var2, int var3) + { + var3 = var3 + var2 - 7; + + while (var2 < var3) + { + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + } + + while (var2 < (var3 += 7)) + { + var1[var2++] = 0; + } + } + + public final int evaluateWave(int var1, int var2, int var3) + { + return var3 == 1 ? ((var1 & 32767) < 16384 ? var2 : -var2) : (var3 == 2 ? AUDIO_SINE[var1 & 32767] * var2 >> 14 : (var3 == 3 ? (var2 * (var1 & 32767) >> 14) - var2 : (var3 == 4 ? var2 * NOISE[var1 / 2607 & 32767] : 0))); + } +} + diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java index 0eda10254e..7f6d71dc17 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java @@ -26,7 +26,102 @@ package net.runelite.cache.definitions.sound; public class SoundEffectDefinition { - public int field1006; - public SoundEffect1Definition[] field1008 = new SoundEffect1Definition[10]; - public int field1009; -} + public int[][][] phases = new int[2][2][4]; + public int[] pairs = new int[2]; + public int[] unity = new int[2]; + public int[][][] magnitudes = new int[2][2][4]; + + public static float[][] minCoefficients = new float[2][8]; + public static int[][] coefficients = new int[2][8]; + public static float fowardMinCoefficientMultiplier; + public static int fowardMultiplier; + + public int compute(int var1, float var2) + { + float var3; + int var4; + + if (var1 == 0) + { + var3 = (float) this.unity[0] + (float) (this.unity[1] - this.unity[0]) * var2; + + fowardMinCoefficientMultiplier = (float) Math.pow(0.1, (var3 *= 0.0030517578f) / 20.0f); + fowardMultiplier = (int) (fowardMinCoefficientMultiplier * 65536.0f); + } + + if (this.pairs[var1] == 0) + { + return 0; + } + + var3 = this.interpolateMagniture(var1, 0, var2); + + minCoefficients[var1][0] = -2.0f * var3 * (float) Math.cos(this.interpolatePhase(var1, 0, var2)); + minCoefficients[var1][1] = var3 * var3; + + for (var4 = 1; var4 < this.pairs[var1]; ++var4) + { + var3 = this.interpolateMagniture(var1, var4, var2); + + float var5 = -2.0f * var3 * (float) Math.cos(this.interpolatePhase(var1, var4, var2)); + float var6 = var3 * var3; + + minCoefficients[var1][var4 * 2 + 1] = minCoefficients[var1][var4 * 2 - 1] * var6; + minCoefficients[var1][var4 * 2] = minCoefficients[var1][var4 * 2 - 1] * var5 + minCoefficients[var1][var4 * 2 - 2] * var6; + + for (int var7 = var4 * 2 - 1; var7 >= 2; --var7) + { + float[] arrf = minCoefficients[var1]; + int n = var7; + + arrf[n] = arrf[n] + (minCoefficients[var1][var7 - 1] * var5 + minCoefficients[var1][var7 - 2] * var6); + } + + float[] arrf = minCoefficients[var1]; + arrf[1] = arrf[1] + (minCoefficients[var1][0] * var5 + var6); + + float[] arrf2 = minCoefficients[var1]; + arrf2[0] = arrf2[0] + var5; + } + + if (var1 == 0) + { + var4 = 0; + while (var4 < this.pairs[0] * 2) + { + float[] arrf = minCoefficients[0]; + int n = var4++; + + arrf[n] = arrf[n] * fowardMinCoefficientMultiplier; + } + } + + for (var4 = 0; var4 < this.pairs[var1] * 2; ++var4) + { + coefficients[var1][var4] = (int) (minCoefficients[var1][var4] * 65536.0f); + } + + return this.pairs[var1] * 2; + } + + public float interpolateMagniture(int var1, int var2, float var3) + { + float var4 = (float) this.magnitudes[var1][0][var2] + var3 * (float) (this.magnitudes[var1][1][var2] - this.magnitudes[var1][0][var2]); + + return 1.0f - (float) Math.pow(10.0, (-(var4 *= 0.0015258789f)) / 20.0f); + } + + public float interpolatePhase(int var1, int var2, float var3) + { + float var4 = (float) this.phases[var1][0][var2] + var3 * (float) (this.phases[var1][1][var2] - this.phases[var1][0][var2]); + + return normalise(var4 *= 1.2207031E-4f); + } + + public static float normalise(float var1) + { + float var2 = 32.703197f * (float) Math.pow(2.0, var1); + + return var2 * 3.1415927f / 11025.0f; + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectTrackDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectTrackDefinition.java new file mode 100644 index 0000000000..6cbbbcc2cf --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectTrackDefinition.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017, Adam + * 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.cache.definitions.sound; + +public class SoundEffectTrackDefinition +{ + public int start; + public InstrumentDefinition[] instruments = new InstrumentDefinition[10]; + public int end; + + public final byte[] mix() + { + int var2; + int var1 = 0; + + for (var2 = 0; var2 < 10; ++var2) + { + if (this.instruments[var2] == null || this.instruments[var2].duration + this.instruments[var2].offset <= var1) + { + continue; + } + + var1 = this.instruments[var2].duration + this.instruments[var2].offset; + } + + if (var1 == 0) + { + return new byte[0]; + } + + var2 = var1 * 22050 / 1000; + + byte[] var3 = new byte[var2]; + + for (int i = 0; i < 10; ++i) + { + if (this.instruments[i] == null) + { + continue; + } + + int var5 = this.instruments[i].duration * 22050 / 1000; + int var6 = this.instruments[i].offset * 22050 / 1000; + + int[] var7 = this.instruments[i].synthesize(var5, this.instruments[i].duration); + + for (int j = 0; j < var5; ++j) + { + int var9 = (var7[j] >> 8) + var3[j + var6]; + + if ((var9 + 128 & -256) != 0) + { + var9 = var9 >> 31 ^ 127; + } + + var3[j + var6] = (byte) var9; + } + } + + return var3; + } +} \ No newline at end of file diff --git a/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java b/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java index 5ac0c9e1c5..ec332656b2 100644 --- a/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java +++ b/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java @@ -27,11 +27,16 @@ package net.runelite.cache; import com.google.common.io.Files; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.io.File; + + import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; import java.nio.charset.Charset; -import net.runelite.cache.definitions.loaders.sound.SoundEffectLoader; -import net.runelite.cache.definitions.sound.SoundEffectDefinition; +import net.runelite.cache.definitions.loaders.sound.SoundEffectTrackLoader; +import net.runelite.cache.definitions.sound.SoundEffectTrackDefinition; import net.runelite.cache.fs.Archive; import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Storage; @@ -42,6 +47,11 @@ import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; + public class SoundEffectsDumperTest { private static final Logger logger = LoggerFactory.getLogger(SoundEffectsDumperTest.class); @@ -68,14 +78,68 @@ public class SoundEffectsDumperTest { byte[] contents = archive.decompress(storage.loadArchive(archive)); - SoundEffectLoader soundEffectLoader = new SoundEffectLoader(); - SoundEffectDefinition soundEffect = soundEffectLoader.load(contents); + SoundEffectTrackLoader setLoader = new SoundEffectTrackLoader(); + SoundEffectTrackDefinition soundEffect = setLoader.load(contents); - Files.asCharSink(new File(dumpDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()).write(gson.toJson(soundEffect)); + Files.write(gson.toJson(soundEffect), new File(dumpDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()); ++count; } } logger.info("Dumped {} sound effects to {}", count, dumpDir); } -} + + @Test + public void extractWavTest() throws IOException + { + File dumpDir = folder.newFolder(); + int count = 0; + + try (Store store = new Store(StoreLocation.LOCATION)) + { + store.load(); + + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.SOUNDEFFECTS); + + for (Archive archive : index.getArchives()) + { + byte[] contents = archive.decompress(storage.loadArchive(archive)); + + SoundEffectTrackLoader setLoader = new SoundEffectTrackLoader(); + SoundEffectTrackDefinition soundEffect = setLoader.load(contents); + try + { + Object audioStream; + byte[] data = soundEffect.mix(); + + AudioFormat audioFormat = new AudioFormat(22050.0f, 8, 1, true, false); + audioStream = new AudioInputStream(new ByteArrayInputStream(data), audioFormat, data.length); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + AudioSystem.write((AudioInputStream) audioStream, AudioFileFormat.Type.WAVE, bos); + data = bos.toByteArray(); + + FileOutputStream fos = new FileOutputStream(new File(dumpDir, archive.getArchiveId() + ".wav")); + + try + { + fos.write(data); + } + finally + { + fos.close(); + } + + ++count; + } + catch (Exception e) + { + continue; + } + } + } + + logger.info("Dumped {} sound effects to {}", count, dumpDir); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index fa02dcee2d..9d1649c992 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.10.19 1.6.0 3.0.6 - 4.0.0 + 3.14.0 0.7 3.1.2 diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index 07a2f68ce6..578ce50408 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -197,6 +197,7 @@ public final class AnimationID public static final int FARMING_PLANT_SEED = 2291; public static final int FARMING_HARVEST_FLOWER = 2292; public static final int FARMING_MIX_ULTRACOMPOST = 7699; + public static final int FARMING_HARVEST_ALLOTMENT = 830; // Lunar spellbook public static final int ENERGY_TRANSFER_VENGEANCE_OTHER = 4411; @@ -315,7 +316,7 @@ public final class AnimationID public static final int DAG_SUPREME = 2855; // Lizardman shaman - public static final int LIZARDMAN_SHAMAN_SPAWN = 2855; + public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; // Combat counter public static final int BARRAGE_ANIMATION = 1979; diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index ab4ee7f15b..3eaadba176 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1353,6 +1353,20 @@ public interface Client extends GameShell */ void setInterpolateObjectAnimations(boolean interpolate); + /** + * Checks whether animation smoothing is enabled for widgets. + * + * @return true if widget animation smoothing is enabled, false otherwise + */ + boolean isInterpolateWidgetAnimations(); + + /** + * Sets the animation smoothing state for widgets. + * + * @param interpolate the new smoothing state + */ + void setInterpolateWidgetAnimations(boolean interpolate); + /** * Checks whether the logged in player is in an instanced region. * diff --git a/runelite-api/src/main/java/net/runelite/api/InventoryID.java b/runelite-api/src/main/java/net/runelite/api/InventoryID.java index df3f995df9..ec170bbc2e 100644 --- a/runelite-api/src/main/java/net/runelite/api/InventoryID.java +++ b/runelite-api/src/main/java/net/runelite/api/InventoryID.java @@ -82,4 +82,16 @@ public enum InventoryID { return id; } + + public static InventoryID getValue(int value) + { + for (InventoryID e: InventoryID.values()) + { + if (e.id == value) + { + return e; + } + } + return null; + } } \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java index ec0ff6627b..1b0a80fa4e 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java @@ -28,7 +28,7 @@ import net.runelite.api.ItemContainer; import lombok.Value; /** - * An event called whenever the stack size of an {@link api.Item} + * An event called whenever the stack size of an {@link net.runelite.api.Item} * in an {@link ItemContainer} is modified. *

* Examples of when this event may trigger include: @@ -41,6 +41,11 @@ import lombok.Value; @Value public class ItemContainerChanged { + /** + * The modified container's ID. + */ + private final int containerId; + /** * The modified item container. */ diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index caac308baf..e30b7deb73 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -290,6 +290,16 @@ 3.2.0 compile + + org.apache.httpcomponents + httpcore + 4.4.11 + + + org.apache.httpcomponents + httpmime + 4.5.9 + diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java index 4a77eff91b..f06de90882 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java @@ -385,6 +385,18 @@ public class ItemManager * @return item price */ public int getItemPrice(int itemID) + { + return getItemPrice(itemID, false); + } + + /** + * Look up an item's price + * + * @param itemID item id + * @param ignoreUntradeableMap should the price returned ignore the {@link UntradeableItemMapping} + * @return item price + */ + public int getItemPrice(int itemID, boolean ignoreUntradeableMap) { if (itemID == ItemID.COINS_995) { @@ -395,10 +407,13 @@ public class ItemManager return 1000; } - UntradeableItemMapping p = UntradeableItemMapping.map(ItemVariationMapping.map(itemID)); - if (p != null) + if (!ignoreUntradeableMap) { - return getItemPrice(p.getPriceID()) * p.getQuantity(); + UntradeableItemMapping p = UntradeableItemMapping.map(ItemVariationMapping.map(itemID)); + if (p != null) + { + return getItemPrice(p.getPriceID()) * p.getQuantity(); + } } int price = 0; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java index 870ccaaaf7..59a89d612d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java @@ -41,6 +41,7 @@ import java.nio.file.WatchService; import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; + import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLite; import net.runelite.client.config.Config; @@ -154,7 +155,6 @@ public class PluginWatcher extends Thread { continue; } - log.info("Loading plugin from {}", file); load(file); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java index 9260e9ef56..05dadb450a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java @@ -65,4 +65,14 @@ public interface AnimationSmoothingConfig extends Config return true; } + @ConfigItem( + keyName = "smoothWidgetAnimations", + name = "Smooth Widget Animations", + description = "Configures whether the widget animations are smooth or not", + position = 4 + ) + default boolean smoothWidgetAnimations() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java index 6a23856217..7c77990a26 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java @@ -69,6 +69,7 @@ public class AnimationSmoothingPlugin extends Plugin client.setInterpolatePlayerAnimations(false); client.setInterpolateNpcAnimations(false); client.setInterpolateObjectAnimations(false); + client.setInterpolateWidgetAnimations(false); } @Subscribe @@ -85,5 +86,6 @@ public class AnimationSmoothingPlugin extends Plugin client.setInterpolatePlayerAnimations(config.smoothPlayerAnimations()); client.setInterpolateNpcAnimations(config.smoothNpcAnimations()); client.setInterpolateObjectAnimations(config.smoothObjectAnimations()); + client.setInterpolateWidgetAnimations(config.smoothWidgetAnimations()); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java index f315956a1b..46e52bdf9e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java @@ -57,7 +57,8 @@ enum Boss KRAKEN(NpcID.KRAKEN, 8400, ChronoUnit.MILLIS, ItemID.PET_KRAKEN), KALPHITE_QUEEN(NpcID.KALPHITE_QUEEN_965, 30, ChronoUnit.SECONDS, ItemID.KALPHITE_PRINCESS), DUSK(NpcID.DUSK_7889, 2, ChronoUnit.MINUTES, ItemID.NOON), - ALCHEMICAL_HYDRA(NpcID.ALCHEMICAL_HYDRA_8622, 25200, ChronoUnit.MILLIS, ItemID.IKKLE_HYDRA); + ALCHEMICAL_HYDRA(NpcID.ALCHEMICAL_HYDRA_8622, 25200, ChronoUnit.MILLIS, ItemID.IKKLE_HYDRA), + SARACHNIS(NpcID.SARACHNIS, 30, ChronoUnit.SECONDS, ItemID.SRARACHA); private static final Map bosses; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java index 264e5ae425..dbdb4d6478 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java @@ -86,4 +86,15 @@ public interface ChatFilterConfig extends Config { return false; } + + @ConfigItem( + keyName = "filterLogin", + name = "Filter Logged In/Out Messages", + description = "Filter your private chat to remove logged in/out messages", + position = 6 + ) + default boolean filterLogin() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java index d9e97c441a..8a24ade869 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java @@ -133,6 +133,13 @@ public class ChatFilterPlugin extends Plugin case FRIENDSCHAT: case GAMEMESSAGE: break; + case LOGINLOGOUTNOTIFICATION: + if (config.filterLogin()) + { + // Block the message + intStack[intStackSize - 3] = 0; + } + return; default: return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 0996eed283..7df1fbd6cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -25,11 +25,14 @@ package net.runelite.client.plugins.config; import java.awt.image.BufferedImage; +import java.lang.reflect.Method; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; import javax.inject.Singleton; import javax.swing.SwingUtilities; import net.runelite.api.MenuAction; +import net.runelite.client.RuneLite; +import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; @@ -41,6 +44,7 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayMenuEntry; @@ -97,9 +101,25 @@ public class ConfigPlugin extends Plugin } @Override - protected void shutDown() throws Exception + public void shutDown() throws Exception { clientToolbar.removeNavigation(navButton); + RuneLite.getInjector().getInstance(ClientThread.class).invokeLater(() -> + { + try + { + ConfigPanel.pluginList.clear(); + pluginManager.setPluginEnabled(this, true); + pluginManager.startPlugin(this); + Method expand = ClientUI.class.getDeclaredMethod("expand", NavigationButton.class); + expand.setAccessible(true); + expand.invoke(RuneLite.getInjector().getInstance(ClientUI.class), navButton); + } + catch (Exception e) + { + System.out.println(e.getMessage()); + } + }); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index 10a72b7667..3d84a2f099 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -253,7 +253,12 @@ public class IdleNotifierPlugin extends Plugin case USING_GILDED_ALTAR: /* Farming */ case FARMING_MIX_ULTRACOMPOST: - /* Misc */ + case FARMING_HARVEST_BUSH: + case FARMING_HARVEST_HERB: + case FARMING_HARVEST_FRUIT_TREE: + case FARMING_HARVEST_FLOWER: + case FARMING_HARVEST_ALLOTMENT: + /* Misc */ case PISCARILIUS_CRANE_REPAIR: case HOME_MAKE_TABLET: case SAND_COLLECTION: diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java index 9309861a98..4e86d634ff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java @@ -69,7 +69,7 @@ class InventoryGridOverlay extends Overlay final Widget if1DraggingWidget = client.getIf1DraggedWidget(); final Widget inventoryWidget = client.getWidget(WidgetInfo.INVENTORY); - if (if1DraggingWidget == null || if1DraggingWidget.equals(inventoryWidget) + if (if1DraggingWidget == null || if1DraggingWidget != inventoryWidget || client.getItemPressedDuration() < plugin.getDragDelay() / Constants.CLIENT_TICK_LENGTH) { return null; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java index 2e9ac325cc..bbb8e1b66a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java @@ -42,7 +42,9 @@ enum AlwaysLostItem { RUNE_POUCH(ItemID.RUNE_POUCH, true), LOOTING_BAG(ItemID.LOOTING_BAG, false), - CLUE_BOX(ItemID.CLUE_BOX, false); + CLUE_BOX(ItemID.CLUE_BOX, false), + BRACELET_OF_ETHEREUM(ItemID.BRACELET_OF_ETHEREUM, false), + BRACELET_OF_ETHEREUM_UNCHARGED(ItemID.BRACELET_OF_ETHEREUM_UNCHARGED, false); private final int itemID; private final boolean keptOutsideOfWilderness; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java new file mode 100644 index 0000000000..4726555485 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import javax.annotation.Nullable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +/** + * Degradable/Non-rechargeable Jewelry death prices are usually determined by the amount of charges the item has left. + * The price of each charge is based on the GE price of the fully charged item divided by the maximum item charges + * Charge price = GE Price / Max Charges + * Death Price = Charge price * Current Charges + */ +@AllArgsConstructor +@Getter +enum DynamicPriceItem +{ + GAMES_NECKLACE1(ItemID.GAMES_NECKLACE1, 1, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE2(ItemID.GAMES_NECKLACE2, 2, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE3(ItemID.GAMES_NECKLACE3, 3, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE4(ItemID.GAMES_NECKLACE4, 4, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE5(ItemID.GAMES_NECKLACE5, 5, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE6(ItemID.GAMES_NECKLACE6, 6, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE7(ItemID.GAMES_NECKLACE7, 7, 8, ItemID.GAMES_NECKLACE8), + + RING_OF_DUELING1(ItemID.RING_OF_DUELING1, 1, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING2(ItemID.RING_OF_DUELING2, 2, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING3(ItemID.RING_OF_DUELING3, 3, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING4(ItemID.RING_OF_DUELING4, 4, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING5(ItemID.RING_OF_DUELING5, 5, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING6(ItemID.RING_OF_DUELING6, 6, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING7(ItemID.RING_OF_DUELING7, 7, 8, ItemID.RING_OF_DUELING8), + + RING_OF_RETURNING1(ItemID.RING_OF_RETURNING1, 1, 5, ItemID.RING_OF_RETURNING5), + RING_OF_RETURNING2(ItemID.RING_OF_RETURNING2, 2, 5, ItemID.RING_OF_RETURNING5), + RING_OF_RETURNING3(ItemID.RING_OF_RETURNING3, 3, 5, ItemID.RING_OF_RETURNING5), + RING_OF_RETURNING4(ItemID.RING_OF_RETURNING4, 4, 5, ItemID.RING_OF_RETURNING5), + + NECKLACE_OF_PASSAGE1(ItemID.NECKLACE_OF_PASSAGE1, 1, 5, ItemID.NECKLACE_OF_PASSAGE5), + NECKLACE_OF_PASSAGE2(ItemID.NECKLACE_OF_PASSAGE2, 2, 5, ItemID.NECKLACE_OF_PASSAGE5), + NECKLACE_OF_PASSAGE3(ItemID.NECKLACE_OF_PASSAGE3, 3, 5, ItemID.NECKLACE_OF_PASSAGE5), + NECKLACE_OF_PASSAGE4(ItemID.NECKLACE_OF_PASSAGE4, 4, 5, ItemID.NECKLACE_OF_PASSAGE5), + + BURNING_AMULET1(ItemID.BURNING_AMULET1, 1, 5, ItemID.BURNING_AMULET5), + BURNING_AMULET2(ItemID.BURNING_AMULET2, 2, 5, ItemID.BURNING_AMULET5), + BURNING_AMULET3(ItemID.BURNING_AMULET3, 3, 5, ItemID.BURNING_AMULET5), + BURNING_AMULET4(ItemID.BURNING_AMULET4, 4, 5, ItemID.BURNING_AMULET5); + + private final int itemId; + private final int currentCharges; + private final int maxCharges; + private final int chargedId; + + private static final Map DYNAMIC_ITEMS; + + static + { + final ImmutableMap.Builder map = ImmutableMap.builder(); + for (final DynamicPriceItem p : values()) + { + map.put(p.itemId, p); + } + DYNAMIC_ITEMS = map.build(); + } + + /** + * Calculates the price off the partially charged jewelry based on the base items price + * @param basePrice price of the base item, usually the trade-able variant + * @return death price of the current DynamicPriceItem + */ + int calculateDeathPrice(final int basePrice) + { + return (basePrice / maxCharges) * currentCharges; + } + + @Nullable + static DynamicPriceItem find(int itemId) + { + return DYNAMIC_ITEMS.get(itemId); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java index 5afb14c931..74120a47d6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java @@ -28,8 +28,6 @@ package net.runelite.client.plugins.itemskeptondeath; import com.google.common.collect.ImmutableMap; import java.util.Map; import javax.annotation.Nullable; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; @@ -37,8 +35,7 @@ import net.runelite.api.ItemID; * Some items have a fixed price that is added to its default value when calculating death prices. * These are typically imbued items, such as Berserker ring (i), to help it protect over the non-imbued variants. */ -@AllArgsConstructor -@Getter(AccessLevel.PACKAGE) +@Getter enum FixedPriceItem { IMBUED_BLACK_MASK_I(ItemID.BLACK_MASK_I, 5000), @@ -67,10 +64,161 @@ enum FixedPriceItem IMBUED_RING_OF_THE_GODS_I(ItemID.RING_OF_THE_GODS_I, 2000), IMBUED_TREASONOUS_RING_I(ItemID.TREASONOUS_RING_I, 2000), - IMBUED_TYRANNICAL_RING_I(ItemID.TYRANNICAL_RING_I, 2000); + IMBUED_TYRANNICAL_RING_I(ItemID.TYRANNICAL_RING_I, 2000), + + GRACEFUL_HOOD(ItemID.GRACEFUL_HOOD, 1965), + GRACEFUL_CAPE(ItemID.GRACEFUL_CAPE, 2460), + GRACEFUL_TOP(ItemID.GRACEFUL_TOP, 2345), + GRACEFUL_LEGS(ItemID.GRACEFUL_LEGS, 2290), + GRACEFUL_GLOVES(ItemID.GRACEFUL_GLOVES, 1970), + GRACEFUL_BOOTS(ItemID.GRACEFUL_BOOTS, 2060), + + ANGLER_HAT(ItemID.ANGLER_HAT, 2600), + ANGLER_TOP(ItemID.ANGLER_TOP, 3550), + ANGLER_WADERS(ItemID.ANGLER_WADERS, 4400), + ANGLER_BOOTS(ItemID.ANGLER_BOOTS, 5300), + + PROSPECTOR_HELMET(ItemID.PROSPECTOR_HELMET, 2640), + PROSPECTOR_JACKET(ItemID.PROSPECTOR_JACKET, 3550), + PROSPECTOR_LEGS(ItemID.PROSPECTOR_LEGS, 4460), + PROSPECTOR_BOOTS(ItemID.PROSPECTOR_BOOTS, 5370), + + LUMBERJACK_HAT(ItemID.LUMBERJACK_HAT, 19950), + LUMBERJACK_TOP(ItemID.LUMBERJACK_TOP, 19950), + LUMBERJACK_LEGS(ItemID.LUMBERJACK_LEGS, 19950), + LUMBERJACK_BOOTS(ItemID.LUMBERJACK_BOOTS, 19950), + + ROGUE_MASK(ItemID.ROGUE_MASK, 725), + ROGUE_TOP(ItemID.ROGUE_TOP, 575), + ROGUE_TROUSERS(ItemID.ROGUE_TROUSERS, 500), + ROGUE_GLOVES(ItemID.ROGUE_GLOVES, 650), + ROGUE_BOOTS(ItemID.ROGUE_BOOTS, 650), + + RING_OF_WEALTH_1(ItemID.RING_OF_WEALTH_1, 500, ItemID.RING_OF_WEALTH), + RING_OF_WEALTH_2(ItemID.RING_OF_WEALTH_2, 1000, ItemID.RING_OF_WEALTH), + RING_OF_WEALTH_3(ItemID.RING_OF_WEALTH_3, 1500, ItemID.RING_OF_WEALTH), + RING_OF_WEALTH_4(ItemID.RING_OF_WEALTH_4, 2000, ItemID.RING_OF_WEALTH), + + AMULET_OF_GLORY1(ItemID.AMULET_OF_GLORY1, 500, ItemID.AMULET_OF_GLORY), + AMULET_OF_GLORY2(ItemID.AMULET_OF_GLORY2, 1000, ItemID.AMULET_OF_GLORY), + AMULET_OF_GLORY3(ItemID.AMULET_OF_GLORY3, 1500, ItemID.AMULET_OF_GLORY), + AMULET_OF_GLORY5(ItemID.AMULET_OF_GLORY5, 2500, ItemID.AMULET_OF_GLORY), + + COMBAT_BRACELET1(ItemID.COMBAT_BRACELET1, 500, ItemID.COMBAT_BRACELET), + COMBAT_BRACELET2(ItemID.COMBAT_BRACELET2, 1000, ItemID.COMBAT_BRACELET), + COMBAT_BRACELET3(ItemID.COMBAT_BRACELET3, 1500, ItemID.COMBAT_BRACELET), + COMBAT_BRACELET5(ItemID.COMBAT_BRACELET5, 2500, ItemID.COMBAT_BRACELET), + + SKILLS_NECKLACE1(ItemID.SKILLS_NECKLACE1, 500, ItemID.SKILLS_NECKLACE), + SKILLS_NECKLACE2(ItemID.SKILLS_NECKLACE2, 1000, ItemID.SKILLS_NECKLACE), + SKILLS_NECKLACE3(ItemID.SKILLS_NECKLACE3, 1500, ItemID.SKILLS_NECKLACE), + SKILLS_NECKLACE4(ItemID.SKILLS_NECKLACE5, 2500, ItemID.SKILLS_NECKLACE), + + AHRIMS_HOOD_25(ItemID.AHRIMS_HOOD_25, 2500, ItemID.AHRIMS_HOOD_0), + AHRIMS_HOOD_50(ItemID.AHRIMS_HOOD_50, 5000, ItemID.AHRIMS_HOOD_0), + AHRIMS_HOOD_75(ItemID.AHRIMS_HOOD_75, 7500, ItemID.AHRIMS_HOOD_0), + AHRIMS_HOOD_100(ItemID.AHRIMS_HOOD_100, 10000, ItemID.AHRIMS_HOOD_0), + AHRIMS_ROBETOP_25(ItemID.AHRIMS_ROBETOP_25, 2500, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBETOP_50(ItemID.AHRIMS_ROBETOP_50, 5000, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBETOP_75(ItemID.AHRIMS_ROBETOP_75, 7500, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBETOP_100(ItemID.AHRIMS_ROBETOP_100, 10000, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBESKIRT_25(ItemID.AHRIMS_ROBESKIRT_25, 2500, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_ROBESKIRT_50(ItemID.AHRIMS_ROBESKIRT_50, 5000, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_ROBESKIRT_75(ItemID.AHRIMS_ROBESKIRT_75, 7500, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_ROBESKIRT_100(ItemID.AHRIMS_ROBESKIRT_100, 10000, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_STAFF_25(ItemID.AHRIMS_STAFF_25, 2500, ItemID.AHRIMS_STAFF_0), + AHRIMS_STAFF_50(ItemID.AHRIMS_STAFF_50, 5000, ItemID.AHRIMS_STAFF_0), + AHRIMS_STAFF_75(ItemID.AHRIMS_STAFF_75, 7500, ItemID.AHRIMS_STAFF_0), + AHRIMS_STAFF_100(ItemID.AHRIMS_STAFF_100, 10000, ItemID.AHRIMS_STAFF_0), + + KARILS_COIF_25(ItemID.KARILS_COIF_25, 2500, ItemID.KARILS_COIF_0), + KARILS_COIF_50(ItemID.KARILS_COIF_50, 5000, ItemID.KARILS_COIF_0), + KARILS_COIF_75(ItemID.KARILS_COIF_75, 7500, ItemID.KARILS_COIF_0), + KARILS_COIF_100(ItemID.KARILS_COIF_100, 10000, ItemID.KARILS_COIF_0), + KARILS_LEATHERTOP_25(ItemID.KARILS_LEATHERTOP_25, 2500, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERTOP_50(ItemID.KARILS_LEATHERTOP_50, 5000, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERTOP_75(ItemID.KARILS_LEATHERTOP_75, 7500, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERTOP_100(ItemID.KARILS_LEATHERTOP_100, 10000, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERSKIRT_25(ItemID.KARILS_LEATHERSKIRT_25, 2500, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_LEATHERSKIRT_50(ItemID.KARILS_LEATHERSKIRT_50, 5000, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_LEATHERSKIRT_75(ItemID.KARILS_LEATHERSKIRT_75, 7500, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_LEATHERSKIRT_100(ItemID.KARILS_LEATHERSKIRT_100, 10000, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_CROSSBOW_25(ItemID.KARILS_CROSSBOW_25, 2500, ItemID.KARILS_CROSSBOW_0), + KARILS_CROSSBOW_50(ItemID.KARILS_CROSSBOW_50, 5000, ItemID.KARILS_CROSSBOW_0), + KARILS_CROSSBOW_75(ItemID.KARILS_CROSSBOW_75, 7500, ItemID.KARILS_CROSSBOW_0), + KARILS_CROSSBOW_100(ItemID.KARILS_CROSSBOW_100, 10000, ItemID.KARILS_CROSSBOW_0), + + DHAROKS_HELM_25(ItemID.DHAROKS_HELM_25, 2500, ItemID.DHAROKS_HELM_0), + DHAROKS_HELM_50(ItemID.DHAROKS_HELM_50, 5000, ItemID.DHAROKS_HELM_0), + DHAROKS_HELM_75(ItemID.DHAROKS_HELM_75, 7500, ItemID.DHAROKS_HELM_0), + DHAROKS_HELM_100(ItemID.DHAROKS_HELM_100, 10000, ItemID.DHAROKS_HELM_0), + DHAROKS_PLATEBODY_25(ItemID.DHAROKS_PLATEBODY_25, 2500, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATEBODY_50(ItemID.DHAROKS_PLATEBODY_50, 5000, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATEBODY_75(ItemID.DHAROKS_PLATEBODY_75, 7500, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATEBODY_100(ItemID.DHAROKS_PLATEBODY_100, 10000, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATELEGS_25(ItemID.DHAROKS_PLATELEGS_25, 2500, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_PLATELEGS_50(ItemID.DHAROKS_PLATELEGS_50, 5000, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_PLATELEGS_75(ItemID.DHAROKS_PLATELEGS_75, 7500, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_PLATELEGS_100(ItemID.DHAROKS_PLATELEGS_100, 10000, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_GREATAXE_25(ItemID.DHAROKS_GREATAXE_25, 2500, ItemID.DHAROKS_GREATAXE_0), + DHAROKS_GREATAXE_50(ItemID.DHAROKS_GREATAXE_50, 5000, ItemID.DHAROKS_GREATAXE_0), + DHAROKS_GREATAXE_75(ItemID.DHAROKS_GREATAXE_75, 7500, ItemID.DHAROKS_GREATAXE_0), + DHAROKS_GREATAXE_100(ItemID.DHAROKS_GREATAXE_100, 10000, ItemID.DHAROKS_GREATAXE_0), + + GUTHANS_HELM_25(ItemID.GUTHANS_HELM_25, 2500, ItemID.GUTHANS_HELM_0), + GUTHANS_HELM_50(ItemID.GUTHANS_HELM_50, 5000, ItemID.GUTHANS_HELM_0), + GUTHANS_HELM_75(ItemID.GUTHANS_HELM_75, 7500, ItemID.GUTHANS_HELM_0), + GUTHANS_HELM_100(ItemID.GUTHANS_HELM_100, 10000, ItemID.GUTHANS_HELM_0), + GUTHANS_PLATEBODY_25(ItemID.GUTHANS_PLATEBODY_25, 2500, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_PLATEBODY_50(ItemID.GUTHANS_PLATEBODY_50, 5000, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_PLATEBODY_75(ItemID.GUTHANS_PLATEBODY_75, 7500, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_PLATEBODY_100(ItemID.GUTHANS_PLATEBODY_100, 10000, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_CHAINSKIRT_25(ItemID.GUTHANS_CHAINSKIRT_25, 2500, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_CHAINSKIRT_50(ItemID.GUTHANS_CHAINSKIRT_50, 5000, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_CHAINSKIRT_75(ItemID.GUTHANS_CHAINSKIRT_75, 7500, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_CHAINSKIRT_100(ItemID.GUTHANS_CHAINSKIRT_100, 10000, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_WARSPEAR_25(ItemID.GUTHANS_WARSPEAR_25, 2500, ItemID.GUTHANS_WARSPEAR_0), + GUTHANS_WARSPEAR_50(ItemID.GUTHANS_WARSPEAR_50, 5000, ItemID.GUTHANS_WARSPEAR_0), + GUTHANS_WARSPEAR_75(ItemID.GUTHANS_WARSPEAR_75, 7500, ItemID.GUTHANS_WARSPEAR_0), + GUTHANS_WARSPEAR_100(ItemID.GUTHANS_WARSPEAR_100, 10000, ItemID.GUTHANS_WARSPEAR_0), + + TORAGS_HELM_25(ItemID.TORAGS_HELM_25, 2500, ItemID.TORAGS_HELM_0), + TORAGS_HELM_50(ItemID.TORAGS_HELM_50, 5000, ItemID.TORAGS_HELM_0), + TORAGS_HELM_75(ItemID.TORAGS_HELM_75, 7500, ItemID.TORAGS_HELM_0), + TORAGS_HELM_100(ItemID.TORAGS_HELM_100, 10000, ItemID.TORAGS_HELM_0), + TORAGS_PLATEBODY_25(ItemID.TORAGS_PLATEBODY_25, 2500, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATEBODY_50(ItemID.TORAGS_PLATEBODY_50, 5000, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATEBODY_75(ItemID.TORAGS_PLATEBODY_75, 7500, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATEBODY_100(ItemID.TORAGS_PLATEBODY_100, 10000, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATELEGS_25(ItemID.TORAGS_PLATELEGS_25, 2500, ItemID.TORAGS_PLATELEGS_0), + TORAGS_PLATELEGS_50(ItemID.TORAGS_PLATELEGS_50, 5000, ItemID.TORAGS_PLATELEGS_0), + TORAGS_PLATELEGS_75(ItemID.TORAGS_PLATELEGS_75, 7500, ItemID.TORAGS_PLATELEGS_0), + TORAGS_PLATELEGS_100(ItemID.TORAGS_PLATELEGS_100, 10000, ItemID.TORAGS_PLATELEGS_0), + TORAGS_HAMMERS_25(ItemID.TORAGS_HAMMERS_25, 2500, ItemID.TORAGS_HAMMERS_0), + TORAGS_HAMMERS_50(ItemID.TORAGS_HAMMERS_50, 5000, ItemID.TORAGS_HAMMERS_0), + TORAGS_HAMMERS_75(ItemID.TORAGS_HAMMERS_75, 7500, ItemID.TORAGS_HAMMERS_0), + TORAGS_HAMMERS_100(ItemID.TORAGS_HAMMERS_100, 10000, ItemID.TORAGS_HAMMERS_0), + + VERACS_HELM_25(ItemID.VERACS_HELM_25, 2500, ItemID.VERACS_HELM_0), + VERACS_HELM_50(ItemID.VERACS_HELM_50, 5000, ItemID.VERACS_HELM_0), + VERACS_HELM_75(ItemID.VERACS_HELM_75, 7500, ItemID.VERACS_HELM_0), + VERACS_HELM_100(ItemID.VERACS_HELM_100, 10000, ItemID.VERACS_HELM_0), + VERACS_BRASSARD_25(ItemID.VERACS_BRASSARD_25, 2500, ItemID.VERACS_BRASSARD_0), + VERACS_BRASSARD_50(ItemID.VERACS_BRASSARD_50, 5000, ItemID.VERACS_BRASSARD_0), + VERACS_BRASSARD_75(ItemID.VERACS_BRASSARD_75, 7500, ItemID.VERACS_BRASSARD_0), + VERACS_BRASSARD_100(ItemID.VERACS_BRASSARD_100, 10000, ItemID.VERACS_BRASSARD_0), + VERACS_PLATESKIRT_25(ItemID.VERACS_PLATESKIRT_25, 2500, ItemID.VERACS_PLATESKIRT_0), + VERACS_PLATESKIRT_50(ItemID.VERACS_PLATESKIRT_50, 5000, ItemID.VERACS_PLATESKIRT_0), + VERACS_PLATESKIRT_75(ItemID.VERACS_PLATESKIRT_75, 7500, ItemID.VERACS_PLATESKIRT_0), + VERACS_PLATESKIRT_100(ItemID.VERACS_PLATESKIRT_100, 10000, ItemID.VERACS_PLATESKIRT_0), + VERACS_FLAIL_25(ItemID.VERACS_FLAIL_25, 2500, ItemID.VERACS_FLAIL_0), + VERACS_FLAIL_50(ItemID.VERACS_FLAIL_50, 5000, ItemID.VERACS_FLAIL_0), + VERACS_FLAIL_75(ItemID.VERACS_FLAIL_75, 7500, ItemID.VERACS_FLAIL_0), + VERACS_FLAIL_100(ItemID.VERACS_FLAIL_100, 10000, ItemID.VERACS_FLAIL_0); private final int itemId; private final int offset; + private final int baseId; private static final Map FIXED_ITEMS; @@ -84,6 +232,18 @@ enum FixedPriceItem FIXED_ITEMS = map.build(); } + FixedPriceItem(final int itemId, final int offset, final int baseId) + { + this.itemId = itemId; + this.offset = offset; + this.baseId = baseId; + } + + FixedPriceItem(final int itemId, final int offset) + { + this(itemId, offset, -1); + } + @Nullable static FixedPriceItem find(int itemId) { diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect3Definition.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java similarity index 81% rename from cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect3Definition.java rename to runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java index a6580c046f..d2f09dd856 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect3Definition.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,12 +22,15 @@ * (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.cache.definitions.sound; +package net.runelite.client.plugins.itemskeptondeath; -public class SoundEffect3Definition +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +class ItemStack { - public int[][][] field1154 = new int[2][2][4]; - public int[] field1155 = new int[2]; - public int[] field1156 = new int[2]; - public int[][][] field1159 = new int[2][2][4]; + private int id; + private int qty; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java index f6d817b54c..89115daa29 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.itemskeptondeath; +import com.google.common.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -32,8 +33,11 @@ import java.util.EnumSet; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Constants; @@ -55,6 +59,7 @@ import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetType; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; +import net.runelite.client.game.ItemMapping; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.util.StackFormatter; @@ -71,6 +76,16 @@ public class ItemsKeptOnDeathPlugin extends Plugin private static final int DEEP_WILDY = 20; private static final Pattern WILDERNESS_LEVEL_PATTERN = Pattern.compile("^Level: (\\d+).*"); + @AllArgsConstructor + @Getter + @VisibleForTesting + static class DeathItems + { + private final List keptItems; + private final List lostItems; + private final boolean hasAlwaysLost; + } + // Item Container helpers private static final int MAX_ROW_ITEMS = 8; private static final int ITEM_X_OFFSET = 5; @@ -100,9 +115,12 @@ public class ItemsKeptOnDeathPlugin extends Plugin private WidgetButton deepWildyButton; private WidgetButton lowWildyButton; - private boolean isSkulled; - private boolean protectingItem; - private int wildyLevel; + @VisibleForTesting + boolean isSkulled; + @VisibleForTesting + boolean protectingItem; + @VisibleForTesting + int wildyLevel; @Subscribe public void onScriptCallbackEvent(ScriptCallbackEvent event) @@ -225,97 +243,12 @@ public class ItemsKeptOnDeathPlugin extends Plugin final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); final Item[] equip = equipment == null ? new Item[0] : equipment.getItems(); - final List items = new ArrayList<>(); - Collections.addAll(items, inv); - Collections.addAll(items, equip); + final DeathItems deathItems = calculateKeptLostItems(inv, equip); - // Sort by item price - items.sort(Comparator.comparing(this::getDeathPrice).reversed()); - - boolean hasAlwaysLost = false; - int keepCount = getDefaultItemsKept(); - - final List keptItems = new ArrayList<>(); - final List lostItems = new ArrayList<>(); - for (final Item i : items) - { - final int id = i.getId(); - int itemQuantity = i.getQuantity(); - - if (id == -1) - { - continue; - } - - final ItemDefinition c = itemManager.getItemDefinition(i.getId()); - - // Bonds are always kept and do not count towards the limit. - if (id == ItemID.OLD_SCHOOL_BOND || id == ItemID.OLD_SCHOOL_BOND_UNTRADEABLE) - { - final Widget itemWidget = createItemWidget(kept, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, itemQuantity, c.getName()); - keptItems.add(itemWidget); - continue; - } - - // Certain items are always lost on death and have a white outline which we need to add - final AlwaysLostItem alwaysLostItem = AlwaysLostItem.getByItemID(i.getId()); - if (alwaysLostItem != null) - { - // Some of these items are kept on death (outside wildy), like the Rune pouch. Ignore them - if (!alwaysLostItem.isKeptOutsideOfWilderness() || wildyLevel > 0) - { - final Widget itemWidget = createItemWidget(lost, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 0, itemQuantity, c.getName()); - itemWidget.setBorderType(2); // white outline - lostItems.add(itemWidget); - hasAlwaysLost = true; - continue; - } - // the rune pouch is "always lost" but its kept outside of pvp, and does not count towards your keep count - } - else if (keepCount > 0) - { - // Keep most valuable items regardless of trade-ability. - if (i.getQuantity() > keepCount) - { - final Widget itemWidget = createItemWidget(kept, keepCount, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, keepCount, c.getName()); - keptItems.add(itemWidget); - itemQuantity -= keepCount; - keepCount = 0; - // Fall through to below to drop the rest of the stack - } - else - { - final Widget itemWidget = createItemWidget(kept, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, itemQuantity, c.getName()); - keptItems.add(itemWidget); - keepCount -= i.getQuantity(); - continue; - } - } - - // Items are kept if: - // 1) is not tradeable - // 2) is under the deep wilderness line - // 3) is outside of the wilderness, or item has a broken form - if (!Pets.isPet(id) - && !isTradeable(c) && wildyLevel <= DEEP_WILDY - && (wildyLevel <= 0 || BrokenOnDeathItem.isBrokenOnDeath(i.getId()))) - { - final Widget itemWidget = createItemWidget(kept, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, itemQuantity, c.getName()); - keptItems.add(itemWidget); - } - else - { - // Otherwise, the item is lost - final Widget itemWidget = createItemWidget(lost, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 0, itemQuantity, c.getName()); - lostItems.add(itemWidget); - } - } + final List keptItems = deathItems.getKeptItems().stream() + .map(item -> createItemWidget(kept, item, true)).collect(Collectors.toList()); + final List lostItems = deathItems.getLostItems().stream() + .map(item -> createItemWidget(lost, item, false)).collect(Collectors.toList()); int rows = (keptItems.size() + MAX_ROW_ITEMS - 1) / MAX_ROW_ITEMS; // Show an empty row if there isn't anything @@ -330,36 +263,209 @@ public class ItemsKeptOnDeathPlugin extends Plugin positionWidgetItems(kept, keptItems); positionWidgetItems(lost, lostItems); - updateKeptWidgetInfoText(hasAlwaysLost, keptItems, lostItems); + updateKeptWidgetInfoText(deathItems.isHasAlwaysLost(), keptItems, lostItems); + } + + /** + * Calculates which items will be kept/lost. first list is kept items, second is lost. + * + * @param inv players inventory + * @param equip players equipement + * @return list of items kept followed by a list of items lost + */ + @VisibleForTesting + DeathItems calculateKeptLostItems(final Item[] inv, final Item[] equip) + { + final List items = new ArrayList<>(); + Collections.addAll(items, inv); + Collections.addAll(items, equip); + + // Sort by item price + items.sort(Comparator.comparing(this::getDeathPrice).reversed()); + + boolean hasClueBox = false; + boolean hasAlwaysLost = false; + int keepCount = getDefaultItemsKept(); + + final List keptItems = new ArrayList<>(); + final List lostItems = new ArrayList<>(); + + for (final Item i : items) + { + final int id = i.getId(); + int qty = i.getQuantity(); + if (id == -1) + { + continue; + } + + final ItemDefinition c = itemManager.getItemDefinition(i.getId()); + + // Bonds are always kept and do not count towards the limit. + if (id == ItemID.OLD_SCHOOL_BOND || id == ItemID.OLD_SCHOOL_BOND_UNTRADEABLE) + { + keptItems.add(new ItemStack(id, qty)); + continue; + } + + final AlwaysLostItem alwaysLostItem = AlwaysLostItem.getByItemID(id); + if (alwaysLostItem != null && (!alwaysLostItem.isKeptOutsideOfWilderness() || wildyLevel > 0)) + { + hasAlwaysLost = true; + hasClueBox = hasClueBox || id == ItemID.CLUE_BOX; + lostItems.add(new ItemStack(id, qty)); + continue; + } + + if (keepCount > 0) + { + // Keep most valuable items regardless of trade-ability. + if (i.getQuantity() > keepCount) + { + keptItems.add(new ItemStack(id, keepCount)); + qty -= keepCount; + keepCount = 0; + // Fall through to determine if the rest of the stack should drop + } + else + { + keptItems.add(new ItemStack(id, qty)); + keepCount -= qty; + continue; + } + } + + // Items are kept if: + // 1) is not tradeable + // 2) is under the deep wilderness line + // 3) is outside of the wilderness, or item has a broken form + if (!Pets.isPet(id) + && !LostIfNotProtected.isLostIfNotProtected(id) + && !isTradeable(itemManager.getItemDefinition(id)) && wildyLevel <= DEEP_WILDY + && (wildyLevel <= 0 || BrokenOnDeathItem.isBrokenOnDeath(i.getId()))) + { + keptItems.add(new ItemStack(id, qty)); + } + else + { + // Otherwise, the item is lost + lostItems.add(new ItemStack(id, qty)); + } + } + + if (hasClueBox) + { + boolean alreadyProtectingClue = false; + for (final ItemStack item : keptItems) + { + if (isClueBoxable(item.getId())) + { + alreadyProtectingClue = true; + break; + } + } + + if (!alreadyProtectingClue) + { + int clueId = -1; + // Clue box protects the last clue in your inventory so loop over the players inv + for (final Item i : inv) + { + final int id = i.getId(); + if (id != -1 && isClueBoxable(id)) + { + clueId = id; + } + } + + if (clueId != -1) + { + // Move the boxed item to the kept items container and remove it from the lost items container + for (final ItemStack boxableItem : lostItems) + { + if (boxableItem.getId() == clueId) + { + if (boxableItem.getQty() > 1) + { + boxableItem.setQty(boxableItem.getQty() - 1); + keptItems.add(new ItemStack(clueId, 1)); + } + else + { + lostItems.remove(boxableItem); + keptItems.add(boxableItem); + } + break; + } + } + } + } + } + + return new DeathItems(keptItems, lostItems, hasAlwaysLost); + } + + @VisibleForTesting + boolean isClueBoxable(final int itemID) + { + final String name = itemManager.getItemDefinition(itemID).getName(); + return name.contains("Clue scroll (") || name.contains("Reward casket ("); } /** * Get the price of an item + * * @param item * @return */ - private int getDeathPrice(Item item) + @VisibleForTesting + int getDeathPrice(Item item) { + // 1) Check if the death price is dynamically calculated, if so return that value + // 2) If death price is based off another item default to that price, otherwise apply normal ItemMapping GE price + // 3) If still no price, default to store price + // 4) Apply fixed price offset if applicable + int itemId = item.getId(); // Unnote/unplaceholder item int canonicalizedItemId = itemManager.canonicalize(itemId); - int exchangePrice = itemManager.getItemPrice(canonicalizedItemId); + int exchangePrice = 0; + + final DynamicPriceItem dynamicPrice = DynamicPriceItem.find(canonicalizedItemId); + if (dynamicPrice != null) + { + final ItemDefinition c1 = itemManager.getItemDefinition(canonicalizedItemId); + exchangePrice = c1.getPrice(); + final int basePrice = itemManager.getItemPrice(dynamicPrice.getChargedId(), true); + return dynamicPrice.calculateDeathPrice(basePrice); + } + + // Some items have artificially offset death prices - such as ring imbues + // which are +2k over the non imbues. Check if the item has a fixed price offset + final FixedPriceItem fixedPrice = FixedPriceItem.find(canonicalizedItemId); + if (fixedPrice != null && fixedPrice.getBaseId() != -1) + { + // Grab base item price + exchangePrice = itemManager.getItemPrice(fixedPrice.getBaseId(), true); + } + else + { + // Account for items whose death value comes from their tradeable variant (barrows) or components (ornate kits) + for (final int mappedID : ItemMapping.map(canonicalizedItemId)) + { + exchangePrice += itemManager.getItemPrice(mappedID, true); + } + } + if (exchangePrice == 0) { final ItemDefinition c1 = itemManager.getItemDefinition(canonicalizedItemId); exchangePrice = c1.getPrice(); } - else - { - // Some items have artifically applied death prices - such as ring imbues - // which are +2k over the non imbues. Check if the item has a fixed price. - FixedPriceItem fixedPrice = FixedPriceItem.find(canonicalizedItemId); - if (fixedPrice != null) - { - // Apply fixed price offset - exchangePrice += fixedPrice.getOffset(); - } - } + + // Apply fixed price offset + exchangePrice += fixedPrice == null ? 0 : fixedPrice.getOffset(); + return exchangePrice; } @@ -591,21 +697,29 @@ public class ItemsKeptOnDeathPlugin extends Plugin /** * Creates an Item Widget for use inside the Kept on Death Interface * - * @param qty Amount of item - * @param c Items Composition - * @return + * @param parent Widget to add element too as a child + * @param item the TempItem representing the item + * @param kept is the item being shown in the kept items container + * @return the Widget that was added to the `parent` */ - private static Widget createItemWidget(final Widget parent, final int qty, final ItemDefinition c) + private Widget createItemWidget(final Widget parent, final ItemStack item, boolean kept) { + final int id = item.getId(); + final int qty = item.getQty(); + final ItemDefinition c = itemManager.getItemDefinition(id); + final Widget itemWidget = parent.createChild(-1, WidgetType.GRAPHIC); - itemWidget.setItemId(c.getId()); - itemWidget.setItemQuantity(qty); - itemWidget.setHasListener(true); itemWidget.setOriginalWidth(Constants.ITEM_SPRITE_WIDTH); itemWidget.setOriginalHeight(Constants.ITEM_SPRITE_HEIGHT); - itemWidget.setBorderType(1); - + itemWidget.setItemId(id); + itemWidget.setItemQuantity(qty); itemWidget.setAction(1, String.format("Item: %s", c.getName())); + itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, kept ? 1 : 0, qty, c.getName()); + itemWidget.setHasListener(true); + + final AlwaysLostItem alwaysLostItem = AlwaysLostItem.getByItemID(id); + final boolean whiteBorder = alwaysLostItem != null && (!alwaysLostItem.isKeptOutsideOfWilderness() || wildyLevel > 0); + itemWidget.setBorderType(whiteBorder ? 2 : 1); return itemWidget; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java new file mode 100644 index 0000000000..56a12c4b79 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import net.runelite.api.ItemID; + +final class LostIfNotProtected +{ + private static final Set ITEMS = ImmutableSet.of( + ItemID.AMULET_OF_THE_DAMNED, + ItemID.RING_OF_CHAROS, ItemID.RING_OF_CHAROSA, + ItemID.LUNAR_STAFF, + ItemID.SHADOW_SWORD, + ItemID.KERIS, ItemID.KERISP, ItemID.KERISP_10583, ItemID.KERISP_10584 + ); + + public static boolean isLostIfNotProtected(int id) + { + return ITEMS.contains(id); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java index cc14b5ff67..e025cd6fad 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java @@ -103,10 +103,153 @@ public interface KeyRemappingConfig extends Config position = 6, keyName = "fkeyRemap", name = "Remap F Keys", - description = "Configures whether F-Keys are Remapped to 1 (F1) through 0 (F10), '-' (F11), and '=' (F12)" + description = "Configures whether F-Keys use remapped keys" ) default boolean fkeyRemap() { return false; } + + @ConfigItem( + position = 7, + keyName = "f1", + name = "F1", + description = "The key which will replace {F1}." + ) + default ModifierlessKeybind f1() + { + return new ModifierlessKeybind(KeyEvent.VK_1, 0); + } + + @ConfigItem( + position = 8, + keyName = "f2", + name = "F2", + description = "The key which will replace {F2}." + ) + default ModifierlessKeybind f2() + { + return new ModifierlessKeybind(KeyEvent.VK_2, 0); + } + + @ConfigItem( + position = 9, + keyName = "f3", + name = "F3", + description = "The key which will replace {F3}." + ) + default ModifierlessKeybind f3() + { + return new ModifierlessKeybind(KeyEvent.VK_3, 0); + } + + @ConfigItem( + position = 10, + keyName = "f4", + name = "F4", + description = "The key which will replace {F4}." + ) + default ModifierlessKeybind f4() + { + return new ModifierlessKeybind(KeyEvent.VK_4, 0); + } + + @ConfigItem( + position = 11, + keyName = "f5", + name = "F5", + description = "The key which will replace {F5}." + ) + default ModifierlessKeybind f5() + { + return new ModifierlessKeybind(KeyEvent.VK_5, 0); + } + + @ConfigItem( + position = 12, + keyName = "f6", + name = "F6", + description = "The key which will replace {F6}." + ) + default ModifierlessKeybind f6() + { + return new ModifierlessKeybind(KeyEvent.VK_6, 0); + } + + @ConfigItem( + position = 13, + keyName = "f7", + name = "F7", + description = "The key which will replace {F7}." + ) + default ModifierlessKeybind f7() + { + return new ModifierlessKeybind(KeyEvent.VK_7, 0); + } + + @ConfigItem( + position = 14, + keyName = "f8", + name = "F8", + description = "The key which will replace {F8}." + ) + default ModifierlessKeybind f8() + { + return new ModifierlessKeybind(KeyEvent.VK_8, 0); + } + + @ConfigItem( + position = 15, + keyName = "f9", + name = "F9", + description = "The key which will replace {F9}." + ) + default ModifierlessKeybind f9() + { + return new ModifierlessKeybind(KeyEvent.VK_9, 0); + } + + @ConfigItem( + position = 16, + keyName = "f10", + name = "F10", + description = "The key which will replace {F10}." + ) + default ModifierlessKeybind f10() + { + return new ModifierlessKeybind(KeyEvent.VK_0, 0); + } + + @ConfigItem( + position = 17, + keyName = "f11", + name = "F11", + description = "The key which will replace {F11}." + ) + default ModifierlessKeybind f11() + { + return new ModifierlessKeybind(KeyEvent.VK_MINUS, 0); + } + + @ConfigItem( + position = 18, + keyName = "f12", + name = "F12", + description = "The key which will replace {F12}." + ) + default ModifierlessKeybind f12() + { + return new ModifierlessKeybind(KeyEvent.VK_EQUALS, 0); + } + + @ConfigItem( + position = 19, + keyName = "esc", + name = "ESC", + description = "The key which will replace {ESC}." + ) + default ModifierlessKeybind esc() + { + return new ModifierlessKeybind(KeyEvent.VK_ESCAPE, 0); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java index d4a493769f..185545ba27 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java @@ -35,30 +35,19 @@ import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.VarClientStr; import net.runelite.client.callback.ClientThread; -import net.runelite.client.config.Keybind; -import net.runelite.client.config.ModifierlessKeybind; import net.runelite.client.input.KeyListener; import net.runelite.client.input.MouseAdapter; @Singleton class KeyRemappingListener extends MouseAdapter implements KeyListener { - private static final Keybind ONE = new ModifierlessKeybind(KeyEvent.VK_1, 0); - private static final Keybind TWO = new ModifierlessKeybind(KeyEvent.VK_2, 0); - private static final Keybind THREE = new ModifierlessKeybind(KeyEvent.VK_3, 0); - private static final Keybind FOUR = new ModifierlessKeybind(KeyEvent.VK_4, 0); - private static final Keybind FIVE = new ModifierlessKeybind(KeyEvent.VK_5, 0); - private static final Keybind SIX = new ModifierlessKeybind(KeyEvent.VK_6, 0); - private static final Keybind SEVEN = new ModifierlessKeybind(KeyEvent.VK_7, 0); - private static final Keybind EIGHT = new ModifierlessKeybind(KeyEvent.VK_8, 0); - private static final Keybind NINE = new ModifierlessKeybind(KeyEvent.VK_9, 0); - private static final Keybind ZERO = new ModifierlessKeybind(KeyEvent.VK_0, 0); - private static final Keybind MINUS = new ModifierlessKeybind(KeyEvent.VK_MINUS, 0); - private static final Keybind EQUALS = new ModifierlessKeybind(KeyEvent.VK_EQUALS, 0); @Inject private KeyRemappingPlugin plugin; + @Inject + private KeyRemappingConfig config; + @Inject private Client client; @@ -111,66 +100,71 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener // to select options if (plugin.isFkeyRemap() && !plugin.isDialogOpen()) { - if (ONE.matches(e)) + if (config.f1().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F1); e.setKeyCode(KeyEvent.VK_F1); } - else if (TWO.matches(e)) + else if (config.f2().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F2); e.setKeyCode(KeyEvent.VK_F2); } - else if (THREE.matches(e)) + else if (config.f3().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F3); e.setKeyCode(KeyEvent.VK_F3); } - else if (FOUR.matches(e)) + else if (config.f4().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F4); e.setKeyCode(KeyEvent.VK_F4); } - else if (FIVE.matches(e)) + else if (config.f5().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F5); e.setKeyCode(KeyEvent.VK_F5); } - else if (SIX.matches(e)) + else if (config.f6().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F6); e.setKeyCode(KeyEvent.VK_F6); } - else if (SEVEN.matches(e)) + else if (config.f7().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F7); e.setKeyCode(KeyEvent.VK_F7); } - else if (EIGHT.matches(e)) + else if (config.f8().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F8); e.setKeyCode(KeyEvent.VK_F8); } - else if (NINE.matches(e)) + else if (config.f9().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F9); e.setKeyCode(KeyEvent.VK_F9); } - else if (ZERO.matches(e)) + else if (config.f10().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F10); e.setKeyCode(KeyEvent.VK_F10); } - else if (MINUS.matches(e)) + else if (config.f11().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F11); e.setKeyCode(KeyEvent.VK_F11); } - else if (EQUALS.matches(e)) + else if (config.f12().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F12); e.setKeyCode(KeyEvent.VK_F12); } + else if (config.esc().matches(e)) + { + modified.put(e.getKeyCode(), KeyEvent.VK_ESCAPE); + e.setKeyCode(KeyEvent.VK_ESCAPE); + } } switch (e.getKeyCode()) @@ -189,8 +183,12 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener { switch (e.getKeyCode()) { - case KeyEvent.VK_ENTER: case KeyEvent.VK_ESCAPE: + // When existing typing mode, block the escape key + // so that it doesn't trigger the in-game hotkeys + e.consume(); + // FALLTHROUGH + case KeyEvent.VK_ENTER: plugin.setTyping(false); clientThread.invoke(plugin::lockChat); break; @@ -240,54 +238,58 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener if (plugin.isFkeyRemap()) { - if (ONE.matches(e)) + if (config.f1().matches(e)) { e.setKeyCode(KeyEvent.VK_F1); } - else if (TWO.matches(e)) + else if (config.f2().matches(e)) { e.setKeyCode(KeyEvent.VK_F2); } - else if (THREE.matches(e)) + else if (config.f3().matches(e)) { e.setKeyCode(KeyEvent.VK_F3); } - else if (FOUR.matches(e)) + else if (config.f4().matches(e)) { e.setKeyCode(KeyEvent.VK_F4); } - else if (FIVE.matches(e)) + else if (config.f5().matches(e)) { e.setKeyCode(KeyEvent.VK_F5); } - else if (SIX.matches(e)) + else if (config.f6().matches(e)) { e.setKeyCode(KeyEvent.VK_F6); } - else if (SEVEN.matches(e)) + else if (config.f7().matches(e)) { e.setKeyCode(KeyEvent.VK_F7); } - else if (EIGHT.matches(e)) + else if (config.f8().matches(e)) { e.setKeyCode(KeyEvent.VK_F8); } - else if (NINE.matches(e)) + else if (config.f9().matches(e)) { e.setKeyCode(KeyEvent.VK_F9); } - else if (ZERO.matches(e)) + else if (config.f10().matches(e)) { e.setKeyCode(KeyEvent.VK_F10); } - else if (MINUS.matches(e)) + else if (config.f11().matches(e)) { e.setKeyCode(KeyEvent.VK_F11); } - else if (EQUALS.matches(e)) + else if (config.f12().matches(e)) { e.setKeyCode(KeyEvent.VK_F12); } + else if (config.esc().matches(e)) + { + e.setKeyCode(KeyEvent.VK_ESCAPE); + } } } else diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java index 7e76be3560..64cc513f22 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java @@ -34,7 +34,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Actor; import static net.runelite.api.AnimationID.LIZARDMAN_SHAMAN_SPAWN; -import net.runelite.api.ChatMessageType; import net.runelite.api.coords.LocalPoint; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.ChatMessage; @@ -104,7 +103,8 @@ public class LizardmenShamanPlugin extends Plugin @Subscribe public void onChatMessage(ChatMessage event) { - if (this.notifyOnSpawn && event.getType() == ChatMessageType.GAMEMESSAGE && event.getMessage().contains(MESSAGE)) + if (this.notifyOnSpawn && /* event.getType() == ChatMessageType.GAMEMESSAGE && */event.getMessage().contains(MESSAGE)) + // ChatMessageType should probably be SPAM <- should be tested first though { notifier.notify(MESSAGE); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java deleted file mode 100644 index f95edccb1b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018 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.regenmeter; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.Stroke; -import java.awt.geom.Arc2D; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.VarPlayer; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPosition; - -@Singleton -public class RegenMeterOverlay extends Overlay -{ - private static final Color HITPOINTS_COLOR = brighter(0x9B0703); - private static final Color SPECIAL_COLOR = brighter(0x1E95B0); - private static final Color OVERLAY_COLOR = new Color(255, 255, 255, 60); - private static final double DIAMETER = 26D; - private static final int OFFSET = 27; - private static final Stroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); - - private final Client client; - private final RegenMeterPlugin plugin; - - private Rectangle getBounds(WidgetInfo widgetInfo) - { - Widget widget = client.getWidget(widgetInfo); - if (widget == null || widget.isHidden()) - { - return null; - } - return widget.getBounds(); - } - - private static Color brighter(int color) - { - float[] hsv = new float[3]; - Color.RGBtoHSB(color >>> 16, (color >> 8) & 0xFF, color & 0xFF, hsv); - return Color.getHSBColor(hsv[0], 1.f, 1.f); - } - - @Inject - public RegenMeterOverlay(final Client client, final RegenMeterPlugin plugin) - { - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_WIDGETS); - this.client = client; - this.plugin = plugin; - } - - @Override - public Dimension render(Graphics2D g) - { - g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); - - if (plugin.isShowHitpoints()) - { - renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, plugin.getHitpointsPercentage(), HITPOINTS_COLOR); - } - - if (plugin.isShowSpecial()) - { - if (client.getVar(VarPlayer.SPECIAL_ATTACK_ENABLED) == 1) - { - final Rectangle bounds = getBounds(WidgetInfo.MINIMAP_SPEC_ORB); - if (bounds != null) - { - g.setColor(RegenMeterOverlay.OVERLAY_COLOR); - g.fillOval( - bounds.x + OFFSET, - bounds.y + (int) (bounds.height / 2D - (DIAMETER) / 2D), - (int) DIAMETER, (int) DIAMETER); - } - } - - renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, plugin.getSpecialPercentage(), SPECIAL_COLOR); - } - - return null; - } - - private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color) - { - final Rectangle bounds = getBounds(widgetInfo); - if (bounds != null) - { - Arc2D.Double arc = new Arc2D.Double(bounds.x + OFFSET, bounds.y + (bounds.height / 2 - DIAMETER / 2), DIAMETER, DIAMETER, 90.d, -360.d * percent, Arc2D.OPEN); - g.setStroke(STROKE); - g.setColor(color); - g.draw(arc); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java deleted file mode 100644 index cc5b57e8ed..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2019, Sean Dewar - * Copyright (c) 2018, Abex - * Copyright (c) 2018, Zimaya - * Copyright (c) 2017, Adam - * 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.regenmeter; - -import com.google.inject.Provides; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.api.Client; -import net.runelite.api.Constants; -import net.runelite.api.GameState; -import net.runelite.api.Prayer; -import net.runelite.api.Skill; -import net.runelite.api.VarPlayer; -import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.VarbitChanged; -import net.runelite.client.Notifier; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.overlay.OverlayManager; - -@PluginDescriptor( - name = "Regeneration Meter", - description = "Track and show the hitpoints and special attack regeneration timers", - tags = {"combat", "health", "hitpoints", "special", "attack", "overlay", "notifications"} -) -@Singleton -public class RegenMeterPlugin extends Plugin -{ - private static final int SPEC_REGEN_TICKS = 50; - private static final int NORMAL_HP_REGEN_TICKS = 100; - - @Inject - private Client client; - - @Inject - private OverlayManager overlayManager; - - @Inject - private Notifier notifier; - - @Inject - private RegenMeterOverlay overlay; - - @Inject - private RegenMeterConfig config; - - @Getter(AccessLevel.PACKAGE) - private double hitpointsPercentage; - - @Getter(AccessLevel.PACKAGE) - private double specialPercentage; - - private int ticksSinceSpecRegen; - private int ticksSinceHPRegen; - private boolean wasRapidHeal; - - @Getter(AccessLevel.PACKAGE) - private boolean showHitpoints; - @Getter(AccessLevel.PACKAGE) - private boolean showSpecial; - private boolean showWhenNoChange; - private int getNotifyBeforeHpRegenSeconds; - - @Provides - RegenMeterConfig provideConfig(ConfigManager configManager) - { - return configManager.getConfig(RegenMeterConfig.class); - } - - @Override - protected void startUp() throws Exception - { - updateConfig(); - overlayManager.add(overlay); - } - - @Override - protected void shutDown() throws Exception - { - overlayManager.remove(overlay); - } - - @Subscribe - private void onGameStateChanged(GameStateChanged ev) - { - if (ev.getGameState() == GameState.HOPPING || ev.getGameState() == GameState.LOGIN_SCREEN) - { - ticksSinceHPRegen = -2; // For some reason this makes this accurate - ticksSinceSpecRegen = 0; - } - } - - @Subscribe - private void onVarbitChanged(VarbitChanged ev) - { - boolean isRapidHeal = client.isPrayerActive(Prayer.RAPID_HEAL); - if (wasRapidHeal != isRapidHeal) - { - ticksSinceHPRegen = 0; - } - wasRapidHeal = isRapidHeal; - } - - @Subscribe - public void onGameTick(GameTick event) - { - if (client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT) == 1000) - { - // The recharge doesn't tick when at 100% - ticksSinceSpecRegen = 0; - } - else - { - ticksSinceSpecRegen = (ticksSinceSpecRegen + 1) % SPEC_REGEN_TICKS; - } - specialPercentage = ticksSinceSpecRegen / (double) SPEC_REGEN_TICKS; - - - int ticksPerHPRegen = NORMAL_HP_REGEN_TICKS; - if (client.isPrayerActive(Prayer.RAPID_HEAL)) - { - ticksPerHPRegen /= 2; - } - - ticksSinceHPRegen = (ticksSinceHPRegen + 1) % ticksPerHPRegen; - hitpointsPercentage = ticksSinceHPRegen / (double) ticksPerHPRegen; - - int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS); - int maxHP = client.getRealSkillLevel(Skill.HITPOINTS); - if (currentHP == maxHP && !this.showWhenNoChange) - { - hitpointsPercentage = 0; - } - else if (currentHP > maxHP) - { - // Show it going down - hitpointsPercentage = 1 - hitpointsPercentage; - } - - if (this.getNotifyBeforeHpRegenSeconds > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen)) - { - notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!"); - } - } - - private boolean shouldNotifyHpRegenThisTick(int ticksPerHPRegen) - { - // if the configured duration lies between two ticks, choose the earlier tick - final int ticksBeforeHPRegen = ticksPerHPRegen - ticksSinceHPRegen; - final int notifyTick = (int) Math.ceil(this.getNotifyBeforeHpRegenSeconds * 1000d / Constants.GAME_TICK_LENGTH); - return ticksBeforeHPRegen == notifyTick; - } - - @Subscribe - public void onConfigChanged(ConfigChanged event) - { - if (event.getGroup().equals("regenmeter")) - { - updateConfig(); - } - } - - private void updateConfig() - { - this.showHitpoints = config.showHitpoints(); - this.showSpecial = config.showSpecial(); - this.showWhenNoChange = config.showWhenNoChange(); - this.getNotifyBeforeHpRegenSeconds = config.getNotifyBeforeHpRegenSeconds(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java deleted file mode 100644 index 258fc98211..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018, Sean Dewar - * 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.runenergy; - -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.Point; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -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.tooltip.Tooltip; -import net.runelite.client.ui.overlay.tooltip.TooltipManager; -import org.apache.commons.lang3.StringUtils; - -@Singleton -class RunEnergyOverlay extends Overlay -{ - private final RunEnergyPlugin plugin; - private final Client client; - private final RunEnergyConfig config; - private final TooltipManager tooltipManager; - - @Inject - private RunEnergyOverlay(final RunEnergyPlugin plugin, final Client client, final RunEnergyConfig config, final TooltipManager tooltipManager) - { - this.plugin = plugin; - this.client = client; - this.config = config; - this.tooltipManager = tooltipManager; - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_WIDGETS); - } - - @Override - public Dimension render(Graphics2D graphics) - { - final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB); - - if (runOrb == null || runOrb.isHidden()) - { - return null; - } - - final Rectangle bounds = runOrb.getBounds(); - - if (bounds.getX() <= 0) - { - return null; - } - - final Point mousePosition = client.getMouseCanvasPosition(); - - if (bounds.contains(mousePosition.getX(), mousePosition.getY())) - { - StringBuilder sb = new StringBuilder(); - sb.append("Weight: ").append(client.getWeight()).append(" kg
"); - - if (config.replaceOrbText()) - { - sb.append("Run Energy: ").append(client.getEnergy()).append("%"); - } - else - { - sb.append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false)); - } - - int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining(); - if (secondsUntil100 > 0) - { - final int minutes = (int) Math.floor(secondsUntil100 / 60.0); - final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0)); - - sb.append("
").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0")); - } - - tooltipManager.add(new Tooltip(sb.toString())); - } - - return null; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java deleted file mode 100644 index 8ef9debda1..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2018, Sean Dewar - * 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.runenergy; - -import com.google.common.collect.ImmutableSet; -import com.google.inject.Provides; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.Constants; -import net.runelite.api.EquipmentInventorySlot; -import net.runelite.api.InventoryID; -import net.runelite.api.Item; -import net.runelite.api.ItemContainer; -import static net.runelite.api.ItemID.AGILITY_CAPE; -import static net.runelite.api.ItemID.AGILITY_CAPET; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_11861; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13589; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13590; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13601; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13602; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13613; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13614; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13625; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13626; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13637; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13638; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13677; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13678; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21076; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21078; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_11853; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13581; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13582; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13593; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13594; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13605; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13606; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13617; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13618; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13629; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13630; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13669; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13670; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_21064; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_21066; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_11859; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13587; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13588; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13599; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13600; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13611; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13612; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13623; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13624; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13635; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13636; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13675; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13676; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21073; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21075; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_11851; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13579; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13580; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13591; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13592; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13603; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13604; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13615; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13616; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13627; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13628; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13667; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13668; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_21061; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_21063; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_11857; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13585; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13586; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13597; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13598; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13609; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13610; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13621; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13622; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13633; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13634; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13673; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13674; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_21070; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_21072; -import static net.runelite.api.ItemID.GRACEFUL_TOP_11855; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13583; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13584; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13595; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13596; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13607; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13608; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13619; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13620; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13631; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13632; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13671; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13672; -import static net.runelite.api.ItemID.GRACEFUL_TOP_21067; -import static net.runelite.api.ItemID.GRACEFUL_TOP_21069; -import static net.runelite.api.ItemID.MAX_CAPE; -import net.runelite.api.Skill; -import net.runelite.api.Varbits; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.overlay.OverlayManager; -import org.apache.commons.lang3.StringUtils; - -@PluginDescriptor( - name = "Run Energy", - description = "Show various information related to run energy", - tags = {"overlay", "stamina"} -) -@Singleton -public class RunEnergyPlugin extends Plugin -{ - // TODO It would be nice if we have the IDs for just the equipped variants of the Graceful set items. - private static final ImmutableSet ALL_GRACEFUL_HOODS = ImmutableSet.of( - GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592, - GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627, - GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063 - ); - - private static final ImmutableSet ALL_GRACEFUL_TOPS = ImmutableSet.of( - GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596, - GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631, - GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069 - ); - - private static final ImmutableSet ALL_GRACEFUL_LEGS = ImmutableSet.of( - GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598, - GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633, - GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072 - ); - - private static final ImmutableSet ALL_GRACEFUL_GLOVES = ImmutableSet.of( - GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600, - GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635, - GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075 - ); - - private static final ImmutableSet ALL_GRACEFUL_BOOTS = ImmutableSet.of( - GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602, - GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637, - GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078 - ); - - // Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect - private static final ImmutableSet ALL_GRACEFUL_CAPES = ImmutableSet.of( - GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594, - GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629, - GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066, - AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE - ); - - @Inject - private Client client; - - @Inject - private OverlayManager overlayManager; - - @Inject - private RunEnergyOverlay energyOverlay; - - @Inject - private RunEnergyConfig energyConfig; - - private boolean localPlayerRunningToDestination; - private WorldPoint prevLocalPlayerLocation; - - @Provides - RunEnergyConfig getConfig(ConfigManager configManager) - { - return configManager.getConfig(RunEnergyConfig.class); - } - - @Override - protected void startUp() throws Exception - { - overlayManager.add(energyOverlay); - } - - @Override - protected void shutDown() throws Exception - { - overlayManager.remove(energyOverlay); - localPlayerRunningToDestination = false; - prevLocalPlayerLocation = null; - resetRunOrbText(); - } - - @Subscribe - public void onGameTick(GameTick event) - { - localPlayerRunningToDestination = - prevLocalPlayerLocation != null && - client.getLocalDestinationLocation() != null && - prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; - - prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation(); - - if (energyConfig.replaceOrbText()) - { - setRunOrbText(getEstimatedRunTimeRemaining(true)); - } - } - - @Subscribe - public void onConfigChanged(ConfigChanged event) - { - if (event.getGroup().equals("runenergy") && !energyConfig.replaceOrbText()) - { - resetRunOrbText(); - } - } - - private void setRunOrbText(String text) - { - Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT); - - if (runOrbText != null) - { - runOrbText.setText(text); - } - } - - private void resetRunOrbText() - { - setRunOrbText(Integer.toString(client.getEnergy())); - } - - String getEstimatedRunTimeRemaining(boolean inSeconds) - { - // Calculate the amount of energy lost every tick. - // Negative weight has the same depletion effect as 0 kg. - final int effectiveWeight = Math.max(client.getWeight(), 0); - double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64; - - if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0) - { - lossRate *= 0.3; // Stamina effect reduces energy depletion to 30% - } - - // Calculate the number of seconds left - final double secondsLeft = (client.getEnergy() * Constants.GAME_TICK_LENGTH) / (lossRate * 1000.0); - - // Return the text - if (inSeconds) - { - return (int) Math.floor(secondsLeft) + "s"; - } - else - { - final int minutes = (int) Math.floor(secondsLeft / 60.0); - final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0)); - - return minutes + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0"); - } - } - - private boolean isLocalPlayerWearingFullGraceful() - { - final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); - - if (equipment == null) - { - return false; - } - - final Item[] items = equipment.getItems(); - - // Check that the local player is wearing enough items to be using full Graceful - // (the Graceful boots will have the highest slot index in the worn set). - if (items == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) - { - return false; - } - - return (ALL_GRACEFUL_HOODS.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) && - ALL_GRACEFUL_TOPS.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) && - ALL_GRACEFUL_LEGS.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) && - ALL_GRACEFUL_GLOVES.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) && - ALL_GRACEFUL_BOOTS.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) && - ALL_GRACEFUL_CAPES.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId())); - } - - int getEstimatedRecoverTimeRemaining() - { - if (localPlayerRunningToDestination) - { - return -1; - } - - // Calculate the amount of energy recovered every second - double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0; - - if (isLocalPlayerWearingFullGraceful()) - { - recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect - } - - // Calculate the number of seconds left - return (int) ((100 - client.getEnergy()) / recoverRate); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java deleted file mode 100644 index ac7d985013..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.inject.Singleton; -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.Experience; -import net.runelite.api.Skill; -import net.runelite.client.game.ItemManager; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; -import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; -import net.runelite.client.plugins.skillcalculator.banked.beans.SecondaryItem; -import net.runelite.client.plugins.skillcalculator.banked.ui.CriticalItemPanel; -import net.runelite.client.plugins.skillcalculator.beans.SkillDataBonus; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.DynamicGridLayout; -import net.runelite.client.ui.FontManager; - -@Slf4j -@Singleton -public class BankedCalculator extends JPanel -{ - private static final DecimalFormat XP_FORMAT_COMMA = new DecimalFormat("#,###.#"); - - private final SkillCalculatorPanel parent; - private final Client client; - private final UICalculatorInputArea uiInput; - private final SkillCalculatorConfig config; - private final ItemManager itemManager; - - private final CacheSkillData skillData = new CacheSkillData(); - private final List bonusCheckBoxes = new ArrayList<>(); - - // UI Input data - private float xpFactor = 1.0f; - private CalculatorType currentCalc; - private Skill currentSkill; - - private double totalBankedXp = 0.0f; - private final JLabel totalLabel = new JLabel(); - private final JPanel detailConfigContainer; - private final JPanel detailContainer; - - // Banked Experience magic - private Map bankMap = new HashMap<>(); - private final Map categoryMap = new HashMap<>(); // Check if CriticalItem Category is enabled - private final Map panelMap = new HashMap<>(); - private final Map criticalMap = new HashMap<>(); // Quantity of CriticalItem inside bankMap - private final Map activityMap = new HashMap<>(); // Selected Activity used for calculating xp - private final Map linkedMap = new HashMap<>(); // ItemID of item that links to the CriticalItem - - BankedCalculator( - final SkillCalculatorPanel parent, - final Client client, - final UICalculatorInputArea uiInput, - final SkillCalculatorConfig config, - final ItemManager itemManager) - { - this.parent = parent; - this.client = client; - this.uiInput = uiInput; - this.config = config; - this.itemManager = itemManager; - - setLayout(new DynamicGridLayout(0, 1, 0, 5)); - - detailContainer = new JPanel(); - detailContainer.setLayout(new BoxLayout(detailContainer, BoxLayout.Y_AXIS)); - - detailConfigContainer = new JPanel(); - detailConfigContainer.setLayout(new BoxLayout(detailConfigContainer, BoxLayout.Y_AXIS)); - } - - private void reset() - { - criticalMap.clear(); - linkedMap.clear(); - xpFactor = 1f; - } - - /** - * Update target Xp and Level inputs to match current Xp + total banked XP - */ - private void syncInputFields() - { - // Update Target XP & Level to include total banked xp - int newTotal = (int) (uiInput.getCurrentXPInput() + totalBankedXp); - uiInput.setTargetXPInput(newTotal); - uiInput.setTargetLevelInput(Experience.getLevelForXp(newTotal)); - } - - /* - * Banked Experience Logic - */ - - /** - * Shows the Banked Xp tab for the CalculatorType - * - * @param calculatorType Selected Calculator Type - */ - void openBanked(CalculatorType calculatorType) - { - // clean slate for creating the required panel - removeAll(); - reset(); - if (calculatorType.getSkill() != currentSkill) - { - // Only clear Category and Activity map on skill change. - activityMap.clear(); - categoryMap.clear(); - } - currentCalc = calculatorType; - currentSkill = calculatorType.getSkill(); - bankMap = parent.getBankMap(); - - uiInput.setCurrentLevelInput(client.getRealSkillLevel(currentSkill)); - uiInput.setCurrentXPInput(client.getSkillExperience(currentSkill)); - - // Only adds Banked Experience portion if enabled for this SkillCalc and have seen their bank - if (!calculatorType.isBankedXpFlag()) - { - add(new JLabel("

Banked Experience is not enabled for this skill.
", JLabel.CENTER)); - } - else if (bankMap.size() <= 0) - { - add(new JLabel("Please visit a bank!", JLabel.CENTER)); - } - else - { - // Prevent editing of the target level/exp since we automagically adjust them - uiInput.getUiFieldTargetLevel().setEditable(false); - uiInput.getUiFieldTargetXP().setEditable(false); - - // Now we can actually show the Banked Experience Panel - // Adds Config Options for this panel - renderBankedXpOptions(); - - renderBonusXpOptions(); - - // sprite 202 - calculatedBankedMaps(); - - // Calculate total banked experience and create detail container - refreshDetailContainer(); - - // Add back all necessary content - add(detailConfigContainer); - add(totalLabel); - add(detailContainer); - } - - revalidate(); - repaint(); - - // Update the input fields. - syncInputFields(); - } - - /** - * Add the config options for toggling each Item Category - */ - private void renderBankedXpOptions() - { - Set categories = CriticalItem.getSkillCategories(currentSkill); - if (categories == null) - { - return; - } - - add(new JLabel("Configs:")); - - for (String category : categories) - { - JPanel uiOption = new JPanel(new BorderLayout()); - JLabel uiLabel = new JLabel(category); - JCheckBox uiCheckbox = new JCheckBox(); - - uiLabel.setForeground(Color.WHITE); - uiLabel.setFont(FontManager.getRunescapeSmallFont()); - - uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); - uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - // Everything is enabled by default - uiCheckbox.setSelected(true); - categoryMap.put(category, true); - - // Adjust Total Banked XP check-state of the box. - uiCheckbox.addActionListener(e -> toggleCategory(category, uiCheckbox.isSelected())); - uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); - - uiOption.add(uiLabel, BorderLayout.WEST); - uiOption.add(uiCheckbox, BorderLayout.EAST); - - add(uiOption); - add(Box.createRigidArea(new Dimension(0, 5))); - } - } - - /** - * Used to toggle Categories of Items inside the Banked Xp tab - * - * @param category Category Name - * @param enabled is enabled - */ - private void toggleCategory(String category, boolean enabled) - { - categoryMap.put(category, enabled); - refreshDetailContainer(); - } - - - /** - * Creates the Maps used for easy access when calculating Banked Xp - */ - private void calculatedBankedMaps() - { - // Grab all CriticalItems for this skill - List items = CriticalItem.getBySkillName(currentSkill); - - // Loop over all Critical Items for this skill and determine how many are in the bank - for (CriticalItem item : items) - { - Integer qty = bankMap.get(item.getItemID()); - if (qty != null && qty > 0) - { - if (criticalMap.containsKey(item)) - { - criticalMap.put(item, criticalMap.get(item) + qty); - } - else - { - criticalMap.put(item, qty); - } - - // Ensure the item this is linked to maps back to us. - if (item.getLinkedItemId() != -1) - { - CriticalItem i = CriticalItem.getByItemId(item.getLinkedItemId()); - if (i != null) - { - linkedMap.put(i, item.getItemID()); - } - } - } - } - } - - /** - * Populates the detailContainer with the necessary CriticalItemPanels - */ - private void refreshDetailContainer() - { - detailContainer.removeAll(); - panelMap.clear(); - - Map map = getBankedXpBreakdown(); - for (Map.Entry entry : map.entrySet()) - { - CriticalItem item = entry.getKey(); - createItemPanel(item); - } - - detailContainer.revalidate(); - detailContainer.repaint(); - - calculateBankedXpTotal(); - } - - /** - * Creates an Individual Item Panel if it should be displayed - * - * @param item CriticalItem this information is tied too - */ - private void createItemPanel(CriticalItem item) - { - // Category Included? - if (categoryMap.get(item.getCategory())) - { - // Get possible activities limited to current level - List activities = Activity.getByCriticalItem(item, uiInput.getCurrentLevelInput()); - - // Check if this should count as another item. - if (item.getLinkedItemId() != -1) - { - // Ensure the linked item panel is created even if there are none in bank. - CriticalItem linked = CriticalItem.getByItemId(item.getLinkedItemId()); - if (!criticalMap.containsKey(linked)) - { - createItemPanel(linked); - } - - // One activity and rewards no xp ignore. - if (activities.size() == 1 && activities.get(0).getXp() <= 0) - { - return; - } - } - - // If it doesn't have any activities ignore it in the breakdown. - if (activities.size() <= 0) - { - return; - } - // Either this item has multiple activities or the single activity rewards xp, create the item panel. - - // Determine xp rate for this item - Activity a = getSelectedActivity(item); - double activityXp = a == null ? 0 : a.getXp(); - double xp = activityXp * (item.isIgnoreBonus() ? 1.0f : xpFactor); - int amount = 0; - - // If it has linked items figure out the working total. - Map links = getLinkedTotalMap(item); - for (Integer num : links.values()) - { - amount += num; - } - - // Actually create the panel displaying banked experience for this item - CriticalItemPanel panel = new CriticalItemPanel(this, itemManager, item, xp, amount, links); - - // Limit to Banked Secondaries - if (config.limitedBankedSecondaries() && a != null) - { - panel.updateAmount(limitToActivitySecondaries(a, amount), true); - panel.recalculate(); - } - panelMap.put(item, panel); - detailContainer.add(panel); - } - - } - - /** - * Return the Activity the player selected for this Item. Defaults to First activity - * - * @param i CriticalItem to check for - * @return selected Activity - */ - public Activity getSelectedActivity(CriticalItem i) - { - // Pull from memory if available - Activity a = activityMap.get(i); - if (a != null) - { - return a; - } - - // If not in memory select the first Activity and add to memory - List activities = Activity.getByCriticalItem(i); - if (activities.size() == 0) - { - // If you can't find an activity it means this item must link to one and give 0 xp - return null; - } - - Activity selected = activities.get(0); - activityMap.put(i, selected); - return selected; - } - - /** - * Creates a Map of Item ID and QTY for this Skill by Category. Keeps order for better UI display - * - * @return Map of Item ID and QTY for this Skill by Category - */ - private Map getBankedXpBreakdown() - { - Map map = new LinkedHashMap<>(); - - for (String category : CriticalItem.getSkillCategories(currentSkill)) - { - List items = CriticalItem.getItemsForSkillCategories(currentSkill, category); - for (CriticalItem item : items) - { - Integer amount = bankMap.get(item.getItemID()); - if (amount != null && amount > 0) - { - map.put(item, amount); - } - } - } - - return map; - } - - /** - * Used to select an Activity for an item - * - * @param i CriticalItem - * @param a Activity selected - */ - public void activitySelected(CriticalItem i, Activity a) - { - // This is triggered on every click so don't update if activity didn't actually change - Activity cur = activityMap.get(i); - if (cur != null && cur.equals(a)) - { - return; - } - - // Update selected activity in map - activityMap.put(i, a); - - // If had a previous selection and this item links to another check for item prevention change. - // If there are changes adjust the Linked panel quantity as well - if (cur != null && i.getLinkedItemId() != -1 && cur.isPreventLinked() != a.isPreventLinked()) - { - CriticalItem linked = CriticalItem.getByItemId(i.getLinkedItemId()); - CriticalItemPanel l = panelMap.get(linked); - if (l != null) - { - l.updateLinkedMap(getLinkedTotalMap(linked)); - int amount = config.limitedBankedSecondaries() ? limitToActivitySecondaries(a, l.getAmount()) : l.getAmount(); - l.updateAmount(amount, false); - l.recalculate(); - } - } - - // Total banked experience - CriticalItemPanel p = panelMap.get(i); - if (p != null) - { - p.updateLinkedMap(getLinkedTotalMap(i)); - int amount = config.limitedBankedSecondaries() ? limitToActivitySecondaries(a, p.getAmount()) : p.getAmount(); - p.updateAmount(amount, true); - p.updateXp(a.getXp() * (i.isIgnoreBonus() ? 1.0f : xpFactor)); - } - - // Update total banked xp value based on updated panels - calculateBankedXpTotal(); - } - - private Map getLinkedTotalMap(CriticalItem i) - { - return getLinkedTotalMap(i, true); - } - - /** - * Creates a Map of CriticalItem and Qty for all items that link to the passed CriticalItem - * - * @param i CriticalItem to base Map off of - * @param first Since this is called recursively we want to ensure the original CriticalItem is always added - * @return Map of Linked CriticalItems and their Qty - */ - private Map getLinkedTotalMap(CriticalItem i, boolean first) - { - Map map = new LinkedHashMap<>(); - if (!categoryMap.get(i.getCategory())) - { - return map; - } - - // This item has an activity selected and its preventing linked functionality? - Activity selected = activityMap.get(i); - if (selected != null && selected.isPreventLinked() - // If initial request is for this item - && !first) - { - return map; - } - - // Add self to map - int amount = criticalMap.getOrDefault(i, 0); - if (amount > 0) - { - map.put(i, amount); - } - - // This item doesn't link to anything, all done. - if (linkedMap.get(i) == null) - { - return map; - } - - CriticalItem item = CriticalItem.getByItemId(linkedMap.get(i)); - if (item == null) - { - log.warn("Error finding Critical Item for Item ID: {}", linkedMap.get(i)); - return map; - } - - map.putAll(getLinkedTotalMap(item, false)); - - return map; - } - - /** - * SkillCalculatorPlugin sends the Bank Map when the bank contents change - * - * @param map Map of Item IDs and Quantity - */ - void updateBankMap(Map map) - { - boolean oldMapFlag = (bankMap.size() <= 0); - bankMap = map; - // Refresh entire panel if old map was empty - if (oldMapFlag) - { - CalculatorType calc = CalculatorType.getBySkill(currentSkill); - SwingUtilities.invokeLater(() -> - { - if (calc != null) - { - openBanked(calc); - } - }); - return; - } - - // recalculate all data related to banked experience except for activity selections - criticalMap.clear(); - linkedMap.clear(); - calculatedBankedMaps(); - - // Update the Total XP banked and the details panel - SwingUtilities.invokeLater(this::refreshDetailContainer); - } - - /** - * Loops over all ItemPanels too sum their total xp and updates the label with the new value - */ - private void calculateBankedXpTotal() - { - double total = 0.0; - for (CriticalItemPanel p : panelMap.values()) - { - total += p.getTotal(); - } - - totalBankedXp = total; - - syncBankedXp(); - } - - /** - * Used to update the UI to reflect the new Banked XP amount - */ - private void syncBankedXp() - { - totalLabel.setText("Total Banked xp: " + XP_FORMAT_COMMA.format(totalBankedXp)); - - syncInputFields(); - - revalidate(); - repaint(); - } - - /** - * Check Bank for Activity Secondaries and Limits to possible Activity amounts - * - * @param a Selected Activity - * @param possible Amount of Critical Item available - * @return possible Limited to Banked Secondaries - */ - private int limitToActivitySecondaries(Activity a, int possible) - { - for (SecondaryItem i : a.getSecondaries()) - { - int banked = bankMap.getOrDefault(i.getId(), 0); - int newPossible = banked / i.getQty(); - possible = newPossible < possible ? newPossible : possible; - } - - return possible; - } - - /** - * Renders the Xp Modifier options - */ - - private void renderBonusXpOptions() - { - SkillDataBonus[] bonuses = skillData.getSkillData(currentCalc.getDataFile()).getBonuses(); - if (bonuses != null) - { - add(new JLabel("Bonus Experience:")); - for (SkillDataBonus bonus : bonuses) - { - JPanel checkboxPanel = buildCheckboxPanel(bonus); - - add(checkboxPanel); - add(Box.createRigidArea(new Dimension(0, 5))); - } - } - } - - private JPanel buildCheckboxPanel(SkillDataBonus bonus) - { - JPanel uiOption = new JPanel(new BorderLayout()); - JLabel uiLabel = new JLabel(bonus.getName()); - JCheckBox uiCheckbox = new JCheckBox(); - - uiLabel.setForeground(Color.WHITE); - uiLabel.setFont(FontManager.getRunescapeSmallFont()); - - uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); - uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - // Adjust XP bonus depending on check-state of the boxes. - uiCheckbox.addActionListener(event -> adjustCheckboxes(uiCheckbox, bonus)); - - uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); - - uiOption.add(uiLabel, BorderLayout.WEST); - uiOption.add(uiCheckbox, BorderLayout.EAST); - bonusCheckBoxes.add(uiCheckbox); - - return uiOption; - } - - private void adjustCheckboxes(JCheckBox target, SkillDataBonus bonus) - { - adjustXPBonus(0); - bonusCheckBoxes.forEach(otherSelectedCheckbox -> - { - if (otherSelectedCheckbox != target) - { - otherSelectedCheckbox.setSelected(false); - } - }); - - if (target.isSelected()) - { - adjustXPBonus(bonus.getValue()); - } - } - - private void adjustXPBonus(float value) - { - xpFactor = 1f + value; - refreshDetailContainer(); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java index a427b602ac..f4f54e0f52 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java @@ -35,7 +35,7 @@ class CacheSkillData { private final Map cache = new HashMap<>(); - SkillData getSkillData(final String dataFile) + SkillData getSkillData(String dataFile) { if (cache.containsKey(dataFile)) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java index 8874563113..e102988f50 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2018, Kruithne - * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,47 +24,32 @@ */ package net.runelite.client.plugins.skillcalculator; -import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.Skill; @AllArgsConstructor -@Getter(AccessLevel.PACKAGE) -public enum CalculatorType +@Getter +enum CalculatorType { - AGILITY(Skill.AGILITY, "skill_agility.json", false), - CONSTRUCTION(Skill.CONSTRUCTION, "skill_construction.json", true), - COOKING(Skill.COOKING, "skill_cooking.json", true), - CRAFTING(Skill.CRAFTING, "skill_crafting.json", true), - FARMING(Skill.FARMING, "skill_farming.json", true), - FIREMAKING(Skill.FIREMAKING, "skill_firemaking.json", false), - FLETCHING(Skill.FLETCHING, "skill_fletching.json", false), - FISHING(Skill.FISHING, "skill_fishing.json", false), - HERBLORE(Skill.HERBLORE, "skill_herblore.json", true), - HUNTER(Skill.HUNTER, "skill_hunter.json", false), - MAGIC(Skill.MAGIC, "skill_magic.json", false), - MINING(Skill.MINING, "skill_mining.json", false), - PRAYER(Skill.PRAYER, "skill_prayer.json", true), - RUNECRAFT(Skill.RUNECRAFT, "skill_runecraft.json", false), - SMITHING(Skill.SMITHING, "skill_smithing.json", true), - THIEVING(Skill.THIEVING, "skill_thieving.json", false), - WOODCUTTING(Skill.WOODCUTTING, "skill_woodcutting.json", false); + MINING(Skill.MINING, "skill_mining.json"), + AGILITY(Skill.AGILITY, "skill_agility.json"), + SMITHING(Skill.SMITHING, "skill_smithing.json"), + HERBLORE(Skill.HERBLORE, "skill_herblore.json"), + FISHING(Skill.FISHING, "skill_fishing.json"), + THIEVING(Skill.THIEVING, "skill_thieving.json"), + COOKING(Skill.COOKING, "skill_cooking.json"), + PRAYER(Skill.PRAYER, "skill_prayer.json"), + CRAFTING(Skill.CRAFTING, "skill_crafting.json"), + FIREMAKING(Skill.FIREMAKING, "skill_firemaking.json"), + MAGIC(Skill.MAGIC, "skill_magic.json"), + FLETCHING(Skill.FLETCHING, "skill_fletching.json"), + WOODCUTTING(Skill.WOODCUTTING, "skill_woodcutting.json"), + RUNECRAFT(Skill.RUNECRAFT, "skill_runecraft.json"), + FARMING(Skill.FARMING, "skill_farming.json"), + CONSTRUCTION(Skill.CONSTRUCTION, "skill_construction.json"), + HUNTER(Skill.HUNTER, "skill_hunter.json"); private final Skill skill; private final String dataFile; - private final boolean bankedXpFlag; - - public static CalculatorType getBySkill(Skill skill) - { - for (CalculatorType c : values()) - { - if (c.getSkill().equals(skill)) - { - return c; - } - } - - return null; - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index e0b614a95a..4434aa92fd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -34,18 +34,13 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; -import lombok.AccessLevel; -import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.Experience; -import net.runelite.api.Skill; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.skillcalculator.beans.SkillData; @@ -59,7 +54,6 @@ import net.runelite.client.ui.components.IconTextField; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; -@Singleton class SkillCalculator extends JPanel { private static final int MAX_XP = 200_000_000; @@ -71,9 +65,7 @@ class SkillCalculator extends JPanel private final ItemManager itemManager; private final List uiActionSlots = new ArrayList<>(); private final CacheSkillData cacheSkillData = new CacheSkillData(); - @Getter(AccessLevel.PACKAGE) private final UICombinedActionSlot combinedActionSlot; - @Getter(AccessLevel.PACKAGE) private final List combinedActionSlots = new ArrayList<>(); private final List bonusCheckBoxes = new ArrayList<>(); private final IconTextField searchBar = new IconTextField(); @@ -85,9 +77,8 @@ class SkillCalculator extends JPanel private int targetXP = Experience.getXpForLevel(targetLevel); private float xpFactor = 1.0f; private float lastBonus = 0.0f; - private CalculatorType calculatorType; - SkillCalculator(final Client client, final UICalculatorInputArea uiInput, final SpriteManager spriteManager, final ItemManager itemManager) + SkillCalculator(Client client, UICalculatorInputArea uiInput, SpriteManager spriteManager, ItemManager itemManager) { this.client = client; this.uiInput = uiInput; @@ -123,8 +114,6 @@ class SkillCalculator extends JPanel void openCalculator(CalculatorType calculatorType) { - this.calculatorType = calculatorType; - // Load the skill data. skillData = cacheSkillData.getSkillData(calculatorType.getDataFile()); @@ -132,11 +121,10 @@ class SkillCalculator extends JPanel xpFactor = 1.0f; // Update internal skill/XP values. - updateInternalValues(); - - // BankedCalculator prevents these from being editable so just ensure they are editable. - uiInput.getUiFieldTargetLevel().setEditable(true); - uiInput.getUiFieldTargetXP().setEditable(true); + currentXP = client.getSkillExperience(calculatorType.getSkill()); + currentLevel = Experience.getLevelForXp(currentXP); + targetLevel = enforceSkillBounds(currentLevel + 1); + targetXP = Experience.getXpForLevel(targetLevel); // Remove all components (action slots) from this panel. removeAll(); @@ -144,9 +132,6 @@ class SkillCalculator extends JPanel // Clear the search bar searchBar.setText(null); - // Clear the search bar - searchBar.setText(null); - // Add in checkboxes for available skill bonuses. renderBonusOptions(); @@ -163,24 +148,6 @@ class SkillCalculator extends JPanel updateInputFields(); } - private void updateInternalValues() - { - updateCurrentValues(); - updateTargetValues(); - } - - private void updateCurrentValues() - { - currentXP = client.getSkillExperience(calculatorType.getSkill()); - currentLevel = Experience.getLevelForXp(currentXP); - } - - private void updateTargetValues() - { - targetLevel = enforceSkillBounds(currentLevel + 1); - targetXP = Experience.getXpForLevel(targetLevel); - } - private void updateCombinedAction() { int size = combinedActionSlots.size(); @@ -254,7 +221,7 @@ class SkillCalculator extends JPanel JCheckBox uiCheckbox = new JCheckBox(); uiLabel.setForeground(Color.WHITE); - uiLabel.setFont(FontManager.getSmallFont(getFont())); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -270,7 +237,7 @@ class SkillCalculator extends JPanel for (JCheckBox checkBox : uiCheckBoxList) { - if (checkBox != null && !checkBox.equals(uiCheckBox)) + if (checkBox != uiCheckBox) { checkBox.setSelected(false); } @@ -466,25 +433,4 @@ class SkillCalculator extends JPanel return slot.getAction().getName().toLowerCase().contains(text.toLowerCase()); } - /** - * Updates the current skill calculator (if present) - *

- * This method is invoked by the {@link SkillCalculatorPlugin} event subscriber - * when an {@link ExperienceChanged} object is posted to the event bus - */ - void updateSkillCalculator(Skill skill) - { - // If the user has selected a calculator, update its fields - Optional.ofNullable(calculatorType).ifPresent(calc -> - { - if (skill.equals(calculatorType.getSkill())) - { - // Update our model "current" values - updateCurrentValues(); - - // Update the UI to reflect our new model - updateInputFields(); - } - }); - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java index 9fd11ba13f..825c620459 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java @@ -32,24 +32,24 @@ import net.runelite.client.config.ConfigItem; public interface SkillCalculatorConfig extends Config { @ConfigItem( - keyName = "showBankedXp", - name = "Show Banked xp Tab", - description = "Shows the Banked xp tab inside the Calculator Panel", + keyName = "enabledBankedXp", + name = "Add Banked XP Panel", + description = "Adds the Banked XP Panel to the side bar", position = 0 ) default boolean showBankedXp() { - return true; + return false; } @ConfigItem( - keyName = "limitedBankedSecondaries", - name = "Limit Banked xp to Secondaries", - description = "Limits the Banked xp shown based on secondaries banked as well", + keyName = "cascadeBankedXp", + name = "Include output items", + description = "Includes output items in the item quantity calculations", position = 1 ) - default boolean limitedBankedSecondaries() + default boolean cascadeBankedXp() { - return false; + return true; } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java index 79684db069..b6abe4577f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java @@ -1,7 +1,6 @@ /* * Copyright (c) 2018, Kruithne * Copyright (c) 2018, Psikoi - * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,20 +29,10 @@ package net.runelite.client.plugins.skillcalculator; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import javax.inject.Singleton; import javax.swing.ImageIcon; import javax.swing.JScrollPane; -import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.api.Skill; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SkillIconManager; import net.runelite.client.game.SpriteManager; @@ -52,43 +41,30 @@ import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.materialtabs.MaterialTab; import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; -@Slf4j -@Singleton class SkillCalculatorPanel extends PluginPanel { private final SkillCalculator uiCalculator; private final SkillIconManager iconManager; - private final SkillCalculatorConfig config; - private final BankedCalculator bankedCalculator; - - private CalculatorType currentCalc; - private final MaterialTabGroup skillGroup; private final MaterialTabGroup tabGroup; - private String currentTab; - private final List tabs = new ArrayList<>(); - @Getter - private Map bankMap = new HashMap<>(); - private final GridBagConstraints c; - SkillCalculatorPanel(final SkillIconManager iconManager, final Client client, final SkillCalculatorConfig config, final SpriteManager spriteManager, final ItemManager itemManager) + SkillCalculatorPanel(SkillIconManager iconManager, Client client, SpriteManager spriteManager, ItemManager itemManager) { super(); getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); this.iconManager = iconManager; - this.config = config; setBorder(new EmptyBorder(10, 10, 10, 10)); setLayout(new GridBagLayout()); - c = new GridBagConstraints(); + GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.gridx = 0; c.gridy = 0; - skillGroup = new MaterialTabGroup(); - skillGroup.setLayout(new GridLayout(0, 6, 7, 7)); + tabGroup = new MaterialTabGroup(); + tabGroup.setLayout(new GridLayout(0, 6, 7, 7)); addCalculatorButtons(); @@ -97,23 +73,14 @@ class SkillCalculatorPanel extends PluginPanel uiInput.setBackground(ColorScheme.DARK_GRAY_COLOR); uiCalculator = new SkillCalculator(client, uiInput, spriteManager, itemManager); - bankedCalculator = new BankedCalculator(this, client, uiInput, config, itemManager); - - tabGroup = new MaterialTabGroup(); - tabGroup.setBorder(new EmptyBorder(0, 0, 10, 0)); - - addTabButtons(); - - add(skillGroup, c); + add(tabGroup, c); c.gridy++; add(uiInput, c); c.gridy++; - add(tabGroup, c); - c.gridy++; - add(uiCalculator, c); + c.gridy++; } private void addCalculatorButtons() @@ -121,126 +88,14 @@ class SkillCalculatorPanel extends PluginPanel for (CalculatorType calculatorType : CalculatorType.values()) { ImageIcon icon = new ImageIcon(iconManager.getSkillImage(calculatorType.getSkill(), true)); - MaterialTab tab = new MaterialTab(icon, skillGroup, null); + MaterialTab tab = new MaterialTab(icon, tabGroup, null); tab.setOnSelectEvent(() -> { - if (currentCalc != null && currentCalc.equals(calculatorType)) - { - return true; - } - currentCalc = calculatorType; - selectedTab(currentTab, true); + uiCalculator.openCalculator(calculatorType); return true; }); - skillGroup.addTab(tab); + tabGroup.addTab(tab); } } - - private void addTabButtons() - { - tabGroup.removeAll(); - tabs.clear(); - - tabs.add("Calculator"); - if (config.showBankedXp()) - { - tabs.add("Banked Xp"); - } - // Only show if both options are visible - tabGroup.setVisible(tabs.size() > 1); - - tabGroup.setLayout(new GridLayout(0, tabs.size(), 7, 7)); - - for (String s : tabs) - { - MaterialTab matTab = new MaterialTab(s, tabGroup, null); - - matTab.setHorizontalAlignment(SwingUtilities.CENTER); - - // Ensure Background is applied - matTab.setOpaque(true); - matTab.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - // When Clicked - matTab.setOnSelectEvent(() -> - { - selectedTab(s, false); - return true; - }); - - tabGroup.addTab(matTab); - } - - MaterialTab selected = tabGroup.getTab(0); - if (tabs.contains(currentTab)) - { - selected = tabGroup.getTab(tabs.indexOf(currentTab)); - } - - tabGroup.select(selected); - currentTab = selected.getText(); - } - - private void selectedTab(String s, boolean force) - { - // Do not refresh the panel if they clicked the same tab, unless they selected a new skill - if (Objects.equals(currentTab, s) && !force) - { - return; - } - - currentTab = s; - - // Only open a panel if a skill is selected - if (currentCalc == null) - { - return; - } - - switch (s) - { - case "Calculator": - remove(bankedCalculator); - add(uiCalculator, c); - uiCalculator.openCalculator(currentCalc); - break; - case "Banked Xp": - remove(uiCalculator); - add(bankedCalculator, c); - bankedCalculator.openBanked(currentCalc); - break; - } - - this.revalidate(); - this.repaint(); - } - - // Refresh entire panel - void refreshPanel() - { - // Recreate Tabs (in case of Config change) and selects the first tab - addTabButtons(); - - // Ensure reload - selectedTab(currentTab, true); - - this.revalidate(); - this.repaint(); - } - - // Wrapper function for updating SkillCalculator's bankMap - void updateBankMap(Map bank) - { - bankMap = bank; - if (currentCalc != null & currentTab.equals("Banked Xp")) - { - bankedCalculator.updateBankMap(bankMap); - } - } - - void updateSkillCalculator(Skill skill) - { - uiCalculator.updateSkillCalculator(skill); - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java index e6a5316cf3..147420e7b1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, Kruithne - * Copyright (c) 2018, TheStonedTurtle + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,18 +31,13 @@ import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; -import javax.inject.Singleton; import javax.swing.SwingUtilities; -import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.ItemContainer; import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.ExperienceChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; @@ -51,7 +46,9 @@ import net.runelite.client.game.SkillIconManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculatorPanel; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.CriticalItem; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.util.ImageUtil; @@ -61,7 +58,6 @@ import net.runelite.client.util.ImageUtil; description = "Enable the Skill Calculator panel", tags = {"panel", "skilling"} ) -@Singleton public class SkillCalculatorPlugin extends Plugin { @Inject @@ -86,12 +82,10 @@ public class SkillCalculatorPlugin extends Plugin private SkillCalculatorConfig skillCalculatorConfig; private NavigationButton uiNavigationButton; - private SkillCalculatorPanel uiPanel; + private NavigationButton bankedUiNavigationButton; - @Getter - private Map bankMap = new HashMap<>(); - - private int bankHash; + private BankedCalculatorPanel bankedUiPanel; + private int bankHash = -1; @Provides SkillCalculatorConfig getConfig(ConfigManager configManager) @@ -103,7 +97,7 @@ public class SkillCalculatorPlugin extends Plugin protected void startUp() throws Exception { final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "calc.png"); - this.uiPanel = new SkillCalculatorPanel(skillIconManager, client, skillCalculatorConfig, spriteManager, itemManager); + final SkillCalculatorPanel uiPanel = new SkillCalculatorPanel(skillIconManager, client, spriteManager, itemManager); uiNavigationButton = NavigationButton.builder() .tooltip("Skill Calculator") @@ -114,56 +108,32 @@ public class SkillCalculatorPlugin extends Plugin clientToolbar.addNavigation(uiNavigationButton); - clientThread.invokeLater(() -> - { - switch (client.getGameState()) - { - case STARTING: - case UNKNOWN: - return false; - } - - CriticalItem.prepareItemDefinitions(itemManager); - return true; - }); + toggleBankedXpPanel(); } @Override protected void shutDown() throws Exception { clientToolbar.removeNavigation(uiNavigationButton); - bankMap.clear(); - bankHash = -1; + if (bankedUiNavigationButton != null) + { + clientToolbar.removeNavigation(bankedUiNavigationButton); + } } @Subscribe public void onConfigChanged(ConfigChanged event) { - if (event.getGroup().equals("skillCalculator")) + if (event.getGroup().equals("skillCalculator") && event.getKey().equals("enabledBankedXp")) { - if (event.getKey().equals("showBankedXp")) - { - bankMap.clear(); - bankHash = -1; - } - - SwingUtilities.invokeLater(() -> uiPanel.refreshPanel()); + toggleBankedXpPanel(); } } - // Pulled from bankvalue plugin to check if bank is open @Subscribe - public void onGameTick(GameTick event) + public void onScriptCallbackEvent(ScriptCallbackEvent event) { - if (!skillCalculatorConfig.showBankedXp()) - { - return; - } - - Widget widgetBankTitleBar = client.getWidget(WidgetInfo.BANK_TITLE_BAR); - - // Don't update on a search because rs seems to constantly update the title - if (widgetBankTitleBar == null || widgetBankTitleBar.isHidden() || widgetBankTitleBar.getText().contains("Showing")) + if (!event.getEventName().equals("setBankTitle") || !skillCalculatorConfig.showBankedXp()) { return; } @@ -171,54 +141,80 @@ public class SkillCalculatorPlugin extends Plugin updateBankItems(); } + private void toggleBankedXpPanel() + { + if (skillCalculatorConfig.showBankedXp()) + { + final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "banked.png"); + + bankedUiPanel = new BankedCalculatorPanel(client, skillCalculatorConfig, skillIconManager, itemManager); + bankedUiNavigationButton = NavigationButton.builder() + .tooltip("Banked XP") + .icon(icon) + .priority(6) + .panel(bankedUiPanel) + .build(); + + clientToolbar.addNavigation(bankedUiNavigationButton); + + clientThread.invoke(() -> + { + switch (client.getGameState()) + { + case LOGIN_SCREEN: + case LOGIN_SCREEN_AUTHENTICATOR: + case LOGGING_IN: + case LOADING: + case LOGGED_IN: + case CONNECTION_LOST: + case HOPPING: + CriticalItem.prepareItemDefinitions(itemManager); + Activity.prepareItemDefinitions(itemManager); + return true; + default: + return false; + } + }); + } + else + { + if (bankedUiNavigationButton == null) + { + return; + } + + clientToolbar.removeNavigation(bankedUiNavigationButton); + + bankedUiNavigationButton = null; + } + } + // Check if bank contents changed and if so send to UI private void updateBankItems() { - ItemContainer c = client.getItemContainer(InventoryID.BANK); - Item[] widgetItems = (c == null ? new Item[0] : c.getItems()); + final ItemContainer c = client.getItemContainer(InventoryID.BANK); + if (c == null) + { + return; + } - // Couldn't find any items in bank, do nothing. + final Item[] widgetItems = c.getItems(); if (widgetItems == null || widgetItems.length == 0) { return; } - Map newBankMap = getBankMapIfDiff(widgetItems); - - // Bank didn't change - if (newBankMap.size() == 0) - { - return; - } - - bankMap = newBankMap; - // send updated bank map to ui - uiPanel.updateBankMap(bankMap); - } - - // Recreates the bankMap and checks if the hashCode is different (the map has changed). Sends an empty map if no changes - private Map getBankMapIfDiff(Item[] widgetItems) - { - Map mapCheck = new HashMap<>(); + final Map m = new HashMap<>(); for (Item widgetItem : widgetItems) { - mapCheck.put(widgetItem.getId(), widgetItem.getQuantity()); + m.put(widgetItem.getId(), widgetItem.getQuantity()); } - int curHash = mapCheck.hashCode(); - - if (curHash != bankHash) + final int curHash = m.hashCode(); + if (bankHash != curHash) { bankHash = curHash; - return mapCheck; + SwingUtilities.invokeLater(() -> bankedUiPanel.setBankMap(m)); } - - return new HashMap<>(); - } - - @Subscribe - public void onExperienceChanged(ExperienceChanged changeEvent) - { - uiPanel.updateSkillCalculator(changeEvent.getSkill()); } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java index 819e75ad4b..2051a0ddcd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java @@ -33,7 +33,6 @@ import java.awt.GridLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; @@ -48,7 +47,6 @@ import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -@Singleton class UIActionSlot extends JPanel { private static final Border GREEN_BORDER = new CompoundBorder( @@ -127,7 +125,7 @@ class UIActionSlot extends JPanel uiLabelName.setForeground(Color.WHITE); uiLabelActions = new JShadowedLabel("Unknown"); - uiLabelActions.setFont(FontManager.getSmallFont(getFont())); + uiLabelActions.setFont(FontManager.getRunescapeSmallFont()); uiLabelActions.setForeground(ColorScheme.LIGHT_GRAY_COLOR); uiInfo.add(uiLabelName); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java index 515b3997d0..6584c85bf9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java @@ -28,27 +28,24 @@ package net.runelite.client.plugins.skillcalculator; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; -import javax.inject.Singleton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; -import lombok.AccessLevel; import lombok.Getter; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.FlatTextField; -@Getter(AccessLevel.PACKAGE) -@Singleton -class UICalculatorInputArea extends JPanel +@Getter +public class UICalculatorInputArea extends JPanel { private final JTextField uiFieldCurrentLevel; private final JTextField uiFieldCurrentXP; private final JTextField uiFieldTargetLevel; private final JTextField uiFieldTargetXP; - UICalculatorInputArea() + public UICalculatorInputArea() { setLayout(new GridLayout(2, 2, 7, 7)); uiFieldCurrentLevel = addComponent("Current Level"); @@ -62,7 +59,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldCurrentLevel); } - void setCurrentLevelInput(int value) + public void setCurrentLevelInput(int value) { setInput(uiFieldCurrentLevel, value); } @@ -72,7 +69,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldCurrentXP); } - void setCurrentXPInput(Object value) + public void setCurrentXPInput(Object value) { setInput(uiFieldCurrentXP, value); } @@ -82,7 +79,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldTargetLevel); } - void setTargetLevelInput(Object value) + public void setTargetLevelInput(Object value) { setInput(uiFieldTargetLevel, value); } @@ -92,7 +89,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldTargetXP); } - void setTargetXPInput(Object value) + public void setTargetXPInput(Object value) { setInput(uiFieldTargetXP, value); } @@ -126,7 +123,7 @@ class UICalculatorInputArea extends JPanel uiInput.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); uiInput.setBorder(new EmptyBorder(5, 7, 5, 7)); - uiLabel.setFont(FontManager.getSmallFont(getFont())); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); uiLabel.setBorder(new EmptyBorder(0, 0, 4, 0)); uiLabel.setForeground(Color.WHITE); @@ -137,4 +134,4 @@ class UICalculatorInputArea extends JPanel return uiInput.getTextField(); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java index f84cdecb23..fa1614c91b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java @@ -30,7 +30,6 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; -import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; @@ -40,14 +39,13 @@ import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -@Singleton class UICombinedActionSlot extends JPanel { private static final Dimension ICON_SIZE = new Dimension(32, 32); private final JShadowedLabel uiLabelActions; private final JShadowedLabel uiLabelTitle; - UICombinedActionSlot(final SpriteManager spriteManager) + UICombinedActionSlot(SpriteManager spriteManager) { setLayout(new BorderLayout()); setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -70,7 +68,7 @@ class UICombinedActionSlot extends JPanel uiLabelTitle.setForeground(Color.WHITE); uiLabelActions = new JShadowedLabel("Shift-click to select multiple"); - uiLabelActions.setFont(FontManager.getSmallFont(getFont())); + uiLabelActions.setFont(FontManager.getRunescapeSmallFont()); uiLabelActions.setForeground(ColorScheme.LIGHT_GRAY_COLOR); uiInfo.add(uiLabelTitle); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java new file mode 100644 index 0000000000..821fd23fc4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ItemEvent; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.swing.BorderFactory; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Experience; +import net.runelite.api.Skill; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.SkillCalculatorConfig; +import net.runelite.client.plugins.skillcalculator.UICalculatorInputArea; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.CriticalItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.XpModifiers; +import net.runelite.client.plugins.skillcalculator.banked.components.GridItem; +import net.runelite.client.plugins.skillcalculator.banked.components.ModifyPanel; +import net.runelite.client.plugins.skillcalculator.banked.components.SelectionGrid; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; + +@Slf4j +public class BankedCalculator extends JPanel +{ + public static final DecimalFormat XP_FORMAT_COMMA = new DecimalFormat("#,###.#"); + + private final Client client; + @Getter + private final SkillCalculatorConfig config; + private final UICalculatorInputArea uiInput; + private final ItemManager itemManager; + + // Some activities output a CriticalItem and may need to be included in the calculable qty + // Using multimap for cases where there are multiple items linked directly to one item, use recursion for otherwise + private final Multimap linkedMap = ArrayListMultimap.create(); + + private final Map bankedItemMap = new LinkedHashMap<>(); + private final JLabel totalXpLabel = new JLabel(); + private final ModifyPanel modifyPanel; + private SelectionGrid itemGrid; + + @Setter + private Map bankMap = new HashMap<>(); + + @Getter + private Skill currentSkill; + + @Getter + private int skillLevel, skillExp, endLevel, endExp; + + private final Collection xpModifierButtons = new ArrayList<>(); + @Getter + private float xpFactor = 1.0f; + + BankedCalculator(UICalculatorInputArea uiInput, Client client, SkillCalculatorConfig config, ItemManager itemManager) + { + this.uiInput = uiInput; + this.client = client; + this.config = config; + this.itemManager = itemManager; + + setLayout(new DynamicGridLayout(0, 1, 0, 5)); + + // Panel used to modify banked item values + this.modifyPanel = new ModifyPanel(this, itemManager); + } + + /** + * opens the Banked Calculator for this skill + */ + void open(final Skill newSkill) + { + if (newSkill.equals(currentSkill)) + { + return; + } + + this.currentSkill = newSkill; + removeAll(); + xpFactor = 1.0f; + + if (bankMap.size() <= 0) + { + add(new JLabel("Please visit a bank!", JLabel.CENTER)); + revalidate(); + repaint(); + return; + } + + skillLevel = client.getRealSkillLevel(currentSkill); + skillExp = client.getSkillExperience(currentSkill); + endLevel = skillLevel; + endExp = skillExp; + + uiInput.setCurrentLevelInput(skillLevel); + uiInput.setCurrentXPInput(skillExp); + uiInput.setTargetLevelInput(endLevel); + uiInput.setTargetXPInput(endExp); + + recreateBankedItemMap(); + + final Collection modifiers = XpModifiers.getModifiersBySkill(this.currentSkill); + for (final XpModifiers modifier : modifiers) + { + JPanel uiOption = new JPanel(new BorderLayout()); + JLabel uiLabel = new JLabel(modifier.getName()); + JCheckBox btn = new JCheckBox(); + + uiLabel.setForeground(Color.WHITE); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); + uiLabel.setHorizontalAlignment(SwingConstants.CENTER); + + uiOption.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 0)); + uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + btn.addItemListener((event) -> + { + switch (event.getStateChange()) + { + case ItemEvent.DESELECTED: + xpFactor = 1.0f; + break; + case ItemEvent.SELECTED: + // Deselects all but the current item + final JCheckBox box = (JCheckBox) event.getItem(); + xpModifierButtons.forEach(b -> b.setSelected(b == box)); + + xpFactor = modifier.getModifier(); + break; + default: + return; + + } + + modifierUpdated(); + }); + xpModifierButtons.add(btn); + + uiOption.add(uiLabel, BorderLayout.WEST); + uiOption.add(btn, BorderLayout.EAST); + add(uiOption); + } + + recreateItemGrid(); + + // This should only be null if there are no items in their bank for this skill + if (itemGrid.getSelectedItem() == null) + { + add(new JLabel("Couldn't find any items for this skill.", JLabel.CENTER)); + } + else + { + add(totalXpLabel); + add(modifyPanel); + add(itemGrid); + } + + revalidate(); + repaint(); + } + + private void recreateBankedItemMap() + { + bankedItemMap.clear(); + linkedMap.clear(); + + final Collection items = CriticalItem.getBySkill(currentSkill); + log.debug("Critical Items for the {} Skill: {}", currentSkill.getName(), items); + + for (final CriticalItem item : items) + { + final BankedItem banked = new BankedItem(item, bankMap.getOrDefault(item.getItemID(), 0)); + bankedItemMap.put(item, banked); + + Activity a = item.getSelectedActivity(); + if (a == null) + { + final List activities = Activity.getByCriticalItem(item); + if (activities.size() == 0) + { + continue; + } + + item.setSelectedActivity(activities.get(0)); + a = activities.get(0); + } + + if (a.getLinkedItem() != null) + { + linkedMap.put(a.getLinkedItem(), banked); + } + } + log.debug("Banked Item Map: {}", bankedItemMap); + log.debug("Linked Map: {}", linkedMap); + } + + /** + * Populates the detailContainer with the necessary BankedItemPanels + */ + private void recreateItemGrid() + { + // Selection grid will only display values with > 0 items + itemGrid = new SelectionGrid(this, bankedItemMap.values(), itemManager); + itemGrid.setOnSelectEvent(() -> + { + modifyPanel.setBankedItem(itemGrid.getSelectedItem()); + return true; + }); + + itemGrid.setOnIgnoreEvent(() -> + { + CriticalItem item = itemGrid.getLastIgnoredItem().getItem(); + updateLinkedItems(item.getSelectedActivity()); + calculateBankedXpTotal(); + return true; + }); + + // Select the first item in the list + modifyPanel.setBankedItem(itemGrid.getSelectedItem()); + + calculateBankedXpTotal(); + } + + public double getItemXpRate(final BankedItem bankedItem) + { + return bankedItem.getXpRate() * (bankedItem.getItem().isIgnoreBonus() ? 1.0f : xpFactor); + } + + /** + * Calculates total item quantity accounting for backwards linked items + * + * @param item starting item + * @return item qty including linked items + */ + public int getItemQty(final BankedItem item) + { + int qty = item.getQty(); + + if (!config.cascadeBankedXp()) + { + return qty; + } + + final Map linked = createLinksMap(item); + final int linkedQty = linked.values().stream().mapToInt(Integer::intValue).sum(); + + return qty + linkedQty; + } + + private void calculateBankedXpTotal() + { + double total = 0.0; + for (final GridItem i : itemGrid.getPanelMap().values()) + { + if (i.isIgnored()) + { + continue; + } + + final BankedItem bi = i.getBankedItem(); + total += getItemQty(bi) * getItemXpRate(bi); + } + + endExp = (int) (skillExp + total); + endLevel = Experience.getLevelForXp(endExp); + + totalXpLabel.setText("Total Banked xp: " + XP_FORMAT_COMMA.format(total)); + uiInput.setTargetLevelInput(endLevel); + uiInput.setTargetXPInput(Math.min(Experience.MAX_SKILL_XP, endExp)); + + revalidate(); + repaint(); + } + + /** + * Used to select an Activity for an item + * + * @param i BankedItem item the activity is tied to + * @param a Activity the selected activity + */ + public void activitySelected(final BankedItem i, final Activity a) + { + final CriticalItem item = i.getItem(); + final Activity old = item.getSelectedActivity(); + if (a.equals(old)) + { + return; + } + + item.setSelectedActivity(a); + + // Cascade activity changes if necessary. + if (config.cascadeBankedXp() && (old.getLinkedItem() != a.getLinkedItem())) + { + // Update Linked Map + linkedMap.remove(old.getLinkedItem(), i); + linkedMap.put(a.getLinkedItem(), i); + // Update all items the old activity effects + updateLinkedItems(old); + // Update all the items the new activity effects + updateLinkedItems(a); + } + + modifyPanel.setBankedItem(i); + itemGrid.getPanelMap().get(i).updateToolTip(); + + // recalculate total xp + calculateBankedXpTotal(); + } + + /** + * Updates the item quantities of all forward linked items + * + * @param activity the starting {@link Activity} to start the cascade from + */ + private void updateLinkedItems(final Activity activity) + { + if (activity == null) + { + return; + } + + boolean foundSelected = false; + boolean panelAmountChange = false; + + CriticalItem i = activity.getLinkedItem(); + while (i != null) + { + final BankedItem bi = bankedItemMap.get(i); + if (bi == null) + { + break; + } + + final int qty = getItemQty(bi); + final boolean stackable = bi.getItem().getItemInfo().isStackable() || qty > 1; + final AsyncBufferedImage img = itemManager.getImage(bi.getItem().getItemID(), qty, stackable); + + final GridItem gridItem = itemGrid.getPanelMap().get(bi); + final int oldQty = gridItem.getAmount(); + panelAmountChange = panelAmountChange || ((oldQty == 0 && qty > 0) || (oldQty > 0 && qty == 0)); + gridItem.updateIcon(img, qty); + gridItem.updateToolTip(); + + foundSelected = foundSelected || itemGrid.getSelectedItem().equals(bi); + + final Activity a = bi.getItem().getSelectedActivity(); + if (a == null) + { + break; + } + + i = a.getLinkedItem(); + } + + if (panelAmountChange) + { + itemGrid.refreshGridDisplay(); + } + + if (foundSelected) + { + // Refresh current modify panel if the cascade effects it + modifyPanel.setBankedItem(itemGrid.getSelectedItem()); + } + } + + /** + * Creates a Map of CriticalItem to bank qty for all items that are being linked to this one + * + * @param item starting item + * @return Map of CriticalItem to bank qty + */ + public Map createLinksMap(final BankedItem item) + { + final Map qtyMap = new HashMap<>(); + + final Activity a = item.getItem().getSelectedActivity(); + if (a == null) + { + return qtyMap; + } + + final Collection linkedBank = linkedMap.get(item.getItem()); + if (linkedBank == null || linkedBank.size() == 0) + { + return qtyMap; + } + + for (final BankedItem linked : linkedBank) + { + // Check if the item is ignored in the grid + if (itemGrid != null) + { + final GridItem grid = itemGrid.getPanelMap().get(linked); + if (grid != null && grid.isIgnored()) + { + continue; + } + } + + final int qty = linked.getQty(); + if (qty > 0) + { + qtyMap.put(linked.getItem(), qty); + } + qtyMap.putAll(createLinksMap(linked)); + } + + return qtyMap; + } + + private void modifierUpdated() + { + itemGrid.getPanelMap().values().forEach(GridItem::updateToolTip); + modifyPanel.setBankedItem(modifyPanel.getBankedItem()); + calculateBankedXpTotal(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java new file mode 100644 index 0000000000..f017040e2a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked; + +import com.google.common.collect.ImmutableSet; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ItemEvent; +import java.awt.image.BufferedImage; +import java.util.Map; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Skill; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.plugins.skillcalculator.SkillCalculatorConfig; +import net.runelite.client.plugins.skillcalculator.UICalculatorInputArea; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.ComboBoxIconEntry; +import net.runelite.client.ui.components.ComboBoxListRenderer; + +@Slf4j +public class BankedCalculatorPanel extends PluginPanel +{ + private final static ImmutableSet BANKABLE_SKILLS = ImmutableSet.of( + Skill.CONSTRUCTION, Skill.COOKING, Skill.CRAFTING, Skill.FARMING, Skill.HERBLORE, Skill.PRAYER, Skill.SMITHING + ); + + private final BankedCalculator calculator; + + public BankedCalculatorPanel(Client client, SkillCalculatorConfig config, SkillIconManager skillIconManager, ItemManager itemManager) + { + super(); + + setBorder(new EmptyBorder(10, 10, 10, 10)); + setLayout(new GridBagLayout()); + + final UICalculatorInputArea inputs = new UICalculatorInputArea(); + inputs.setBorder(new EmptyBorder(15, 0, 15, 0)); + inputs.setBackground(ColorScheme.DARK_GRAY_COLOR); + + inputs.getUiFieldTargetXP().setEditable(false); + inputs.getUiFieldTargetLevel().setEditable(false); + + calculator = new BankedCalculator(inputs, client, config, itemManager); + + // Create the Skill dropdown with icons + final JComboBox dropdown = new JComboBox<>(); + + final ComboBoxListRenderer renderer = new ComboBoxListRenderer(); + renderer.setDefaultText("Select a Skill..."); + dropdown.setRenderer(renderer); + + for (final Skill skill : BANKABLE_SKILLS) + { + final BufferedImage img = skillIconManager.getSkillImage(skill, true); + final ComboBoxIconEntry entry = new ComboBoxIconEntry(new ImageIcon(img), skill.getName(), skill); + dropdown.addItem(entry); + } + + // Add click event handler now to prevent above code from triggering it. + dropdown.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + final ComboBoxIconEntry source = (ComboBoxIconEntry) e.getItem(); + if (source.getData() instanceof Skill) + { + final Skill skill = (Skill) source.getData(); + this.calculator.open(skill); + } + } + }); + + dropdown.setSelectedIndex(-1); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 0; + c.gridy = 0; + + add(dropdown, c); + c.gridy++; + add(inputs, c); + c.gridy++; + add(calculator, c); + } + + public void setBankMap(final Map bankMap) + { + calculator.setBankMap(bankMap); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java deleted file mode 100644 index 983526c59a..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator.banked; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import lombok.Getter; -import net.runelite.api.ItemDefinition; -import net.runelite.api.ItemID; -import net.runelite.api.Skill; -import net.runelite.client.game.ItemManager; - -public enum CriticalItem -{ - /** - * Construction Items - */ - // Planks - PLANK(ItemID.PLANK, "Planks", Skill.CONSTRUCTION), - OAK_PLANK(ItemID.OAK_PLANK, "Planks", Skill.CONSTRUCTION), - TEAK_PLANK(ItemID.TEAK_PLANK, "Planks", Skill.CONSTRUCTION), - MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, "Planks", Skill.CONSTRUCTION), - // Logs - LOGS(ItemID.LOGS, "Logs", Skill.CONSTRUCTION, ItemID.PLANK), - OAK_LOGS(ItemID.OAK_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.OAK_PLANK), - TEAK_LOGS(ItemID.TEAK_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.TEAK_PLANK), - MAHOGANY_LOGS(ItemID.MAHOGANY_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.MAHOGANY_PLANK), - - /** - * Herblore Items - */ - // Grimy Herbs - GRIMY_GUAM_LEAF(ItemID.GRIMY_GUAM_LEAF, "Grimy Herbs", Skill.HERBLORE, ItemID.GUAM_LEAF), - GRIMY_MARRENTILL(ItemID.GRIMY_MARRENTILL, "Grimy Herbs", Skill.HERBLORE, ItemID.MARRENTILL), - GRIMY_TARROMIN(ItemID.GRIMY_TARROMIN, "Grimy Herbs", Skill.HERBLORE, ItemID.TARROMIN), - GRIMY_HARRALANDER(ItemID.GRIMY_HARRALANDER, "Grimy Herbs", Skill.HERBLORE, ItemID.HARRALANDER), - GRIMY_RANARR_WEED(ItemID.GRIMY_RANARR_WEED, "Grimy Herbs", Skill.HERBLORE, ItemID.RANARR_WEED), - GRIMY_TOADFLAX(ItemID.GRIMY_TOADFLAX, "Grimy Herbs", Skill.HERBLORE, ItemID.TOADFLAX), - GRIMY_IRIT_LEAF(ItemID.GRIMY_IRIT_LEAF, "Grimy Herbs", Skill.HERBLORE, ItemID.IRIT_LEAF), - GRIMY_AVANTOE(ItemID.GRIMY_AVANTOE, "Grimy Herbs", Skill.HERBLORE, ItemID.AVANTOE), - GRIMY_KWUARM(ItemID.GRIMY_KWUARM, "Grimy Herbs", Skill.HERBLORE, ItemID.KWUARM), - GRIMY_SNAPDRAGON(ItemID.GRIMY_SNAPDRAGON, "Grimy Herbs", Skill.HERBLORE, ItemID.SNAPDRAGON), - GRIMY_CADANTINE(ItemID.GRIMY_CADANTINE, "Grimy Herbs", Skill.HERBLORE, ItemID.CADANTINE), - GRIMY_LANTADYME(ItemID.GRIMY_LANTADYME, "Grimy Herbs", Skill.HERBLORE, ItemID.LANTADYME), - GRIMY_DWARF_WEED(ItemID.GRIMY_DWARF_WEED, "Grimy Herbs", Skill.HERBLORE, ItemID.DWARF_WEED), - GRIMY_TORSTOL(ItemID.GRIMY_TORSTOL, "Grimy Herbs", Skill.HERBLORE, ItemID.TORSTOL), - // Clean Herbs - GUAM_LEAF(ItemID.GUAM_LEAF, "Cleaned Herbs", Skill.HERBLORE, ItemID.GUAM_POTION_UNF), - MARRENTILL(ItemID.MARRENTILL, "Cleaned Herbs", Skill.HERBLORE, ItemID.MARRENTILL_POTION_UNF), - TARROMIN(ItemID.TARROMIN, "Cleaned Herbs", Skill.HERBLORE, ItemID.TARROMIN_POTION_UNF), - HARRALANDER(ItemID.HARRALANDER, "Cleaned Herbs", Skill.HERBLORE, ItemID.HARRALANDER_POTION_UNF), - RANARR_WEED(ItemID.RANARR_WEED, "Cleaned Herbs", Skill.HERBLORE, ItemID.RANARR_POTION_UNF), - TOADFLAX(ItemID.TOADFLAX, "Cleaned Herbs", Skill.HERBLORE, ItemID.TOADFLAX_POTION_UNF), - IRIT_LEAF(ItemID.IRIT_LEAF, "Cleaned Herbs", Skill.HERBLORE, ItemID.IRIT_POTION_UNF), - AVANTOE(ItemID.AVANTOE, "Cleaned Herbs", Skill.HERBLORE, ItemID.AVANTOE_POTION_UNF), - KWUARM(ItemID.KWUARM, "Cleaned Herbs", Skill.HERBLORE, ItemID.KWUARM_POTION_UNF), - SNAPDRAGON(ItemID.SNAPDRAGON, "Cleaned Herbs", Skill.HERBLORE, ItemID.SNAPDRAGON_POTION_UNF), - CADANTINE(ItemID.CADANTINE, "Cleaned Herbs", Skill.HERBLORE, ItemID.CADANTINE_POTION_UNF), - LANTADYME(ItemID.LANTADYME, "Cleaned Herbs", Skill.HERBLORE, ItemID.LANTADYME_POTION_UNF), - DWARF_WEED(ItemID.DWARF_WEED, "Cleaned Herbs", Skill.HERBLORE, ItemID.DWARF_WEED_POTION_UNF), - TORSTOL(ItemID.TORSTOL, "Cleaned Herbs", Skill.HERBLORE, ItemID.TORSTOL_POTION_UNF), - // Unfinished Potions - GUAM_LEAF_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - - /** - * Prayer Items - */ - // Bones - BONES(ItemID.BONES, "Bones", Skill.PRAYER), - WOLF_BONES(ItemID.WOLF_BONES, "Bones", Skill.PRAYER), - BURNT_BONES(ItemID.BURNT_BONES, "Bones", Skill.PRAYER), - MONKEY_BONES(ItemID.MONKEY_BONES, "Bones", Skill.PRAYER), - BAT_BONES(ItemID.BAT_BONES, "Bones", Skill.PRAYER), - JOGRE_BONES(ItemID.JOGRE_BONES, "Bones", Skill.PRAYER), - BIG_BONES(ItemID.BIG_BONES, "Bones", Skill.PRAYER), - ZOGRE_BONES(ItemID.ZOGRE_BONES, "Bones", Skill.PRAYER), - SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Bones", Skill.PRAYER), - BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Bones", Skill.PRAYER), - WYVERN_BONES(ItemID.WYVERN_BONES, "Bones", Skill.PRAYER), - DRAGON_BONES(ItemID.DRAGON_BONES, "Bones", Skill.PRAYER), - FAYRG_BONES(ItemID.FAYRG_BONES, "Bones", Skill.PRAYER), - LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Bones", Skill.PRAYER), - RAURG_BONES(ItemID.RAURG_BONES, "Bones", Skill.PRAYER), - DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Bones", Skill.PRAYER), - OURG_BONES(ItemID.OURG_BONES, "Bones", Skill.PRAYER), - SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Bones", Skill.PRAYER), - // Shade Remains (Pyre Logs) - LOAR_REMAINS(ItemID.LOAR_REMAINS, "Shades", Skill.PRAYER, true), - PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Shades", Skill.PRAYER, true), - RIYL_REMAINS(ItemID.RIYL_REMAINS, "Shades", Skill.PRAYER, true), - ASYN_REMAINS(ItemID.ASYN_REMAINS, "Shades", Skill.PRAYER, true), - FIYR_REMAINS(ItemID.FIYR_REMAINS, "Shades", Skill.PRAYER, true), - // Ensouled Heads - ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled Heads", Skill.PRAYER, true), - - /** - * Cooking Items - */ - RAW_HERRING(ItemID.RAW_HERRING, "Fish", Skill.COOKING), - RAW_MACKEREL(ItemID.RAW_MACKEREL, "Fish", Skill.COOKING), - RAW_TROUT(ItemID.RAW_TROUT, "Fish", Skill.COOKING), - RAW_COD(ItemID.RAW_COD, "Fish", Skill.COOKING), - RAW_PIKE(ItemID.RAW_PIKE, "Fish", Skill.COOKING), - RAW_SALMON(ItemID.RAW_SALMON, "Fish", Skill.COOKING), - RAW_TUNA(ItemID.RAW_TUNA, "Fish", Skill.COOKING), - RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, "Fish", Skill.COOKING), - RAW_LOBSTER(ItemID.RAW_LOBSTER, "Fish", Skill.COOKING), - RAW_BASS(ItemID.RAW_BASS, "Fish", Skill.COOKING), - RAW_SWORDFISH(ItemID.RAW_SWORDFISH, "Fish", Skill.COOKING), - RAW_MONKFISH(ItemID.RAW_MONKFISH, "Fish", Skill.COOKING), - RAW_SHARK(ItemID.RAW_SHARK, "Fish", Skill.COOKING), - RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, "Fish", Skill.COOKING), - RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, "Fish", Skill.COOKING), - RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, "Fish", Skill.COOKING), - RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, "Fish", Skill.COOKING), - - GRAPES(ItemID.GRAPES, "Other", Skill.COOKING), - - /** - * Crafting Items - */ - WOOL(ItemID.WOOL, "Misc", Skill.CRAFTING), - FLAX(ItemID.FLAX, "Misc", Skill.CRAFTING), - MOLTEN_GLASS(ItemID.MOLTEN_GLASS, "Misc", Skill.CRAFTING), - BATTLESTAFF(ItemID.BATTLESTAFF, "Misc", Skill.CRAFTING), - - // D'hide/Dragon Leather - GREEN_DRAGONHIDE(ItemID.GREEN_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.GREEN_DRAGON_LEATHER), - GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - BLUE_DRAGONHIDE(ItemID.BLUE_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.BLUE_DRAGON_LEATHER), - BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - RED_DRAGONHIDE(ItemID.RED_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.RED_DRAGON_LEATHER), - RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - BLACK_DRAGONHIDE(ItemID.BLACK_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.BLACK_DRAGON_LEATHER), - BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - - // Uncut Gems - UNCUT_OPAL(ItemID.UNCUT_OPAL, "Gems", Skill.CRAFTING, ItemID.OPAL), - UNCUT_JADE(ItemID.UNCUT_JADE, "Gems", Skill.CRAFTING, ItemID.JADE), - UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, "Gems", Skill.CRAFTING, ItemID.RED_TOPAZ), - UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, "Gems", Skill.CRAFTING, ItemID.SAPPHIRE), - UNCUT_EMERALD(ItemID.UNCUT_EMERALD, "Gems", Skill.CRAFTING, ItemID.EMERALD), - UNCUT_RUBY(ItemID.UNCUT_RUBY, "Gems", Skill.CRAFTING, ItemID.RUBY), - UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, "Gems", Skill.CRAFTING, ItemID.DIAMOND), - UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, "Gems", Skill.CRAFTING, ItemID.DRAGONSTONE), - UNCUT_ONYX(ItemID.UNCUT_ONYX, "Gems", Skill.CRAFTING, ItemID.ONYX), - UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, "Gems", Skill.CRAFTING, ItemID.ZENYTE), - - // Cut Gems - OPAL(ItemID.OPAL, "Gems", Skill.CRAFTING), - JADE(ItemID.JADE, "Gems", Skill.CRAFTING), - RED_TOPAZ(ItemID.RED_TOPAZ, "Gems", Skill.CRAFTING), - SAPPHIRE(ItemID.SAPPHIRE, "Gems", Skill.CRAFTING), - EMERALD(ItemID.EMERALD, "Gems", Skill.CRAFTING), - RUBY(ItemID.RUBY, "Gems", Skill.CRAFTING), - DIAMOND(ItemID.DIAMOND, "Gems", Skill.CRAFTING), - DRAGONSTONE(ItemID.DRAGONSTONE, "Gems", Skill.CRAFTING), - ONYX(ItemID.ONYX, "Gems", Skill.CRAFTING), - ZENYTE(ItemID.ZENYTE, "Gems", Skill.CRAFTING), - - /** - * Smithing Items - */ - - // Ores - IRON_ORE(ItemID.IRON_ORE, "Ore", Skill.SMITHING), - SILVER_ORE(ItemID.SILVER_ORE, "Ore", Skill.SMITHING), - GOLD_ORE(ItemID.GOLD_ORE, "Ore", Skill.SMITHING), - MITHRIL_ORE(ItemID.MITHRIL_ORE, "Ore", Skill.SMITHING), - ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, "Ore", Skill.SMITHING), - RUNITE_ORE(ItemID.RUNITE_ORE, "Ore", Skill.SMITHING), - - // Bars - BRONZE_BAR(ItemID.BRONZE_BAR, "Bars", Skill.SMITHING), - IRON_BAR(ItemID.IRON_BAR, "Bars", Skill.SMITHING), - STEEL_BAR(ItemID.STEEL_BAR, "Bars", Skill.SMITHING), - MITHRIL_BAR(ItemID.MITHRIL_BAR, "Bars", Skill.SMITHING), - ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Bars", Skill.SMITHING), - RUNITE_BAR(ItemID.RUNITE_BAR, "Bars", Skill.SMITHING), - - /** - * Farming Items - */ - // Seeds - ACORN(ItemID.ACORN, "Seeds", Skill.FARMING), - WILLOW_SEED(ItemID.WILLOW_SEED, "Seeds", Skill.FARMING), - MAPLE_SEED(ItemID.MAPLE_SEED, "Seeds", Skill.FARMING), - YEW_SEED(ItemID.YEW_SEED, "Seeds", Skill.FARMING), - MAGIC_SEED(ItemID.MAGIC_SEED, "Seeds", Skill.FARMING), - APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, "Seeds", Skill.FARMING), - BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, "Seeds", Skill.FARMING), - ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, "Seeds", Skill.FARMING), - CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, "Seeds", Skill.FARMING), - PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, "Seeds", Skill.FARMING), - PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, "Seeds", Skill.FARMING), - PALM_TREE_SEED(ItemID.PALM_TREE_SEED, "Seeds", Skill.FARMING), - CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, "Seeds", Skill.FARMING), - TEAK_SEED(ItemID.TEAK_SEED, "Seeds", Skill.FARMING), - MAHOGANY_SEED(ItemID.MAHOGANY_SEED, "Seeds", Skill.FARMING), - SPIRIT_SEED(ItemID.SPIRIT_SEED, "Seeds", Skill.FARMING), - - // Saplings - OAK_SAPLING(ItemID.OAK_SAPLING, "Saplings", Skill.FARMING, ItemID.ACORN), - WILLOW_SAPLING(ItemID.WILLOW_SAPLING, "Saplings", Skill.FARMING, ItemID.WILLOW_SEED), - MAPLE_SAPLING(ItemID.MAPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.MAPLE_SEED), - YEW_SAPLING(ItemID.YEW_SAPLING, "Saplings", Skill.FARMING, ItemID.YEW_SEED), - MAGIC_SAPLING(ItemID.MAGIC_SAPLING, "Saplings", Skill.FARMING, ItemID.MAGIC_SEED), - APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.APPLE_TREE_SEED), - BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, "Saplings", Skill.FARMING, ItemID.BANANA_TREE_SEED), - ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, "Saplings", Skill.FARMING, ItemID.ORANGE_TREE_SEED), - CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, "Saplings", Skill.FARMING, ItemID.CURRY_TREE_SEED), - PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.PINEAPPLE_SEED), - PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, "Saplings", Skill.FARMING, ItemID.PAPAYA_TREE_SEED), - PALM_TREE_SAPLING(ItemID.PALM_SAPLING, "Saplings", Skill.FARMING, ItemID.PALM_TREE_SEED), - CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, "Saplings", Skill.FARMING, ItemID.CALQUAT_TREE_SEED), - TEAK_SAPLING(ItemID.TEAK_SAPLING, "Saplings", Skill.FARMING, ItemID.TEAK_SEED), - MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, "Saplings", Skill.FARMING, ItemID.MAHOGANY_SEED), - SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, "Saplings", Skill.FARMING, ItemID.SPIRIT_SEED), - ; - - @Getter - private final int itemID; - @Getter - private final String category; - @Getter - private final Skill skill; - - /** - * Should be operated on and then treated like this item or does nothing if null. - * Used mostly for things like herblore where you want Grimy, Clean, and UNF to count for creating potions. - * To do this GRIMY links to CLEAN which links to UNFINISHED which links to null - */ - @Getter - private final int linkedItemId; - - @Getter - private boolean ignoreBonus; - - @Getter - private ItemDefinition definition; - - CriticalItem(int itemID, String category, Skill skill, int linkedItem) - { - this.itemID = itemID; - this.category = category; - this.skill = skill; - this.linkedItemId = linkedItem; - this.definition = null; - this.ignoreBonus = false; - } - - CriticalItem(int itemID, String category, Skill skill) - { - this(itemID, category, skill, -1); - } - - CriticalItem(int itemID, String category, Skill skill, boolean ignoreBonusXp) - { - this(itemID, category, skill, -1); - this.ignoreBonus = ignoreBonusXp; - } - - // Builds a Map to reduce looping frequency - private static Map> buildSkillItemMap() - { - Map> map = new HashMap<>(); - for (CriticalItem item : values()) - { - map.computeIfAbsent(item.getSkill(), e -> new ArrayList<>()).add(item); - } - - return map; - } - - private static final Map> bySkillName = buildSkillItemMap(); - - public static List getBySkillName(Skill skill) - { - return bySkillName.get(skill); - } - - // Builds a Map to reduce looping frequency - private static Map> buildSkillCategoryMap() - { - Map> map = new HashMap<>(); - for (CriticalItem item : values()) - { - map.computeIfAbsent(item.getSkill(), k -> new HashSet<>()).add(item.category); - } - - return map; - } - - private static final Map> bySkillCategory = buildSkillCategoryMap(); - - public static Set getSkillCategories(Skill skill) - { - return bySkillCategory.get(skill); - } - - // Builds a Map to reduce looping frequency - private static Map> buildItemSkillCategoryMap() - { - Map> map = new HashMap<>(); - for (CriticalItem item : values()) - { - String key = item.getCategory() + item.skill.getName(); - map.computeIfAbsent(key, e -> new ArrayList<>()).add(item); - } - - return map; - } - - private static final Map> itemsBySkillCategory = buildItemSkillCategoryMap(); - - public static List getItemsForSkillCategories(Skill skill, String category) - { - return itemsBySkillCategory.get(category + skill.getName()); - } - - // Builds a Map to reduce looping frequency - private static Map buildItemsByIdMap() - { - Map map = new HashMap<>(); - for (CriticalItem item : values()) - { - map.put(item.getItemID(), item); - } - - return map; - } - - private static final Map itemsById = buildItemsByIdMap(); - - public static CriticalItem getByItemId(int id) - { - return itemsById.get(id); - } - - /** - * Attaches the Item Composition to each Critical Item on client initial load - * - * @param m ItemManager - */ - public static void prepareItemDefinitions(ItemManager m) - { - for (CriticalItem i : values()) - { - i.definition = m.getItemDefinition(i.getItemID()); - } - } - - @Override - public String toString() - { - return "CriticalItem=(name=" + this.name() + ",id=" + this.itemID + ",category=" + this.category + ")"; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java index 080dad9cb9..16aee82217 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java @@ -24,17 +24,19 @@ */ package net.runelite.client.plugins.skillcalculator.banked.beans; +import com.google.common.collect.ImmutableMultimap; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collection; import java.util.List; -import java.util.Map; -import lombok.AccessLevel; +import java.util.stream.Collectors; +import javax.annotation.Nullable; import lombok.Getter; +import net.runelite.api.ItemDefinition; import net.runelite.api.ItemID; import net.runelite.api.Skill; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import net.runelite.client.game.ItemManager; -@Getter(AccessLevel.PUBLIC) +@Getter public enum Activity { /** @@ -42,372 +44,679 @@ public enum Activity */ // Creating Potions // Guam - GUAM_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.GUAM_LEAF, ActivitySecondaries.UNFINISHED_POTION), - GUAM_TAR(ItemID.GUAM_TAR, "Guam tar", Skill.HERBLORE, 19, 30, CriticalItem.GUAM_LEAF, ActivitySecondaries.SWAMP_TAR, true), + GUAM_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, + CriticalItem.GUAM_LEAF, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.GUAM_POTION_UNF, 1)), + GUAM_TAR(ItemID.GUAM_TAR, "Guam tar", Skill.HERBLORE, 19, 30, + CriticalItem.GUAM_LEAF, Secondaries.SWAMP_TAR, new ItemStack(ItemID.GUAM_TAR, 15)), - ATTACK_POTION(ItemID.ATTACK_POTION4, "Attack Potion", Skill.HERBLORE, 3, 25, CriticalItem.GUAM_LEAF_POTION_UNF, ActivitySecondaries.ATTACK_POTION), + ATTACK_POTION(ItemID.ATTACK_POTION3, "Attack potion", Skill.HERBLORE, 3, 25, + CriticalItem.GUAM_LEAF_POTION_UNF, Secondaries.ATTACK_POTION, new ItemStack(ItemID.ATTACK_POTION3, 1)), // Marrentil - MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.MARRENTILL, ActivitySecondaries.UNFINISHED_POTION), - MARRENTILL_TAR(ItemID.MARRENTILL_TAR, "Marrentill tar", Skill.HERBLORE, 31, 42.5, CriticalItem.MARRENTILL, ActivitySecondaries.SWAMP_TAR, true), + MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 1, 0, + CriticalItem.MARRENTILL, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.MARRENTILL_POTION_UNF, 1)), + MARRENTILL_TAR(ItemID.MARRENTILL_TAR, "Marrentill tar", Skill.HERBLORE, 31, 42.5, + CriticalItem.MARRENTILL, Secondaries.SWAMP_TAR, new ItemStack(ItemID.MARRENTILL_TAR, 15)), - ANTIPOISON(ItemID.ANTIPOISON4, "Antipoison", Skill.HERBLORE, 5, 37.5, CriticalItem.MARRENTILL_POTION_UNF, ActivitySecondaries.ANTIPOISON), + ANTIPOISON(ItemID.ANTIPOISON3, "Antipoison", Skill.HERBLORE, 5, 37.5, + CriticalItem.MARRENTILL_POTION_UNF, Secondaries.ANTIPOISON, new ItemStack(ItemID.ANTIPOISON3, 1)), // Tarromin - TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.TARROMIN, ActivitySecondaries.UNFINISHED_POTION), - TARROMIN_TAR(ItemID.TARROMIN_TAR, "Tarromin tar", Skill.HERBLORE, 39, 55, CriticalItem.TARROMIN, ActivitySecondaries.SWAMP_TAR, true), + TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 1, 0, + CriticalItem.TARROMIN, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.TARROMIN_POTION_UNF, 1)), + TARROMIN_TAR(ItemID.TARROMIN_TAR, "Tarromin tar", Skill.HERBLORE, 39, 55, + CriticalItem.TARROMIN, Secondaries.SWAMP_TAR, new ItemStack(ItemID.TARROMIN_TAR, 15)), - STRENGTH_POTION(ItemID.STRENGTH_POTION4, "Strength potion", Skill.HERBLORE, 12, 50, CriticalItem.TARROMIN_POTION_UNF, ActivitySecondaries.STRENGTH_POTION), - SERUM_207(ItemID.SERUM_207_4, "Serum 207", Skill.HERBLORE, 15, 50, CriticalItem.TARROMIN_POTION_UNF, ActivitySecondaries.SERUM_207), + STRENGTH_POTION(ItemID.STRENGTH_POTION3, "Strength potion", Skill.HERBLORE, 12, 50, + CriticalItem.TARROMIN_POTION_UNF, Secondaries.STRENGTH_POTION, new ItemStack(ItemID.STRENGTH_POTION3, 1)), + SERUM_207(ItemID.SERUM_207_3, "Serum 207", Skill.HERBLORE, 15, 50, + CriticalItem.TARROMIN_POTION_UNF, Secondaries.SERUM_207, new ItemStack(ItemID.SERUM_207_3, 1)), // Harralander - HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.HARRALANDER, ActivitySecondaries.UNFINISHED_POTION), - HARRALANDER_TAR(ItemID.HARRALANDER_TAR, "Harralander tar", Skill.HERBLORE, 44, 72.5, CriticalItem.HARRALANDER, ActivitySecondaries.SWAMP_TAR, true), + HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 1, 0, + CriticalItem.HARRALANDER, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.HARRALANDER_POTION_UNF, 1)), + HARRALANDER_TAR(ItemID.HARRALANDER_TAR, "Harralander tar", Skill.HERBLORE, 44, 72.5, + CriticalItem.HARRALANDER, Secondaries.SWAMP_TAR, new ItemStack(ItemID.HARRALANDER_TAR, 15)), - COMPOST_POTION(ItemID.COMPOST_POTION4, "Compost potion", Skill.HERBLORE, 21, 60, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.COMPOST_POTION), - RESTORE_POTION(ItemID.RESTORE_POTION4, "Restore potion", Skill.HERBLORE, 22, 62.5, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.RESTORE_POTION), - ENERGY_POTION(ItemID.ENERGY_POTION4, "Energy potion", Skill.HERBLORE, 26, 67.5, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.ENERGY_POTION), - COMBAT_POTION(ItemID.COMBAT_POTION4, "Combat potion", Skill.HERBLORE, 36, 84, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.COMBAT_POTION), + COMPOST_POTION(ItemID.COMPOST_POTION3, "Compost potion", Skill.HERBLORE, 21, 60, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.COMPOST_POTION, new ItemStack(ItemID.COMPOST_POTION3, 1)), + RESTORE_POTION(ItemID.RESTORE_POTION3, "Restore potion", Skill.HERBLORE, 22, 62.5, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.RESTORE_POTION, new ItemStack(ItemID.RESTORE_POTION3, 1)), + ENERGY_POTION(ItemID.ENERGY_POTION3, "Energy potion", Skill.HERBLORE, 26, 67.5, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.ENERGY_POTION, new ItemStack(ItemID.ENERGY_POTION3, 1)), + COMBAT_POTION(ItemID.COMBAT_POTION3, "Combat potion", Skill.HERBLORE, 36, 84, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.COMBAT_POTION, new ItemStack(ItemID.COMBAT_POTION3, 1)), // Ranarr Weed - DEFENCE_POTION(ItemID.DEFENCE_POTION4, "Defence potion", Skill.HERBLORE, 30, 75, CriticalItem.RANARR_POTION_UNF, ActivitySecondaries.DEFENCE_POTION), - PRAYER_POTION(ItemID.PRAYER_POTION4, "Prayer potion", Skill.HERBLORE, 38, 87.5, CriticalItem.RANARR_POTION_UNF, ActivitySecondaries.PRAYER_POTION), + RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 30, 0, + CriticalItem.RANARR_WEED, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.RANARR_POTION_UNF, 1)), + DEFENCE_POTION(ItemID.DEFENCE_POTION3, "Defence potion", Skill.HERBLORE, 30, 75, + CriticalItem.RANARR_POTION_UNF, Secondaries.DEFENCE_POTION, new ItemStack(ItemID.DEFENCE_POTION3, 1)), + PRAYER_POTION(ItemID.PRAYER_POTION3, "Prayer potion", Skill.HERBLORE, 38, 87.5, + CriticalItem.RANARR_POTION_UNF, Secondaries.PRAYER_POTION, new ItemStack(ItemID.PRAYER_POTION3, 1)), // Toadflax - AGILITY_POTION(ItemID.AGILITY_POTION4, "Agility potion", Skill.HERBLORE, 34, 80, CriticalItem.TOADFLAX_POTION_UNF, ActivitySecondaries.AGILITY_POTION), - SARADOMIN_BREW(ItemID.SARADOMIN_BREW4, "Saradomin brew", Skill.HERBLORE, 81, 180, CriticalItem.TOADFLAX_POTION_UNF, ActivitySecondaries.SARADOMIN_BREW), + TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 34, 0, + CriticalItem.TOADFLAX, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.TOADFLAX_POTION_UNF, 1)), + AGILITY_POTION(ItemID.AGILITY_POTION3, "Agility potion", Skill.HERBLORE, 34, 80, + CriticalItem.TOADFLAX_POTION_UNF, Secondaries.AGILITY_POTION, new ItemStack(ItemID.AGILITY_POTION3, 1)), + SARADOMIN_BREW(ItemID.SARADOMIN_BREW3, "Saradomin brew", Skill.HERBLORE, 81, 180, + CriticalItem.TOADFLAX_POTION_UNF, Secondaries.SARADOMIN_BREW, new ItemStack(ItemID.SARADOMIN_BREW3, 1)), // Irit - SUPER_ATTACK(ItemID.SUPER_ATTACK4, "Super attack", Skill.HERBLORE, 45, 100, CriticalItem.IRIT_POTION_UNF, ActivitySecondaries.SUPER_ATTACK), - SUPERANTIPOISON(ItemID.SUPERANTIPOISON4, "Superantipoison", Skill.HERBLORE, 48, 106.3, CriticalItem.IRIT_POTION_UNF, ActivitySecondaries.SUPERANTIPOISON), + IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 45, 0, + CriticalItem.IRIT_LEAF, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.IRIT_POTION_UNF, 1)), + SUPER_ATTACK(ItemID.SUPER_ATTACK3, "Super attack", Skill.HERBLORE, 45, 100, + CriticalItem.IRIT_POTION_UNF, Secondaries.SUPER_ATTACK, new ItemStack(ItemID.SUPER_ATTACK3, 1)), + SUPERANTIPOISON(ItemID.SUPERANTIPOISON3, "Superantipoison", Skill.HERBLORE, 48, 106.3, + CriticalItem.IRIT_POTION_UNF, Secondaries.SUPERANTIPOISON, new ItemStack(ItemID.SUPERANTIPOISON3, 1)), // Avantoe - FISHING_POTION(ItemID.FISHING_POTION4, "Fishing potion", Skill.HERBLORE, 50, 112.5, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.FISHING_POTION), - SUPER_ENERGY_POTION(ItemID.SUPER_ENERGY3_20549, "Super energy potion", Skill.HERBLORE, 52, 117.5, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.SUPER_ENERGY_POTION), - HUNTER_POTION(ItemID.HUNTER_POTION4, "Hunter potion", Skill.HERBLORE, 53, 120, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.HUNTER_POTION), + AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 50, 0, + CriticalItem.AVANTOE, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.AVANTOE_POTION_UNF, 1)), + FISHING_POTION(ItemID.FISHING_POTION3, "Fishing potion", Skill.HERBLORE, 50, 112.5, + CriticalItem.AVANTOE_POTION_UNF, Secondaries.FISHING_POTION, new ItemStack(ItemID.FISHING_POTION3, 1)), + SUPER_ENERGY_POTION(ItemID.SUPER_ENERGY3_20549, "Super energy potion", Skill.HERBLORE, 52, 117.5, + CriticalItem.AVANTOE_POTION_UNF, Secondaries.SUPER_ENERGY_POTION, new ItemStack(ItemID.SUPER_ENERGY3_20549, 1)), + HUNTER_POTION(ItemID.HUNTER_POTION3, "Hunter potion", Skill.HERBLORE, 53, 120, + CriticalItem.AVANTOE_POTION_UNF, Secondaries.HUNTER_POTION, new ItemStack(ItemID.HUNTER_POTION3, 1)), // Kwuarm - SUPER_STRENGTH(ItemID.SUPER_STRENGTH4, "Super strength", Skill.HERBLORE, 55, 125, CriticalItem.KWUARM_POTION_UNF, ActivitySecondaries.SUPER_STRENGTH), + KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 55, 0, + CriticalItem.KWUARM, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.KWUARM_POTION_UNF, 1)), + SUPER_STRENGTH(ItemID.SUPER_STRENGTH3, "Super strength", Skill.HERBLORE, 55, 125, + CriticalItem.KWUARM_POTION_UNF, Secondaries.SUPER_STRENGTH, new ItemStack(ItemID.SUPER_STRENGTH3, 1)), // Snapdragon - SUPER_RESTORE(ItemID.SUPER_RESTORE4, "Super restore", Skill.HERBLORE, 63, 142.5, CriticalItem.SNAPDRAGON_POTION_UNF, ActivitySecondaries.SUPER_RESTORE), - SANFEW_SERUM(ItemID.SANFEW_SERUM4, "Sanfew serum", Skill.HERBLORE, 65, 160, CriticalItem.SNAPDRAGON_POTION_UNF, ActivitySecondaries.SANFEW_SERUM), + SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 63, 0, + CriticalItem.SNAPDRAGON, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.SNAPDRAGON_POTION_UNF, 1)), + SUPER_RESTORE(ItemID.SUPER_RESTORE3, "Super restore", Skill.HERBLORE, 63, 142.5, + CriticalItem.SNAPDRAGON_POTION_UNF, Secondaries.SUPER_RESTORE, new ItemStack(ItemID.SUPER_RESTORE3, 1)), + SANFEW_SERUM(ItemID.SANFEW_SERUM3, "Sanfew serum", Skill.HERBLORE, 65, 160, + CriticalItem.SNAPDRAGON_POTION_UNF, Secondaries.SANFEW_SERUM, new ItemStack(ItemID.SANFEW_SERUM3, 1)), // Cadantine - SUPER_DEFENCE_POTION(ItemID.SUPER_DEFENCE4, "Super defence", Skill.HERBLORE, 66, 150, CriticalItem.CADANTINE_POTION_UNF, ActivitySecondaries.SUPER_DEFENCE_POTION), + CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 66, 0, + CriticalItem.CADANTINE, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.CADANTINE_POTION_UNF, 1)), + SUPER_DEFENCE_POTION(ItemID.SUPER_DEFENCE3, "Super defence", Skill.HERBLORE, 66, 150, + CriticalItem.CADANTINE_POTION_UNF, Secondaries.SUPER_DEFENCE_POTION, new ItemStack(ItemID.SUPER_DEFENCE3, 1)), // Lantadyme - ANTIFIRE_POTION(ItemID.ANTIFIRE_POTION4, "Anti-fire potion", Skill.HERBLORE, 69, 157.5, CriticalItem.LANTADYME_POTION_UNF, ActivitySecondaries.ANTIFIRE_POTION), - MAGIC_POTION(ItemID.MAGIC_POTION4, "Magic potion", Skill.HERBLORE, 76, 172.5, CriticalItem.LANTADYME_POTION_UNF, ActivitySecondaries.MAGIC_POTION), + LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 69, 0, + CriticalItem.LANTADYME, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.LANTADYME_POTION_UNF, 1)), + ANTIFIRE_POTION(ItemID.ANTIFIRE_POTION3, "Anti-fire potion", Skill.HERBLORE, 69, 157.5, + CriticalItem.LANTADYME_POTION_UNF, Secondaries.ANTIFIRE_POTION, new ItemStack(ItemID.ANTIFIRE_POTION3, 1)), + MAGIC_POTION(ItemID.MAGIC_POTION3, "Magic potion", Skill.HERBLORE, 76, 172.5, + CriticalItem.LANTADYME_POTION_UNF, Secondaries.MAGIC_POTION, new ItemStack(ItemID.MAGIC_POTION3, 1)), // Dwarf Weed - RANGING_POTION(ItemID.RANGING_POTION4, "Ranging potion", Skill.HERBLORE, 72, 162.5, CriticalItem.DWARF_WEED_POTION_UNF, ActivitySecondaries.RANGING_POTION), + DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 72, 0, + CriticalItem.DWARF_WEED, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.DWARF_WEED_POTION_UNF, 1)), + RANGING_POTION(ItemID.RANGING_POTION3, "Ranging potion", Skill.HERBLORE, 72, 162.5, + CriticalItem.DWARF_WEED_POTION_UNF, Secondaries.RANGING_POTION, new ItemStack(ItemID.RANGING_POTION3, 1)), // Torstol - TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 78, 0, CriticalItem.TORSTOL, ActivitySecondaries.UNFINISHED_POTION), - SUPER_COMBAT_POTION(ItemID.SUPER_COMBAT_POTION4, "Super combat", Skill.HERBLORE, 90, 150, CriticalItem.TORSTOL, ActivitySecondaries.SUPER_COMBAT_POTION, true), - ANTIVENOM_PLUS(ItemID.ANTIVENOM4_12913, "Anti-venom+", Skill.HERBLORE, 94, 125, CriticalItem.TORSTOL, ActivitySecondaries.ANTIVENOM_PLUS, true), + TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 78, 0, + CriticalItem.TORSTOL, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.TORSTOL_POTION_UNF, 1)), + SUPER_COMBAT_POTION(ItemID.SUPER_COMBAT_POTION4, "Super combat", Skill.HERBLORE, 90, 150, + CriticalItem.TORSTOL, Secondaries.SUPER_COMBAT_POTION, new ItemStack(ItemID.SUPER_COMBAT_POTION4, 1)), + ANTIVENOM_PLUS(ItemID.ANTIVENOM3_12915, "Anti-venom+", Skill.HERBLORE, 94, 125, + CriticalItem.TORSTOL, Secondaries.ANTIVENOM_PLUS, new ItemStack(ItemID.ANTIVENOM3_12915, 1)), - ZAMORAK_BREW(ItemID.ZAMORAK_BREW4, "Zamorak brew", Skill.HERBLORE, 78, 175, CriticalItem.TORSTOL_POTION_UNF, ActivitySecondaries.ZAMORAK_BREW), + ZAMORAK_BREW(ItemID.ZAMORAK_BREW3, "Zamorak brew", Skill.HERBLORE, 78, 175, + CriticalItem.TORSTOL_POTION_UNF, Secondaries.ZAMORAK_BREW, new ItemStack(ItemID.ZAMORAK_BREW3, 1)), // Cleaning Grimy Herbs - CLEAN_GUAM(ItemID.GUAM_LEAF, "Clean guam", Skill.HERBLORE, 3, 2.5, CriticalItem.GRIMY_GUAM_LEAF), - CLEAN_MARRENTILL(ItemID.MARRENTILL, "Clean marrentill", Skill.HERBLORE, 5, 3.8, CriticalItem.GRIMY_MARRENTILL), - CLEAN_TARROMIN(ItemID.TARROMIN, "Clean tarromin", Skill.HERBLORE, 11, 5, CriticalItem.GRIMY_TARROMIN), - CLEAN_HARRALANDER(ItemID.HARRALANDER, "Clean harralander", Skill.HERBLORE, 20, 6.3, CriticalItem.GRIMY_HARRALANDER), - CLEAN_RANARR_WEED(ItemID.RANARR_WEED, "Clean ranarr weed", Skill.HERBLORE, 25, 7.5, CriticalItem.GRIMY_RANARR_WEED), - CLEAN_TOADFLAX(ItemID.TOADFLAX, "Clean toadflax", Skill.HERBLORE, 30, 8, CriticalItem.GRIMY_TOADFLAX), - CLEAN_IRIT_LEAF(ItemID.IRIT_LEAF, "Clean irit leaf", Skill.HERBLORE, 40, 8.8, CriticalItem.GRIMY_IRIT_LEAF), - CLEAN_AVANTOE(ItemID.AVANTOE, "Clean avantoe", Skill.HERBLORE, 48, 10, CriticalItem.GRIMY_AVANTOE), - CLEAN_KWUARM(ItemID.KWUARM, "Clean kwuarm", Skill.HERBLORE, 54, 11.3, CriticalItem.GRIMY_KWUARM), - CLEAN_SNAPDRAGON(ItemID.SNAPDRAGON, "Clean snapdragon", Skill.HERBLORE, 59, 11.8, CriticalItem.GRIMY_SNAPDRAGON), - CLEAN_CADANTINE(ItemID.CADANTINE, "Clean cadantine", Skill.HERBLORE, 65, 12.5, CriticalItem.GRIMY_CADANTINE), - CLEAN_LANTADYME(ItemID.LANTADYME, "Clean lantadyme", Skill.HERBLORE, 67, 13.1, CriticalItem.GRIMY_LANTADYME), - CLEAN_DWARF_WEED(ItemID.DWARF_WEED, "Clean dwarf weed", Skill.HERBLORE, 70, 13.8, CriticalItem.GRIMY_DWARF_WEED), - CLEAN_TORSTOL(ItemID.TORSTOL, "Clean torstol", Skill.HERBLORE, 75, 15, CriticalItem.GRIMY_TORSTOL), + CLEAN_GUAM(ItemID.GUAM_LEAF, "Clean guam", Skill.HERBLORE, 3, 2.5, + CriticalItem.GRIMY_GUAM_LEAF, null, new ItemStack(ItemID.GUAM_LEAF, 1)), + CLEAN_MARRENTILL(ItemID.MARRENTILL, "Clean marrentill", Skill.HERBLORE, 5, 3.8, + CriticalItem.GRIMY_MARRENTILL, null, new ItemStack(ItemID.MARRENTILL, 1)), + CLEAN_TARROMIN(ItemID.TARROMIN, "Clean tarromin", Skill.HERBLORE, 11, 5, + CriticalItem.GRIMY_TARROMIN, null, new ItemStack(ItemID.TARROMIN, 1)), + CLEAN_HARRALANDER(ItemID.HARRALANDER, "Clean harralander", Skill.HERBLORE, 20, 6.3, + CriticalItem.GRIMY_HARRALANDER, null, new ItemStack(ItemID.HARRALANDER, 1)), + CLEAN_RANARR_WEED(ItemID.RANARR_WEED, "Clean ranarr weed", Skill.HERBLORE, 25, 7.5, + CriticalItem.GRIMY_RANARR_WEED, null, new ItemStack(ItemID.RANARR_WEED, 1)), + CLEAN_TOADFLAX(ItemID.TOADFLAX, "Clean toadflax", Skill.HERBLORE, 30, 8, + CriticalItem.GRIMY_TOADFLAX, null, new ItemStack(ItemID.TOADFLAX, 1)), + CLEAN_IRIT_LEAF(ItemID.IRIT_LEAF, "Clean irit leaf", Skill.HERBLORE, 40, 8.8, + CriticalItem.GRIMY_IRIT_LEAF, null, new ItemStack(ItemID.IRIT_LEAF, 1)), + CLEAN_AVANTOE(ItemID.AVANTOE, "Clean avantoe", Skill.HERBLORE, 48, 10, + CriticalItem.GRIMY_AVANTOE, null, new ItemStack(ItemID.AVANTOE, 1)), + CLEAN_KWUARM(ItemID.KWUARM, "Clean kwuarm", Skill.HERBLORE, 54, 11.3, + CriticalItem.GRIMY_KWUARM, null, new ItemStack(ItemID.KWUARM, 1)), + CLEAN_SNAPDRAGON(ItemID.SNAPDRAGON, "Clean snapdragon", Skill.HERBLORE, 59, 11.8, + CriticalItem.GRIMY_SNAPDRAGON, null, new ItemStack(ItemID.SNAPDRAGON, 1)), + CLEAN_CADANTINE(ItemID.CADANTINE, "Clean cadantine", Skill.HERBLORE, 65, 12.5, + CriticalItem.GRIMY_CADANTINE, null, new ItemStack(ItemID.CADANTINE, 1)), + CLEAN_LANTADYME(ItemID.LANTADYME, "Clean lantadyme", Skill.HERBLORE, 67, 13.1, + CriticalItem.GRIMY_LANTADYME, null, new ItemStack(ItemID.LANTADYME, 1)), + CLEAN_DWARF_WEED(ItemID.DWARF_WEED, "Clean dwarf weed", Skill.HERBLORE, 70, 13.8, + CriticalItem.GRIMY_DWARF_WEED, null, new ItemStack(ItemID.DWARF_WEED, 1)), + CLEAN_TORSTOL(ItemID.TORSTOL, "Clean torstol", Skill.HERBLORE, 75, 15, + CriticalItem.GRIMY_TORSTOL, null, new ItemStack(ItemID.TORSTOL, 1)), /** * Construction Options */ - PLANKS(ItemID.PLANK, "Normal Plank Products", Skill.CONSTRUCTION, 1, 29, CriticalItem.PLANK), - OAK_PLANKS(ItemID.OAK_PLANK, "Normal Oak Products", Skill.CONSTRUCTION, 1, 60, CriticalItem.OAK_PLANK), - TEAK_PLANKS(ItemID.TEAK_PLANK, "Normal Teak Products", Skill.CONSTRUCTION, 1, 90, CriticalItem.TEAK_PLANK), - MYTHICAL_CAPE(ItemID.MYTHICAL_CAPE, "Mythical Cape Rakes", Skill.CONSTRUCTION, 1, 123.33, CriticalItem.TEAK_PLANK), - MAHOGANY_PLANKS(ItemID.MAHOGANY_PLANK, "Normal Mahogany Products", Skill.CONSTRUCTION, 1, 140, CriticalItem.MAHOGANY_PLANK), + PLANK(ItemID.PLANK, "Regular Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.LOGS, Secondaries.COINS_100, new ItemStack(ItemID.PLANK, 1)), + PLANKS(ItemID.PLANK, "Regular plank products", Skill.CONSTRUCTION, 1, 29, + CriticalItem.PLANK, null, null), + + OAK_PLANK(ItemID.OAK_PLANK, "Oak Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.OAK_LOGS, Secondaries.COINS_250, new ItemStack(ItemID.OAK_PLANK, 1)), + OAK_PLANKS(ItemID.OAK_PLANK, "Oak products", Skill.CONSTRUCTION, 1, 60, + CriticalItem.OAK_PLANK, null, null), + + TEAK_PLANK(ItemID.TEAK_PLANK, "Teak Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.TEAK_LOGS, Secondaries.COINS_500, new ItemStack(ItemID.TEAK_PLANK, 1)), + TEAK_PLANKS(ItemID.TEAK_PLANK, "Teak products", Skill.CONSTRUCTION, 1, 90, + CriticalItem.TEAK_PLANK, null, null), + MYTHICAL_CAPE(ItemID.MYTHICAL_CAPE, "Mythical cape rakes", Skill.CONSTRUCTION, 1, 123.33, + CriticalItem.TEAK_PLANK, null, null), + + + MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, "Mahogany Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.MAHOGANY_LOGS, Secondaries.COINS_1500, new ItemStack(ItemID.MAHOGANY_PLANK, 1)), + MAHOGANY_PLANKS(ItemID.MAHOGANY_PLANK, "Mahogany products", Skill.CONSTRUCTION, 1, 140, + CriticalItem.MAHOGANY_PLANK, null, null), /** * Prayer Options */ - BONES(ItemID.BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.BONES), - WOLF_BONES(ItemID.WOLF_BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.WOLF_BONES), - BURNT_BONES(ItemID.BURNT_BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.BURNT_BONES), - MONKEY_BONES(ItemID.MONKEY_BONES, "Bones", Skill.PRAYER, 1, 5.0, CriticalItem.MONKEY_BONES), - BAT_BONES(ItemID.BAT_BONES, "Bones", Skill.PRAYER, 1, 5.3, CriticalItem.BAT_BONES), - JOGRE_BONES(ItemID.JOGRE_BONES, "Bones", Skill.PRAYER, 1, 15.0, CriticalItem.JOGRE_BONES), - BIG_BONES(ItemID.BIG_BONES, "Bones", Skill.PRAYER, 1, 15.0, CriticalItem.BIG_BONES), - ZOGRE_BONES(ItemID.ZOGRE_BONES, "Bones", Skill.PRAYER, 1, 22.5, CriticalItem.ZOGRE_BONES), - SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Bones", Skill.PRAYER, 1, 25.0, CriticalItem.SHAIKAHAN_BONES), - BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Bones", Skill.PRAYER, 1, 30.0, CriticalItem.BABYDRAGON_BONES), - WYVERN_BONES(ItemID.WYVERN_BONES, "Bones", Skill.PRAYER, 1, 72.0, CriticalItem.WYVERN_BONES), - DRAGON_BONES(ItemID.DRAGON_BONES, "Bones", Skill.PRAYER, 1, 72.0, CriticalItem.DRAGON_BONES), - FAYRG_BONES(ItemID.FAYRG_BONES, "Bones", Skill.PRAYER, 1, 84.0, CriticalItem.FAYRG_BONES), - LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Bones", Skill.PRAYER, 1, 85.0, CriticalItem.LAVA_DRAGON_BONES), - RAURG_BONES(ItemID.RAURG_BONES, "Bones", Skill.PRAYER, 1, 96.0, CriticalItem.RAURG_BONES), - DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Bones", Skill.PRAYER, 1, 125.0, CriticalItem.DAGANNOTH_BONES), - OURG_BONES(ItemID.OURG_BONES, "Bones", Skill.PRAYER, 1, 140.0, CriticalItem.OURG_BONES), - SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Bones", Skill.PRAYER, 1, 150.0, CriticalItem.SUPERIOR_DRAGON_BONES), + BONES(ItemID.BONES, "Bones", Skill.PRAYER, 1, 4.5, + CriticalItem.BONES, null, null), + WOLF_BONES(ItemID.WOLF_BONES, "Wolf bones", Skill.PRAYER, 1, 4.5, + CriticalItem.WOLF_BONES, null, null), + BURNT_BONES(ItemID.BURNT_BONES, "Burnt bones", Skill.PRAYER, 1, 4.5, + CriticalItem.BURNT_BONES, null, null), + MONKEY_BONES(ItemID.MONKEY_BONES, "Monkey bones", Skill.PRAYER, 1, 5.0, + CriticalItem.MONKEY_BONES, null, null), + BAT_BONES(ItemID.BAT_BONES, "Bat bones", Skill.PRAYER, 1, 5.3, + CriticalItem.BAT_BONES, null, null), + JOGRE_BONES(ItemID.JOGRE_BONES, "Jogre bones", Skill.PRAYER, 1, 15.0, + CriticalItem.JOGRE_BONES, null, null), + BIG_BONES(ItemID.BIG_BONES, "Big bones", Skill.PRAYER, 1, 15.0, + CriticalItem.BIG_BONES, null, null), + ZOGRE_BONES(ItemID.ZOGRE_BONES, "Zogre bones", Skill.PRAYER, 1, 22.5, + CriticalItem.ZOGRE_BONES, null, null), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Shaikahan bones", Skill.PRAYER, 1, 25.0, + CriticalItem.SHAIKAHAN_BONES, null, null), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Babydragon bones", Skill.PRAYER, 1, 30.0, + CriticalItem.BABYDRAGON_BONES, null, null), + WYVERN_BONES(ItemID.WYVERN_BONES, "Wyvern bones", Skill.PRAYER, 1, 72.0, + CriticalItem.WYVERN_BONES, null, null), + DRAGON_BONES(ItemID.DRAGON_BONES, "Dragon bones", Skill.PRAYER, 1, 72.0, + CriticalItem.DRAGON_BONES, null, null), + FAYRG_BONES(ItemID.FAYRG_BONES, "Fayrg bones", Skill.PRAYER, 1, 84.0, + CriticalItem.FAYRG_BONES, null, null), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Lava dragon bones", Skill.PRAYER, 1, 85.0, + CriticalItem.LAVA_DRAGON_BONES, null, null), + RAURG_BONES(ItemID.RAURG_BONES, "Raurg bones", Skill.PRAYER, 1, 96.0, + CriticalItem.RAURG_BONES, null, null), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Dagannoth bones", Skill.PRAYER, 1, 125.0, + CriticalItem.DAGANNOTH_BONES, null, null), + OURG_BONES(ItemID.OURG_BONES, "Ourg bones", Skill.PRAYER, 1, 140.0, + CriticalItem.OURG_BONES, null, null), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Superior dragon bones", Skill.PRAYER, 1, 150.0, + CriticalItem.SUPERIOR_DRAGON_BONES, null, null), // Shade Remains (Pyre Logs) - LOAR_REMAINS(ItemID.LOAR_REMAINS, "Shades", Skill.PRAYER, 1, 33.0, CriticalItem.LOAR_REMAINS), - PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Shades", Skill.PRAYER, 1, 46.5, CriticalItem.PHRIN_REMAINS), - RIYL_REMAINS(ItemID.RIYL_REMAINS, "Shades", Skill.PRAYER, 1, 59.5, CriticalItem.RIYL_REMAINS), - ASYN_REMAINS(ItemID.ASYN_REMAINS, "Shades", Skill.PRAYER, 1, 82.5, CriticalItem.ASYN_REMAINS), - FIYR_REMAINS(ItemID.FIYR_REMAINS, "Shades", Skill.PRAYER, 1, 84.0, CriticalItem.FIYR_REMAINS), + LOAR_REMAINS(ItemID.LOAR_REMAINS, "Loar remains", Skill.PRAYER, 1, 33.0, + CriticalItem.LOAR_REMAINS, null, null), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Phrin remains", Skill.PRAYER, 1, 46.5, + CriticalItem.PHRIN_REMAINS, null, null), + RIYL_REMAINS(ItemID.RIYL_REMAINS, "Riyl remains", Skill.PRAYER, 1, 59.5, + CriticalItem.RIYL_REMAINS, null, null), + ASYN_REMAINS(ItemID.ASYN_REMAINS, "Asyn remains", Skill.PRAYER, 1, 82.5, + CriticalItem.ASYN_REMAINS, null, null), + FIYR_REMAINS(ItemID.FIYR_REMAINS, "Fiyre remains", Skill.PRAYER, 1, 84.0, + CriticalItem.FIYR_REMAINS, null, null), // Ensouled Heads - ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled Heads", Skill.PRAYER, 1, 130.0, CriticalItem.ENSOULED_GOBLIN_HEAD), - ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled Heads", Skill.PRAYER, 1, 182.0, CriticalItem.ENSOULED_MONKEY_HEAD), - ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled Heads", Skill.PRAYER, 1, 286.0, CriticalItem.ENSOULED_IMP_HEAD), - ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled Heads", Skill.PRAYER, 1, 364.0, CriticalItem.ENSOULED_MINOTAUR_HEAD), - ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled Heads", Skill.PRAYER, 1, 454.0, CriticalItem.ENSOULED_SCORPION_HEAD), - ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled Heads", Skill.PRAYER, 1, 480.0, CriticalItem.ENSOULED_BEAR_HEAD), - ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled Heads", Skill.PRAYER, 1, 494.0, CriticalItem.ENSOULED_UNICORN_HEAD), - ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled Heads", Skill.PRAYER, 1, 520.0, CriticalItem.ENSOULED_DOG_HEAD), - ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled Heads", Skill.PRAYER, 1, 584.0, CriticalItem.ENSOULED_CHAOS_DRUID_HEAD), - ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled Heads", Skill.PRAYER, 1, 650.0, CriticalItem.ENSOULED_GIANT_HEAD), - ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled Heads", Skill.PRAYER, 1, 716.0, CriticalItem.ENSOULED_OGRE_HEAD), - ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled Heads", Skill.PRAYER, 1, 754.0, CriticalItem.ENSOULED_ELF_HEAD), - ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled Heads", Skill.PRAYER, 1, 780.0, CriticalItem.ENSOULED_TROLL_HEAD), - ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled Heads", Skill.PRAYER, 1, 832.0, CriticalItem.ENSOULED_HORROR_HEAD), - ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled Heads", Skill.PRAYER, 1, 884.0, CriticalItem.ENSOULED_KALPHITE_HEAD), - ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled Heads", Skill.PRAYER, 1, 936.0, CriticalItem.ENSOULED_DAGANNOTH_HEAD), - ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled Heads", Skill.PRAYER, 1, 1040.0, CriticalItem.ENSOULED_BLOODVELD_HEAD), - ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled Heads", Skill.PRAYER, 1, 1104.0, CriticalItem.ENSOULED_TZHAAR_HEAD), - ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled Heads", Skill.PRAYER, 1, 1170.0, CriticalItem.ENSOULED_DEMON_HEAD), - ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled Heads", Skill.PRAYER, 1, 1234.0, CriticalItem.ENSOULED_AVIANSIE_HEAD), - ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled Heads", Skill.PRAYER, 1, 1300.0, CriticalItem.ENSOULED_ABYSSAL_HEAD), - ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled Heads", Skill.PRAYER, 1, 1560.0, CriticalItem.ENSOULED_DRAGON_HEAD), + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled goblin head", Skill.PRAYER, 1, 130.0, + CriticalItem.ENSOULED_GOBLIN_HEAD, null, null), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled monkey head", Skill.PRAYER, 1, 182.0, + CriticalItem.ENSOULED_MONKEY_HEAD, null, null), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled imp head", Skill.PRAYER, 1, 286.0, + CriticalItem.ENSOULED_IMP_HEAD, null, null), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled minotaur head", Skill.PRAYER, 1, 364.0, + CriticalItem.ENSOULED_MINOTAUR_HEAD, null, null), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled scorpion head", Skill.PRAYER, 1, 454.0, + CriticalItem.ENSOULED_SCORPION_HEAD, null, null), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled bear head", Skill.PRAYER, 1, 480.0, + CriticalItem.ENSOULED_BEAR_HEAD, null, null), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled unicorn head", Skill.PRAYER, 1, 494.0, + CriticalItem.ENSOULED_UNICORN_HEAD, null, null), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled dog head", Skill.PRAYER, 1, 520.0, + CriticalItem.ENSOULED_DOG_HEAD, null, null), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled druid head", Skill.PRAYER, 1, 584.0, + CriticalItem.ENSOULED_CHAOS_DRUID_HEAD, null, null), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled giant head", Skill.PRAYER, 1, 650.0, + CriticalItem.ENSOULED_GIANT_HEAD, null, null), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled ogre head", Skill.PRAYER, 1, 716.0, + CriticalItem.ENSOULED_OGRE_HEAD, null, null), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled elf head", Skill.PRAYER, 1, 754.0, + CriticalItem.ENSOULED_ELF_HEAD, null, null), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled troll head", Skill.PRAYER, 1, 780.0, + CriticalItem.ENSOULED_TROLL_HEAD, null, null), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled horror head", Skill.PRAYER, 1, 832.0, + CriticalItem.ENSOULED_HORROR_HEAD, null, null), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled kalphite head", Skill.PRAYER, 1, 884.0, + CriticalItem.ENSOULED_KALPHITE_HEAD, null, null), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled dagannoth head", Skill.PRAYER, 1, 936.0, + CriticalItem.ENSOULED_DAGANNOTH_HEAD, null, null), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled bloodveld head", Skill.PRAYER, 1, 1040.0, + CriticalItem.ENSOULED_BLOODVELD_HEAD, null, null), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled tzhaar head", Skill.PRAYER, 1, 1104.0, + CriticalItem.ENSOULED_TZHAAR_HEAD, null, null), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled demon head", Skill.PRAYER, 1, 1170.0, + CriticalItem.ENSOULED_DEMON_HEAD, null, null), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled aviansie head", Skill.PRAYER, 1, 1234.0, + CriticalItem.ENSOULED_AVIANSIE_HEAD, null, null), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled abyssal head", Skill.PRAYER, 1, 1300.0, + CriticalItem.ENSOULED_ABYSSAL_HEAD, null, null), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled dragon head", Skill.PRAYER, 1, 1560.0, + CriticalItem.ENSOULED_DRAGON_HEAD, null, null), /* * Cooking Items */ - RAW_HERRING(ItemID.RAW_HERRING, "Fish", Skill.COOKING, 5, 50.0, CriticalItem.RAW_HERRING), - RAW_MACKEREL(ItemID.RAW_MACKEREL, "Fish", Skill.COOKING, 10, 60.0, CriticalItem.RAW_MACKEREL), - RAW_TROUT(ItemID.RAW_TROUT, "Fish", Skill.COOKING, 15, 70.0, CriticalItem.RAW_TROUT), - RAW_COD(ItemID.RAW_COD, "Fish", Skill.COOKING, 18, 75.0, CriticalItem.RAW_COD), - RAW_PIKE(ItemID.RAW_PIKE, "Fish", Skill.COOKING, 20, 80.0, CriticalItem.RAW_PIKE), - RAW_SALMON(ItemID.RAW_SALMON, "Fish", Skill.COOKING, 25, 90.0, CriticalItem.RAW_SALMON), - RAW_TUNA(ItemID.RAW_TUNA, "Fish", Skill.COOKING, 30, 100.0, CriticalItem.RAW_TUNA), - RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, "Fish", Skill.COOKING, 30, 190.0, CriticalItem.RAW_KARAMBWAN), - RAW_LOBSTER(ItemID.RAW_LOBSTER, "Fish", Skill.COOKING, 40, 120.0, CriticalItem.RAW_LOBSTER), - RAW_BASS(ItemID.RAW_BASS, "Fish", Skill.COOKING, 43, 130.0, CriticalItem.RAW_BASS), - RAW_SWORDFISH(ItemID.RAW_SWORDFISH, "Fish", Skill.COOKING, 45, 140.0, CriticalItem.RAW_SWORDFISH), - RAW_MONKFISH(ItemID.RAW_MONKFISH, "Fish", Skill.COOKING, 62, 150.0, CriticalItem.RAW_MONKFISH), - RAW_SHARK(ItemID.RAW_SHARK, "Fish", Skill.COOKING, 80, 210.0, CriticalItem.RAW_SHARK), - RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, "Fish", Skill.COOKING, 82, 211.3, CriticalItem.RAW_SEA_TURTLE), - RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, "Fish", Skill.COOKING, 84, 230.0, CriticalItem.RAW_ANGLERFISH), - RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, "Fish", Skill.COOKING, 90, 215.0, CriticalItem.RAW_DARK_CRAB), - RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, "Fish", Skill.COOKING, 91, 216.2, CriticalItem.RAW_MANTA_RAY), + COOK_HERRING(ItemID.HERRING, "Herring", Skill.COOKING, 5, 50.0, + CriticalItem.RAW_HERRING, null, new ItemStack(ItemID.HERRING, 1)), + COOK_MACKEREL(ItemID.MACKEREL, "Mackerel", Skill.COOKING, 10, 60.0, + CriticalItem.RAW_MACKEREL, null, new ItemStack(ItemID.MACKEREL, 1)), + COOK_TROUT(ItemID.TROUT, "Trout", Skill.COOKING, 15, 70.0, + CriticalItem.RAW_TROUT, null, new ItemStack(ItemID.TROUT, 1)), + COOK_COD(ItemID.COD, "Cod", Skill.COOKING, 18, 75.0, + CriticalItem.RAW_COD, null, new ItemStack(ItemID.COD, 1)), + COOK_PIKE(ItemID.PIKE, "Pike", Skill.COOKING, 20, 80.0, + CriticalItem.RAW_PIKE, null, new ItemStack(ItemID.PIKE, 1)), + COOK_SALMON(ItemID.SALMON, "Salmon", Skill.COOKING, 25, 90.0, + CriticalItem.RAW_SALMON, null, new ItemStack(ItemID.SALMON, 1)), + COOK_TUNA(ItemID.TUNA, "Tuna", Skill.COOKING, 30, 100.0, + CriticalItem.RAW_TUNA, null, new ItemStack(ItemID.TUNA, 1)), + COOK_KARAMBWAN(ItemID.COOKED_KARAMBWAN, "Cooked Karambwan", Skill.COOKING, 30, 190.0, + CriticalItem.RAW_KARAMBWAN, null, new ItemStack(ItemID.COOKED_KARAMBWAN, 1)), + COOK_LOBSTER(ItemID.LOBSTER, "Lobster", Skill.COOKING, 40, 120.0, + CriticalItem.RAW_LOBSTER, null, new ItemStack(ItemID.LOBSTER, 1)), + COOK_BASS(ItemID.BASS, "Bass", Skill.COOKING, 43, 130.0, + CriticalItem.RAW_BASS, null, new ItemStack(ItemID.BASS, 1)), + COOK_SWORDFISH(ItemID.SWORDFISH, "Swordfish", Skill.COOKING, 45, 140.0, + CriticalItem.RAW_SWORDFISH, null, new ItemStack(ItemID.SWORDFISH, 1)), + COOK_MONKFISH(ItemID.MONKFISH, "Monkfish", Skill.COOKING, 62, 150.0, + CriticalItem.RAW_MONKFISH, null, new ItemStack(ItemID.MONKFISH, 1)), + COOK_SHARK(ItemID.SHARK, "Shark", Skill.COOKING, 80, 210.0, + CriticalItem.RAW_SHARK, null, new ItemStack(ItemID.SHARK, 1)), + COOK_SEA_TURTLE(ItemID.SEA_TURTLE, "Sea turtle", Skill.COOKING, 82, 211.3, + CriticalItem.RAW_SEA_TURTLE, null, new ItemStack(ItemID.SEA_TURTLE, 1)), + COOK_ANGLERFISH(ItemID.ANGLERFISH, "Anglerfish", Skill.COOKING, 84, 230.0, + CriticalItem.RAW_ANGLERFISH, null, new ItemStack(ItemID.ANGLERFISH, 1)), + COOK_DARK_CRAB(ItemID.DARK_CRAB, "Dark crab", Skill.COOKING, 90, 215.0, + CriticalItem.RAW_DARK_CRAB, null, new ItemStack(ItemID.DARK_CRAB, 1)), + COOK_MANTA_RAY(ItemID.MANTA_RAY, "Manta ray", Skill.COOKING, 91, 216.2, + CriticalItem.RAW_MANTA_RAY, null, new ItemStack(ItemID.MANTA_RAY, 1)), - WINE(ItemID.JUG_OF_WINE, "Other", Skill.COOKING, 35, 200, CriticalItem.GRAPES, ActivitySecondaries.JUG_OF_WATER), + WINE(ItemID.JUG_OF_WINE, "Jug of wine", Skill.COOKING, 35, 200, + CriticalItem.GRAPES, Secondaries.JUG_OF_WATER, new ItemStack(ItemID.JUG_OF_WINE, 1)), /* * Crafting Items */ // Spinning - BALL_OF_WOOL(ItemID.WOOL, "Misc", Skill.CRAFTING, 1, 2.5, CriticalItem.WOOL), - BOW_STRING(ItemID.BOW_STRING, "Misc", Skill.CRAFTING, 1, 15, CriticalItem.FLAX), + BALL_OF_WOOL(ItemID.BALL_OF_WOOL, "Ball of wool", Skill.CRAFTING, 1, 2.5, + CriticalItem.WOOL, null, new ItemStack(ItemID.BALL_OF_WOOL, 1)), + BOW_STRING(ItemID.BOW_STRING, "Bow string", Skill.CRAFTING, 1, 15, + CriticalItem.FLAX, null, new ItemStack(ItemID.BOW_STRING, 1)), // Glass Blowing - BEER_GLASS(ItemID.BEER_GLASS, "Beer Glass", Skill.CRAFTING, 1, 17.5, CriticalItem.MOLTEN_GLASS), - CANDLE_LANTERN(ItemID.CANDLE_LANTERN, "Candle Lantern", Skill.CRAFTING, 4, 19, CriticalItem.MOLTEN_GLASS), - OIL_LAMP(ItemID.OIL_LAMP, "Oil Lamp", Skill.CRAFTING, 12, 25, CriticalItem.MOLTEN_GLASS), - VIAL(ItemID.VIAL, "Vial", Skill.CRAFTING, 33, 35, CriticalItem.MOLTEN_GLASS), - EMPTY_FISHBOWL(ItemID.EMPTY_FISHBOWL, "Empty fishbowl", Skill.CRAFTING, 42, 42.5, CriticalItem.MOLTEN_GLASS), - UNPOWERED_ORB(ItemID.UNPOWERED_ORB, "Unpowered orb", Skill.CRAFTING, 46, 52.5, CriticalItem.MOLTEN_GLASS), - LANTERN_LENS(ItemID.LANTERN_LENS, "Lantern lens", Skill.CRAFTING, 49, 55, CriticalItem.MOLTEN_GLASS), - LIGHT_ORB(ItemID.LIGHT_ORB, "Light orb", Skill.CRAFTING, 87, 70, CriticalItem.MOLTEN_GLASS), + BEER_GLASS(ItemID.BEER_GLASS, "Beer glass", Skill.CRAFTING, 1, 17.5, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.BEER_GLASS, 1)), + CANDLE_LANTERN(ItemID.CANDLE_LANTERN, "Candle lantern", Skill.CRAFTING, 4, 19, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.CANDLE_LANTERN, 1)), + OIL_LAMP(ItemID.OIL_LAMP, "Oil lamp", Skill.CRAFTING, 12, 25, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.OIL_LAMP, 1)), + VIAL(ItemID.VIAL, "Vial", Skill.CRAFTING, 33, 35, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.VIAL, 1)), + EMPTY_FISHBOWL(ItemID.EMPTY_FISHBOWL, "Empty fishbowl", Skill.CRAFTING, 42, 42.5, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.EMPTY_FISHBOWL, 1)), + UNPOWERED_ORB(ItemID.UNPOWERED_ORB, "Unpowered orb", Skill.CRAFTING, 46, 52.5, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.UNPOWERED_ORB, 1)), + LANTERN_LENS(ItemID.LANTERN_LENS, "Lantern lens", Skill.CRAFTING, 49, 55, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.LANTERN_LENS, 1)), + LIGHT_ORB(ItemID.LIGHT_ORB, "Light orb", Skill.CRAFTING, 87, 70, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.LIGHT_ORB, 1)), + // D'hide/Dragon Leather - GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 57, 62.0, CriticalItem.GREEN_DRAGON_LEATHER), - BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 66, 70.0, CriticalItem.BLUE_DRAGON_LEATHER), - RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 73, 78.0, CriticalItem.RED_DRAGON_LEATHER), - BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 79, 86.0, CriticalItem.BLACK_DRAGON_LEATHER), + GREEN_DRAGONHIDE(ItemID.GREEN_DRAGON_LEATHER, "Tan Green D'hide", Skill.CRAFTING, 57, 0, + CriticalItem.GREEN_DRAGONHIDE, null, new ItemStack(ItemID.GREEN_DRAGON_LEATHER, 1)), + BLUE_DRAGONHIDE(ItemID.BLUE_DRAGON_LEATHER, "Tan Blue D'hide", Skill.CRAFTING, 66, 0, + CriticalItem.BLUE_DRAGONHIDE, null, new ItemStack(ItemID.BLUE_DRAGON_LEATHER, 1)), + RED_DRAGONHIDE(ItemID.RED_DRAGON_LEATHER, "Tan Red D'hide", Skill.CRAFTING, 73, 0, + CriticalItem.RED_DRAGONHIDE, null, new ItemStack(ItemID.RED_DRAGON_LEATHER, 1)), + BLACK_DRAGONHIDE(ItemID.BLACK_DRAGON_LEATHER, "Tan Black D'hide", Skill.CRAFTING, 79, 0, + CriticalItem.BLACK_DRAGONHIDE, null, new ItemStack(ItemID.BLACK_DRAGON_LEATHER, 1)), + + GREEN_DRAGON_LEATHER(ItemID.GREEN_DHIDE_VAMB, "Green D'hide product", Skill.CRAFTING, 57, 62.0, + CriticalItem.GREEN_DRAGON_LEATHER, null, null), + BLUE_DRAGON_LEATHER(ItemID.BLUE_DHIDE_VAMB, "Blue D'hide product", Skill.CRAFTING, 66, 70.0, + CriticalItem.BLUE_DRAGON_LEATHER, null, null), + RED_DRAGON_LEATHER(ItemID.RED_DHIDE_VAMB, "Red D'hide product", Skill.CRAFTING, 73, 78.0, + CriticalItem.RED_DRAGON_LEATHER, null, null), + BLACK_DRAGON_LEATHER(ItemID.BLACK_DHIDE_VAMB, "Black D'hide product", Skill.CRAFTING, 79, 86.0, + CriticalItem.BLACK_DRAGON_LEATHER, null, null), + // Uncut Gems - UNCUT_OPAL(ItemID.UNCUT_OPAL, "Gems", Skill.CRAFTING, 1, 15.0, CriticalItem.UNCUT_OPAL), - UNCUT_JADE(ItemID.UNCUT_JADE, "Gems", Skill.CRAFTING, 13, 20.0, CriticalItem.UNCUT_JADE), - UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, "Gems", Skill.CRAFTING, 16, 25.0, CriticalItem.UNCUT_RED_TOPAZ), - UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, "Gems", Skill.CRAFTING, 20, 50.0, CriticalItem.UNCUT_SAPPHIRE), - UNCUT_EMERALD(ItemID.UNCUT_EMERALD, "Gems", Skill.CRAFTING, 27, 67.5, CriticalItem.UNCUT_EMERALD), - UNCUT_RUBY(ItemID.UNCUT_RUBY, "Gems", Skill.CRAFTING, 34, 85, CriticalItem.UNCUT_RUBY), - UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, "Gems", Skill.CRAFTING, 43, 107.5, CriticalItem.UNCUT_DIAMOND), - UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, "Gems", Skill.CRAFTING, 55, 137.5, CriticalItem.UNCUT_DRAGONSTONE), - UNCUT_ONYX(ItemID.UNCUT_ONYX, "Gems", Skill.CRAFTING, 67, 167.5, CriticalItem.UNCUT_ONYX), - UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, "Gems", Skill.CRAFTING, 89, 200.0, CriticalItem.UNCUT_ZENYTE), + UNCUT_OPAL(ItemID.OPAL, "Cut opal", Skill.CRAFTING, 1, 15.0, + CriticalItem.UNCUT_OPAL, null, new ItemStack(ItemID.OPAL, 1)), + UNCUT_JADE(ItemID.JADE, "Cut jade", Skill.CRAFTING, 13, 20.0, + CriticalItem.UNCUT_JADE, null, new ItemStack(ItemID.JADE, 1)), + UNCUT_RED_TOPAZ(ItemID.RED_TOPAZ, "Cut red topaz", Skill.CRAFTING, 16, 25.0, + CriticalItem.UNCUT_RED_TOPAZ, null, new ItemStack(ItemID.RED_TOPAZ, 1)), + UNCUT_SAPPHIRE(ItemID.SAPPHIRE, "Cut sapphire", Skill.CRAFTING, 20, 50.0, + CriticalItem.UNCUT_SAPPHIRE, null, new ItemStack(ItemID.SAPPHIRE, 1)), + UNCUT_EMERALD(ItemID.EMERALD, "Cut emerald", Skill.CRAFTING, 27, 67.5, + CriticalItem.UNCUT_EMERALD, null, new ItemStack(ItemID.EMERALD, 1)), + UNCUT_RUBY(ItemID.RUBY, "Cut ruby", Skill.CRAFTING, 34, 85, + CriticalItem.UNCUT_RUBY, null, new ItemStack(ItemID.RUBY, 1)), + UNCUT_DIAMOND(ItemID.DIAMOND, "Cut diamond", Skill.CRAFTING, 43, 107.5, + CriticalItem.UNCUT_DIAMOND, null, new ItemStack(ItemID.DIAMOND, 1)), + UNCUT_DRAGONSTONE(ItemID.DRAGONSTONE, "Cut dragonstone", Skill.CRAFTING, 55, 137.5, + CriticalItem.UNCUT_DRAGONSTONE, null, new ItemStack(ItemID.DRAGONSTONE, 1)), + UNCUT_ONYX(ItemID.ONYX, "Cut onyx", Skill.CRAFTING, 67, 167.5, + CriticalItem.UNCUT_ONYX, null, new ItemStack(ItemID.ONYX, 1)), + UNCUT_ZENYTE(ItemID.ZENYTE, "Cut zenyte", Skill.CRAFTING, 89, 200.0, + CriticalItem.UNCUT_ZENYTE, null, new ItemStack(ItemID.ZENYTE, 1)), // Silver Jewelery - OPAL_RING(ItemID.OPAL_RING, "Opal ring", Skill.CRAFTING, 1, 10, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - OPAL_NECKLACE(ItemID.OPAL_NECKLACE, "Opal necklace", Skill.CRAFTING, 16, 35, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - OPAL_BRACELET(ItemID.OPAL_BRACELET, "Opal bracelet", Skill.CRAFTING, 22, 45, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - OPAL_AMULET(ItemID.OPAL_AMULET, "Opal amulet", Skill.CRAFTING, 27, 55, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - JADE_RING(ItemID.JADE_RING, "Jade ring", Skill.CRAFTING, 13, 32, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - JADE_NECKLACE(ItemID.JADE_NECKLACE, "Jade necklace", Skill.CRAFTING, 25, 54, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - JADE_BRACELET(ItemID.JADE_BRACELET, "Jade bracelet", Skill.CRAFTING, 29, 60, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - JADE_AMULET(ItemID.JADE_AMULET, "Jade amulet", Skill.CRAFTING, 34, 70, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - TOPAZ_RING(ItemID.TOPAZ_RING, "Topaz ring", Skill.CRAFTING, 16, 35, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), - TOPAZ_NECKLACE(ItemID.TOPAZ_NECKLACE, "Topaz necklace", Skill.CRAFTING, 32, 70, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), - TOPAZ_BRACELET(ItemID.TOPAZ_BRACELET, "Topaz bracelet", Skill.CRAFTING, 38, 75, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), - TOPAZ_AMULET(ItemID.TOPAZ_AMULET, "Topaz amulet", Skill.CRAFTING, 45, 80, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), + OPAL_RING(ItemID.OPAL_RING, "Opal ring", Skill.CRAFTING, 1, 10, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_RING, 1)), + OPAL_NECKLACE(ItemID.OPAL_NECKLACE, "Opal necklace", Skill.CRAFTING, 16, 35, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_NECKLACE, 1)), + OPAL_BRACELET(ItemID.OPAL_BRACELET, "Opal bracelet", Skill.CRAFTING, 22, 45, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_BRACELET, 1)), + OPAL_AMULET(ItemID.OPAL_AMULET, "Opal amulet", Skill.CRAFTING, 27, 55, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_AMULET, 1)), + JADE_RING(ItemID.JADE_RING, "Jade ring", Skill.CRAFTING, 13, 32, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_RING, 1)), + JADE_NECKLACE(ItemID.JADE_NECKLACE, "Jade necklace", Skill.CRAFTING, 25, 54, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_NECKLACE, 1)), + JADE_BRACELET(ItemID.JADE_BRACELET, "Jade bracelet", Skill.CRAFTING, 29, 60, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_BRACELET, 1)), + JADE_AMULET(ItemID.JADE_AMULET, "Jade amulet", Skill.CRAFTING, 34, 70, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_AMULET, 1)), + TOPAZ_RING(ItemID.TOPAZ_RING, "Topaz ring", Skill.CRAFTING, 16, 35, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_RING, 1)), + TOPAZ_NECKLACE(ItemID.TOPAZ_NECKLACE, "Topaz necklace", Skill.CRAFTING, 32, 70, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_NECKLACE, 1)), + TOPAZ_BRACELET(ItemID.TOPAZ_BRACELET, "Topaz bracelet", Skill.CRAFTING, 38, 75, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_BRACELET, 1)), + TOPAZ_AMULET(ItemID.TOPAZ_AMULET, "Topaz amulet", Skill.CRAFTING, 45, 80, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_AMULET, 1)), // Gold Jewelery - SAPPHIRE_RING(ItemID.SAPPHIRE_RING, "Sapphire ring", Skill.CRAFTING, 20, 40, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, "Sapphire necklace", Skill.CRAFTING, 22, 55, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET, "Sapphire bracelet", Skill.CRAFTING, 23, 60, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - SAPPHIRE_AMULET(ItemID.SAPPHIRE_AMULET, "Sapphire amulet", Skill.CRAFTING, 24, 65, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - EMERALD_RING(ItemID.EMERALD_RING, "Emerald ring", Skill.CRAFTING, 27, 55, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - EMERALD_NECKLACE(ItemID.EMERALD_NECKLACE, "Emerald necklace", Skill.CRAFTING, 29, 60, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - EMERALD_BRACELET(ItemID.EMERALD_BRACELET, "Emerald bracelet", Skill.CRAFTING, 30, 65, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - EMERALD_AMULET(ItemID.EMERALD_AMULET, "Emerald amulet", Skill.CRAFTING, 31, 70, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - RUBY_RING(ItemID.RUBY_RING, "Ruby ring", Skill.CRAFTING, 34, 70, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - RUBY_NECKLACE(ItemID.RUBY_NECKLACE, "Ruby necklace", Skill.CRAFTING, 40, 75, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - RUBY_BRACELET(ItemID.RUBY_BRACELET, "Ruby bracelet", Skill.CRAFTING, 42, 80, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - RUBY_AMULET(ItemID.RUBY_AMULET, "Ruby amulet", Skill.CRAFTING, 50, 85, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - DIAMOND_RING(ItemID.DIAMOND_RING, "Diamond ring", Skill.CRAFTING, 43, 85, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DIAMOND_NECKLACE(ItemID.DIAMOND_NECKLACE, "Diamond necklace", Skill.CRAFTING, 56, 90, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DIAMOND_BRACELET(ItemID.DIAMOND_BRACELET, "Diamond bracelet", Skill.CRAFTING, 58, 95, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DIAMOND_AMULET(ItemID.DIAMOND_AMULET, "Diamond amulet", Skill.CRAFTING, 70, 100, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DRAGONSTONE_RING(ItemID.DRAGONSTONE_RING, "Dragonstone ring", Skill.CRAFTING, 55, 100, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - DRAGON_NECKLACE(ItemID.DRAGON_NECKLACE, "Dragon necklace", Skill.CRAFTING, 72, 105, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - DRAGONSTONE_BRACELET(ItemID.DRAGONSTONE_BRACELET, "Dragonstone bracelet", Skill.CRAFTING, 74, 110, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - DRAGONSTONE_AMULET(ItemID.DRAGONSTONE_AMULET, "Dragonstone amulet", Skill.CRAFTING, 80, 150, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - ONYX_RING(ItemID.ONYX_RING, "Onyx ring", Skill.CRAFTING, 67, 115, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - ONYX_NECKLACE(ItemID.ONYX_NECKLACE, "Onyx necklace", Skill.CRAFTING, 82, 120, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - REGEN_BRACELET(ItemID.REGEN_BRACELET, "Regen bracelet", Skill.CRAFTING, 84, 125, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - ONYX_AMULET(ItemID.ONYX_AMULET, "Onyx amulet", Skill.CRAFTING, 90, 165, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - ZENYTE_RING(ItemID.ZENYTE_RING, "Zenyte ring", Skill.CRAFTING, 89, 150, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), - ZENYTE_NECKLACE(ItemID.ZENYTE_NECKLACE, "Zenyte necklace", Skill.CRAFTING, 92, 165, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), - ZENYTE_BRACELET(ItemID.ZENYTE_BRACELET, "Zenyte bracelet", Skill.CRAFTING, 95, 180, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), - ZENYTE_AMULET(ItemID.ZENYTE_AMULET, "Zenyte amulet", Skill.CRAFTING, 98, 200, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), + SAPPHIRE_RING(ItemID.SAPPHIRE_RING, "Sapphire ring", Skill.CRAFTING, 20, 40, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_RING, 1)), + SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, "Sapphire necklace", Skill.CRAFTING, 22, 55, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_NECKLACE, 1)), + SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET, "Sapphire bracelet", Skill.CRAFTING, 23, 60, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_BRACELET, 1)), + SAPPHIRE_AMULET(ItemID.SAPPHIRE_AMULET, "Sapphire amulet", Skill.CRAFTING, 24, 65, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_AMULET, 1)), + EMERALD_RING(ItemID.EMERALD_RING, "Emerald ring", Skill.CRAFTING, 27, 55, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_RING, 1)), + EMERALD_NECKLACE(ItemID.EMERALD_NECKLACE, "Emerald necklace", Skill.CRAFTING, 29, 60, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_NECKLACE, 1)), + EMERALD_BRACELET(ItemID.EMERALD_BRACELET, "Emerald bracelet", Skill.CRAFTING, 30, 65, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_BRACELET, 1)), + EMERALD_AMULET(ItemID.EMERALD_AMULET, "Emerald amulet", Skill.CRAFTING, 31, 70, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_AMULET, 1)), + RUBY_RING(ItemID.RUBY_RING, "Ruby ring", Skill.CRAFTING, 34, 70, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_RING, 1)), + RUBY_NECKLACE(ItemID.RUBY_NECKLACE, "Ruby necklace", Skill.CRAFTING, 40, 75, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_NECKLACE, 1)), + RUBY_BRACELET(ItemID.RUBY_BRACELET, "Ruby bracelet", Skill.CRAFTING, 42, 80, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_BRACELET, 1)), + RUBY_AMULET(ItemID.RUBY_AMULET, "Ruby amulet", Skill.CRAFTING, 50, 85, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_AMULET, 1)), + DIAMOND_RING(ItemID.DIAMOND_RING, "Diamond ring", Skill.CRAFTING, 43, 85, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_RING, 1)), + DIAMOND_NECKLACE(ItemID.DIAMOND_NECKLACE, "Diamond necklace", Skill.CRAFTING, 56, 90, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_NECKLACE, 1)), + DIAMOND_BRACELET(ItemID.DIAMOND_BRACELET, "Diamond bracelet", Skill.CRAFTING, 58, 95, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_BRACELET, 1)), + DIAMOND_AMULET(ItemID.DIAMOND_AMULET, "Diamond amulet", Skill.CRAFTING, 70, 100, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_AMULET, 1)), + DRAGONSTONE_RING(ItemID.DRAGONSTONE_RING, "Dragonstone ring", Skill.CRAFTING, 55, 100, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGONSTONE_RING, 1)), + DRAGON_NECKLACE(ItemID.DRAGON_NECKLACE, "Dragon necklace", Skill.CRAFTING, 72, 105, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGON_NECKLACE, 1)), + DRAGONSTONE_BRACELET(ItemID.DRAGONSTONE_BRACELET, "Dragonstone bracelet", Skill.CRAFTING, 74, 110, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGONSTONE_BRACELET, 1)), + DRAGONSTONE_AMULET(ItemID.DRAGONSTONE_AMULET, "Dragonstone amulet", Skill.CRAFTING, 80, 150, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGONSTONE_AMULET, 1)), + ONYX_RING(ItemID.ONYX_RING, "Onyx ring", Skill.CRAFTING, 67, 115, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.ONYX_RING, 1)), + ONYX_NECKLACE(ItemID.ONYX_NECKLACE, "Onyx necklace", Skill.CRAFTING, 82, 120, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.ONYX_NECKLACE, 1)), + REGEN_BRACELET(ItemID.REGEN_BRACELET, "Regen bracelet", Skill.CRAFTING, 84, 125, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.REGEN_BRACELET, 1)), + ONYX_AMULET(ItemID.ONYX_AMULET, "Onyx amulet", Skill.CRAFTING, 90, 165, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.ONYX_AMULET, 1)), + ZENYTE_RING(ItemID.ZENYTE_RING, "Zenyte ring", Skill.CRAFTING, 89, 150, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_RING, 1)), + ZENYTE_NECKLACE(ItemID.ZENYTE_NECKLACE, "Zenyte necklace", Skill.CRAFTING, 92, 165, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_NECKLACE, 1)), + ZENYTE_BRACELET(ItemID.ZENYTE_BRACELET, "Zenyte bracelet", Skill.CRAFTING, 95, 180, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_BRACELET, 1)), + ZENYTE_AMULET(ItemID.ZENYTE_AMULET, "Zenyte amulet", Skill.CRAFTING, 98, 200, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_AMULET, 1)), // Battle Staves - WATER_BATTLESTAFF(ItemID.WATER_BATTLESTAFF, "Water battlestaff", Skill.CRAFTING, 54, 100, CriticalItem.BATTLESTAFF, ActivitySecondaries.WATER_ORB), - EARTH_BATTLESTAFF(ItemID.EARTH_BATTLESTAFF, "Earth battlestaff", Skill.CRAFTING, 58, 112.5, CriticalItem.BATTLESTAFF, ActivitySecondaries.EARTH_ORB), - FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, "Fire battlestaff", Skill.CRAFTING, 62, 125, CriticalItem.BATTLESTAFF, ActivitySecondaries.FIRE_ORB), - AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, "Air battlestaff", Skill.CRAFTING, 66, 137.5, CriticalItem.BATTLESTAFF, ActivitySecondaries.AIR_ORB), + WATER_BATTLESTAFF(ItemID.WATER_BATTLESTAFF, "Water battlestaff", Skill.CRAFTING, 54, 100, + CriticalItem.BATTLESTAFF, Secondaries.WATER_ORB, new ItemStack(ItemID.WATER_BATTLESTAFF, 1)), + EARTH_BATTLESTAFF(ItemID.EARTH_BATTLESTAFF, "Earth battlestaff", Skill.CRAFTING, 58, 112.5, + CriticalItem.BATTLESTAFF, Secondaries.EARTH_ORB, new ItemStack(ItemID.EARTH_BATTLESTAFF, 1)), + FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, "Fire battlestaff", Skill.CRAFTING, 62, 125, + CriticalItem.BATTLESTAFF, Secondaries.FIRE_ORB, new ItemStack(ItemID.FIRE_BATTLESTAFF, 1)), + AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, "Air battlestaff", Skill.CRAFTING, 66, 137.5, + CriticalItem.BATTLESTAFF, Secondaries.AIR_ORB, new ItemStack(ItemID.AIR_BATTLESTAFF, 1)), /* * Smithing Items */ // Smelting ores (Furnace) - IRON_ORE(ItemID.IRON_BAR, "Iron Bars", Skill.SMITHING, 15, 12.5, CriticalItem.IRON_ORE, ActivitySecondaries.COAL_ORE), - STEEL_ORE(ItemID.STEEL_BAR, "Steel Bars", Skill.SMITHING, 30, 17.5, CriticalItem.IRON_ORE, ActivitySecondaries.COAL_ORE_2), - SILVER_ORE(ItemID.SILVER_ORE, "Bar", Skill.SMITHING, 20, 13.67, CriticalItem.SILVER_ORE), - GOLD_ORE(ItemID.GOLD_BAR, "Regular exp", Skill.SMITHING, 40, 22.5, CriticalItem.GOLD_ORE), - GOLD_ORE_GAUNTLETS(ItemID.GOLDSMITH_GAUNTLETS, "Goldsmith Gauntlets", Skill.SMITHING, 40, 56.2, CriticalItem.GOLD_ORE), - MITHRIL_ORE(ItemID.MITHRIL_ORE, "Bar", Skill.SMITHING, 50, 30, CriticalItem.MITHRIL_ORE, ActivitySecondaries.COAL_ORE_4), - ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, "Bar", Skill.SMITHING, 70, 37.5, CriticalItem.ADAMANTITE_ORE, ActivitySecondaries.COAL_ORE_6), - RUNITE_ORE(ItemID.RUNITE_ORE, "Bar", Skill.SMITHING, 85, 50, CriticalItem.RUNITE_ORE, ActivitySecondaries.COAL_ORE_8), + IRON_ORE(ItemID.IRON_BAR, "Iron bar", Skill.SMITHING, 15, 12.5, + CriticalItem.IRON_ORE, Secondaries.COAL_ORE, new ItemStack(ItemID.IRON_BAR, 1)), + STEEL_ORE(ItemID.STEEL_BAR, "Steel bar", Skill.SMITHING, 30, 17.5, + CriticalItem.IRON_ORE, Secondaries.COAL_ORE_2, new ItemStack(ItemID.STEEL_BAR, 1)), + SILVER_ORE(ItemID.SILVER_BAR, "Silver Bar", Skill.SMITHING, 20, 13.67, + CriticalItem.SILVER_ORE, null, new ItemStack(ItemID.SILVER_BAR, 1)), + GOLD_ORE(ItemID.GOLD_BAR, "Gold bar", Skill.SMITHING, 40, 22.5, + CriticalItem.GOLD_ORE, null, new ItemStack(ItemID.GOLD_BAR, 1)), + GOLD_ORE_GAUNTLETS(ItemID.GOLDSMITH_GAUNTLETS, "Goldsmith gauntlets", Skill.SMITHING, 40, 56.2, + CriticalItem.GOLD_ORE, null, new ItemStack(ItemID.GOLD_BAR, 1)), + MITHRIL_ORE(ItemID.MITHRIL_BAR, "Mithril bar", Skill.SMITHING, 50, 30, + CriticalItem.MITHRIL_ORE, Secondaries.COAL_ORE_4, new ItemStack(ItemID.MITHRIL_BAR, 1)), + ADAMANTITE_ORE(ItemID.ADAMANTITE_BAR, "Adamantite bar", Skill.SMITHING, 70, 37.5, + CriticalItem.ADAMANTITE_ORE, Secondaries.COAL_ORE_6, new ItemStack(ItemID.ADAMANTITE_BAR, 1)), + RUNITE_ORE(ItemID.RUNITE_BAR, "Runite bar", Skill.SMITHING, 85, 50, + CriticalItem.RUNITE_ORE, Secondaries.COAL_ORE_8, new ItemStack(ItemID.RUNITE_BAR, 1)), // Smelting bars (Anvil) - BRONZE_BAR(ItemID.BRONZE_BAR, "Bars", Skill.SMITHING, 1, 12.5, CriticalItem.BRONZE_BAR), - IRON_BAR(ItemID.IRON_BAR, "Bars", Skill.SMITHING, 15, 25.0, CriticalItem.IRON_BAR), - STEEL_BAR(ItemID.STEEL_BAR, "Steel Products", Skill.SMITHING, 30, 37.5, CriticalItem.STEEL_BAR), - CANNONBALLS(ItemID.CANNONBALL, "Cannonballs", Skill.SMITHING, 35, 25.5, CriticalItem.STEEL_BAR), - MITHRIL_BAR(ItemID.MITHRIL_BAR, "Bars", Skill.SMITHING, 50, 50.0, CriticalItem.MITHRIL_BAR), - ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Bars", Skill.SMITHING, 70, 62.5, CriticalItem.ADAMANTITE_BAR), - RUNITE_BAR(ItemID.RUNITE_BAR, "Bars", Skill.SMITHING, 85, 75.0, CriticalItem.RUNITE_BAR), + BRONZE_BAR(ItemID.BRONZE_BAR, "Bronze products", Skill.SMITHING, 1, 12.5, + CriticalItem.BRONZE_BAR, null, null), + IRON_BAR(ItemID.IRON_BAR, "Iron products", Skill.SMITHING, 15, 25.0, + CriticalItem.IRON_BAR, null, null), + STEEL_BAR(ItemID.STEEL_BAR, "Steel products", Skill.SMITHING, 30, 37.5, + CriticalItem.STEEL_BAR, null, null), + CANNONBALLS(ItemID.CANNONBALL, "Cannonballs", Skill.SMITHING, 35, 25.5, + CriticalItem.STEEL_BAR, null, new ItemStack(ItemID.CANNONBALL, 4)), + MITHRIL_BAR(ItemID.MITHRIL_BAR, "Mithril products", Skill.SMITHING, 50, 50.0, + CriticalItem.MITHRIL_BAR, null, null), + ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Adamantite products", Skill.SMITHING, 70, 62.5, + CriticalItem.ADAMANTITE_BAR, null, null), + RUNITE_BAR(ItemID.RUNITE_BAR, "Runite products", Skill.SMITHING, 85, 75.0, + CriticalItem.RUNITE_BAR, null, null), /** * Farming Items */ - ACORN(ItemID.ACORN, "Seeds", Skill.FARMING, 15, 481.3, CriticalItem.ACORN), - WILLOW_SEED(ItemID.WILLOW_SEED, "Seeds", Skill.FARMING, 30, 1481.5, CriticalItem.WILLOW_SEED), - MAPLE_SEED(ItemID.MAPLE_SEED, "Seeds", Skill.FARMING, 45, 3448.4, CriticalItem.MAPLE_SEED), - YEW_SEED(ItemID.YEW_SEED, "Seeds", Skill.FARMING, 60, 7150.9, CriticalItem.YEW_SEED), - MAGIC_SEED(ItemID.MAGIC_SEED, "Seeds", Skill.FARMING, 75, 13913.8, CriticalItem.MAGIC_SEED), - APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, "Seeds", Skill.FARMING, 27, 1272.5, CriticalItem.APPLE_TREE_SEED), - BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, "Seeds", Skill.FARMING, 33, 1841.5, CriticalItem.BANANA_TREE_SEED), - ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, "Seeds", Skill.FARMING, 39, 2586.7, CriticalItem.ORANGE_TREE_SEED), - CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, "Seeds", Skill.FARMING, 42, 3036.9, CriticalItem.CURRY_TREE_SEED), - PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, "Seeds", Skill.FARMING, 51, 4791.7, CriticalItem.PINEAPPLE_SEED), - PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, "Seeds", Skill.FARMING, 57, 6380.4, CriticalItem.PAPAYA_TREE_SEED), - PALM_TREE_SEED(ItemID.PALM_TREE_SEED, "Seeds", Skill.FARMING, 68, 10509.6, CriticalItem.PALM_TREE_SEED), - CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, "Seeds", Skill.FARMING, 72, 12516.5, CriticalItem.CALQUAT_TREE_SEED), - TEAK_SEED(ItemID.TEAK_SEED, "Seeds", Skill.FARMING, 35, 7325, CriticalItem.TEAK_SEED), - MAHOGANY_SEED(ItemID.MAHOGANY_SEED, "Seeds", Skill.FARMING, 55, 15783, CriticalItem.MAHOGANY_SEED), - SPIRIT_SEED(ItemID.SPIRIT_SEED, "Seeds", Skill.FARMING, 83, 19500, CriticalItem.SPIRIT_SEED), + ACORN(ItemID.OAK_SAPLING, "Oak sapling", Skill.FARMING, 15, 0, + CriticalItem.ACORN, null, new ItemStack(ItemID.OAK_SAPLING, 1)), + WILLOW_SEED(ItemID.WILLOW_SAPLING, "Willow sapling", Skill.FARMING, 30, 0, + CriticalItem.WILLOW_SEED, null, new ItemStack(ItemID.WILLOW_SAPLING, 1)), + MAPLE_SEED(ItemID.MAPLE_SAPLING, "Maple sapling", Skill.FARMING, 45, 0, + CriticalItem.MAPLE_SEED, null, new ItemStack(ItemID.MAPLE_SAPLING, 1)), + YEW_SEED(ItemID.YEW_SAPLING, "Yew sapling", Skill.FARMING, 60, 0, + CriticalItem.YEW_SEED, null, new ItemStack(ItemID.YEW_SAPLING, 1)), + MAGIC_SEED(ItemID.MAGIC_SAPLING, "Magic sapling", Skill.FARMING, 75, 0, + CriticalItem.MAGIC_SEED, null, new ItemStack(ItemID.MAGIC_SAPLING, 1)), + APPLE_TREE_SEED(ItemID.APPLE_SAPLING, "Apple sapling", Skill.FARMING, 27, 0, + CriticalItem.APPLE_TREE_SEED, null, new ItemStack(ItemID.APPLE_SAPLING, 1)), + BANANA_TREE_SEED(ItemID.BANANA_SAPLING, "Banana sapling", Skill.FARMING, 33, 0, + CriticalItem.BANANA_TREE_SEED, null, new ItemStack(ItemID.BANANA_SAPLING, 1)), + ORANGE_TREE_SEED(ItemID.ORANGE_SAPLING, "Orange sapling", Skill.FARMING, 39, 0, + CriticalItem.ORANGE_TREE_SEED, null, new ItemStack(ItemID.ORANGE_SAPLING, 1)), + CURRY_TREE_SEED(ItemID.CURRY_SAPLING, "Curry sapling", Skill.FARMING, 42, 0, + CriticalItem.CURRY_TREE_SEED, null, new ItemStack(ItemID.CURRY_SAPLING, 1)), + PINEAPPLE_SEED(ItemID.PINEAPPLE_SAPLING, "Pineapple sapling", Skill.FARMING, 51, 0, + CriticalItem.PINEAPPLE_SEED, null, new ItemStack(ItemID.PINEAPPLE_SAPLING, 1)), + PAPAYA_TREE_SEED(ItemID.PAPAYA_SAPLING, "Papaya sapling", Skill.FARMING, 57, 0, + CriticalItem.PAPAYA_TREE_SEED, null, new ItemStack(ItemID.PAPAYA_SAPLING, 1)), + PALM_TREE_SEED(ItemID.PALM_SAPLING, "Palm sapling", Skill.FARMING, 68, 0, + CriticalItem.PALM_TREE_SEED, null, new ItemStack(ItemID.PALM_SAPLING, 1)), + CALQUAT_TREE_SEED(ItemID.CALQUAT_SAPLING, "Calquat sapling", Skill.FARMING, 72, 0, + CriticalItem.CALQUAT_TREE_SEED, null, new ItemStack(ItemID.CALQUAT_SAPLING, 1)), + TEAK_SEED(ItemID.TEAK_SAPLING, "Teak sapling", Skill.FARMING, 35, 0, + CriticalItem.TEAK_SEED, null, new ItemStack(ItemID.TEAK_SAPLING, 1)), + MAHOGANY_SEED(ItemID.MAHOGANY_SAPLING, "Mahogany sapling", Skill.FARMING, 55, 0, + CriticalItem.MAHOGANY_SEED, null, new ItemStack(ItemID.MAHOGANY_SAPLING, 1)), + SPIRIT_SEED(ItemID.SPIRIT_SAPLING, "Spirit sapling", Skill.FARMING, 83, 0, + CriticalItem.SPIRIT_SEED, null, new ItemStack(ItemID.SPIRIT_SAPLING, 1)), + + OAK_SAPPLING(ItemID.OAK_SAPLING, "Oak tree", Skill.FARMING, 15, 481.3, + CriticalItem.OAK_SAPLING, null, null), + WILLOW_SAPLING(ItemID.WILLOW_SAPLING, "Willow tree", Skill.FARMING, 30, 1481.5, + CriticalItem.WILLOW_SAPLING, null, null), + MAPLE_SAPLING(ItemID.MAPLE_SAPLING, "Maple tree", Skill.FARMING, 45, 3448.4, + CriticalItem.MAPLE_SAPLING, null, null), + YEW_SAPLING(ItemID.YEW_SAPLING, "Yew tree", Skill.FARMING, 60, 7150.9, + CriticalItem.YEW_SAPLING, null, null), + MAGIC_SAPLING(ItemID.MAGIC_SAPLING, "Magic tree", Skill.FARMING, 75, 13913.8, + CriticalItem.MAGIC_SAPLING, null, null), + APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, "Apple tree", Skill.FARMING, 27, 1272.5, + CriticalItem.APPLE_TREE_SAPLING, null, null), + BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, "Banana tree", Skill.FARMING, 33, 1841.5, + CriticalItem.BANANA_TREE_SAPLING, null, null), + ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, "Orange tree", Skill.FARMING, 39, 2586.7, + CriticalItem.ORANGE_TREE_SAPLING, null, null), + CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, "Curry tree", Skill.FARMING, 42, 3036.9, + CriticalItem.CURRY_TREE_SAPLING, null, null), + PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, "Pineapple tree", Skill.FARMING, 51, 4791.7, + CriticalItem.PINEAPPLE_SAPLING, null, null), + PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, "Papaya tree", Skill.FARMING, 57, 6380.4, + CriticalItem.PAPAYA_TREE_SAPLING, null, null), + PALM_TREE_SAPLING(ItemID.PALM_SAPLING, "Palm tree", Skill.FARMING, 68, 10509.6, + CriticalItem.PALM_TREE_SAPLING, null, null), + CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, "Calquat tree", Skill.FARMING, 72, 12516.5, + CriticalItem.CALQUAT_TREE_SAPLING, null, null), + TEAK_SAPLING(ItemID.TEAK_SAPLING, "Teak tree", Skill.FARMING, 35, 7325, + CriticalItem.TEAK_SAPLING, null, null), + MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, "Mahogany tree", Skill.FARMING, 55, 15783, + CriticalItem.MAHOGANY_SAPLING, null, null), + SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, "Spirit tree", Skill.FARMING, 83, 19500, + CriticalItem.SPIRIT_SAPLING, null, null), ; private final int icon; private final String name; + private final CriticalItem criticalItem; private final Skill skill; private final int level; private final double xp; - private final SecondaryItem[] secondaries; - private final CriticalItem criticalItem; - private final boolean preventLinked; + private final ItemStack[] secondaries; + @Nullable + private final ItemStack output; + private ItemInfo outputItemInfo = null; + @Nullable + private final CriticalItem linkedItem; - Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem) - { - this.icon = Icon; - this.name = name; - this.skill = skill; - this.level = level; - this.xp = xp; - this.criticalItem = criticalItem; - this.secondaries = new SecondaryItem[0]; - this.preventLinked = false; - } + // Store activity by CriticalItem + private static final ImmutableMultimap CRITICAL_MAP; - Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem, ActivitySecondaries secondaries) + static { - this.icon = Icon; - this.name = name; - this.skill = skill; - this.level = level; - this.xp = xp; - this.criticalItem = criticalItem; - this.secondaries = secondaries == null ? new SecondaryItem[0] : secondaries.getItems(); - this.preventLinked = false; - } - - Activity(final int Icon, final String name, final Skill skill, final int level, final double xp, final CriticalItem criticalItem, final ActivitySecondaries secondaries, final boolean preventLinked) - { - this.icon = Icon; - this.name = name; - this.skill = skill; - this.level = level; - this.xp = xp; - this.criticalItem = criticalItem; - this.secondaries = secondaries == null ? new SecondaryItem[0] : secondaries.getItems(); - this.preventLinked = preventLinked; - } - - // Builds a Map to reduce looping frequency - private static Map> buildItemMap() - { - Map> map = new HashMap<>(); - for (Activity item : values()) + final ImmutableMultimap.Builder map = ImmutableMultimap.builder(); + for (final Activity item : values()) { - map.computeIfAbsent(item.getCriticalItem(), e -> new ArrayList<>()).add(item); + map.put(item.getCriticalItem(), item); } - - return map; + CRITICAL_MAP = map.build(); } - private static final Map> byCriticalItem = buildItemMap(); - - public static List getByCriticalItem(CriticalItem item) + Activity( + final int icon, + final String name, + final Skill skill, + final int level, + final double xp, + final CriticalItem criticalItem, + @Nullable final Secondaries secondaries, + @Nullable final ItemStack output) { - - return byCriticalItem.getOrDefault(item, new ArrayList<>()); + this.icon = icon; + this.name = name; + this.skill = skill; + this.level = level; + this.xp = xp; + this.criticalItem = criticalItem; + this.secondaries = secondaries == null ? new ItemStack[0] : secondaries.getItems(); + this.output = output; + this.linkedItem = output == null ? null : CriticalItem.getByItemId(output.getId()); } /** * Get all Activities for this CriticalItem * - * @param item CriticalItem to check for - * @param limitLevel Level to check Activitiy requirements against. -1 or 0 value disables limits - * @return an empty list if no activities + * @param item CriticalItem to check for + * @return an empty Collection if no activities */ - public static List getByCriticalItem(CriticalItem item, int limitLevel) + public static List getByCriticalItem(CriticalItem item) { - List activities = getByCriticalItem(item); - List l = new ArrayList<>(); + final Collection activities = CRITICAL_MAP.get(item); + if (activities == null) + { + return new ArrayList<>(); + } + + return new ArrayList<>(activities); + } + + /** + * Get all Activities for this CriticalItem limited to level + * + * @param item CriticalItem to check for + * @param limitLevel Level to check Activitiy requirements against. -1/0 value disables limits + * @return an empty Collection if no activities + */ + public static List getByCriticalItem(final CriticalItem item, final int limitLevel) + { + // Return as list to allow getting by index + final List l = getByCriticalItem(item); if (limitLevel <= 0) { return l; } - for (Activity a : activities) - { - if (!(a.getLevel() > limitLevel)) - { - l.add(a); - } - } - - return l; + return l.stream().filter(a -> a.getLevel() <= limitLevel).collect(Collectors.toList()); } -} + + /** + * Attaches the Item Composition to each CriticalItem on client initial load + * + * @param m ItemManager + */ + public static void prepareItemDefinitions(ItemManager m) + { + for (Activity a : values()) + { + final ItemStack output = a.getOutput(); + if (output == null) + { + continue; + } + + if (a.getOutputItemInfo() != null) + { + return; + } + + final ItemDefinition c = m.getItemDefinition(output.getId()); + a.outputItemInfo = new ItemInfo(c.getName(), c.isStackable()); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java deleted file mode 100644 index 0afb3cf033..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator.banked.beans; - -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.api.ItemID; - -@Getter(AccessLevel.PUBLIC) -public enum ActivitySecondaries -{ - /** - * Herblore - */ - UNFINISHED_POTION(new SecondaryItem(ItemID.VIAL_OF_WATER, 1)), - SWAMP_TAR(new SecondaryItem(ItemID.SWAMP_TAR, 15)), - - // Guam - ATTACK_POTION(new SecondaryItem(ItemID.EYE_OF_NEWT)), - // Marrentil - ANTIPOISON(new SecondaryItem(ItemID.UNICORN_HORN_DUST)), - // Tarromin - STRENGTH_POTION(new SecondaryItem(ItemID.LIMPWURT_ROOT)), - SERUM_207(new SecondaryItem(ItemID.ASHES)), - // Harralander - COMPOST_POTION(new SecondaryItem(ItemID.VOLCANIC_ASH)), - RESTORE_POTION(new SecondaryItem(ItemID.RED_SPIDERS_EGGS)), - ENERGY_POTION(new SecondaryItem(ItemID.CHOCOLATE_DUST)), - COMBAT_POTION(new SecondaryItem(ItemID.GOAT_HORN_DUST)), - // Ranarr Weed - DEFENCE_POTION(new SecondaryItem(ItemID.WHITE_BERRIES)), - PRAYER_POTION(new SecondaryItem(ItemID.SNAPE_GRASS)), - // Toadflax - AGILITY_POTION(new SecondaryItem(ItemID.TOADS_LEGS)), - SARADOMIN_BREW(new SecondaryItem(ItemID.CRUSHED_NEST)), - // Irit - SUPER_ATTACK(new SecondaryItem(ItemID.EYE_OF_NEWT)), - SUPERANTIPOISON(new SecondaryItem(ItemID.UNICORN_HORN_DUST)), - // Avantoe - FISHING_POTION(new SecondaryItem(ItemID.SNAPE_GRASS)), - SUPER_ENERGY_POTION(new SecondaryItem(ItemID.MORT_MYRE_FUNGUS)), - HUNTER_POTION(new SecondaryItem(ItemID.KEBBIT_TEETH_DUST)), - // Kwuarm - SUPER_STRENGTH(new SecondaryItem(ItemID.LIMPWURT_ROOT)), - // Snapdragon - SUPER_RESTORE(new SecondaryItem(ItemID.RED_SPIDERS_EGGS)), - SANFEW_SERUM(new SecondaryItem(ItemID.SNAKE_WEED), new SecondaryItem(ItemID.UNICORN_HORN_DUST), new SecondaryItem(ItemID.SUPER_RESTORE4), new SecondaryItem(ItemID.NAIL_BEAST_NAILS)), - // Cadantine - SUPER_DEFENCE_POTION(new SecondaryItem(ItemID.WHITE_BERRIES)), - // Lantadyme - ANTIFIRE_POTION(new SecondaryItem(ItemID.DRAGON_SCALE_DUST)), - MAGIC_POTION(new SecondaryItem(ItemID.POTATO_CACTUS)), - // Dwarf Weed - RANGING_POTION(new SecondaryItem(ItemID.WINE_OF_ZAMORAK)), - // Torstol - ZAMORAK_BREW(new SecondaryItem(ItemID.JANGERBERRIES)), - SUPER_COMBAT_POTION(new SecondaryItem(ItemID.SUPER_ATTACK3), new SecondaryItem(ItemID.SUPER_STRENGTH3), new SecondaryItem(ItemID.SUPER_DEFENCE3)), - ANTIVENOM_PLUS(new SecondaryItem(ItemID.ANTIVENOM4)), - - /** - * Smithing - */ - COAL_ORE(new SecondaryItem(ItemID.COAL)), - COAL_ORE_2(new SecondaryItem(ItemID.COAL, 2)), - COAL_ORE_4(new SecondaryItem(ItemID.COAL, 4)), - COAL_ORE_6(new SecondaryItem(ItemID.COAL, 6)), - COAL_ORE_8(new SecondaryItem(ItemID.COAL, 8)), - - /** - * Crafting - */ - GOLD_BAR(new SecondaryItem(ItemID.GOLD_BAR)), - SILVER_BAR(new SecondaryItem(ItemID.SILVER_BAR)), - WATER_ORB(new SecondaryItem(ItemID.WATER_ORB)), - EARTH_ORB(new SecondaryItem(ItemID.EARTH_ORB)), - FIRE_ORB(new SecondaryItem(ItemID.FIRE_ORB)), - AIR_ORB(new SecondaryItem(ItemID.AIR_ORB)), - - /** - * Cooking - */ - JUG_OF_WATER(new SecondaryItem(ItemID.JUG_OF_WATER)), - ; - private final SecondaryItem[] items; - - ActivitySecondaries(final SecondaryItem... items) - { - this.items = items; - } -} diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect2Definition.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java similarity index 70% rename from cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect2Definition.java rename to runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java index 358d462f50..5256d26351 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect2Definition.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,27 +22,32 @@ * (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.cache.definitions.sound; +package net.runelite.client.plugins.skillcalculator.banked.beans; -public class SoundEffect2Definition +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class BankedItem { - public int field1085; - public int[] field1086 = new int[2]; - public int field1087; - public int field1088; - public int field1089; - public int[] field1090 = new int[2]; - public int field1091; - public int field1092 = 2; - public int field1093; - public int field1094; - public int field1095; + private final CriticalItem item; + private final int qty; - public SoundEffect2Definition() + public double getXpRate() { - this.field1086[0] = 0; - this.field1086[1] = '\uffff'; - this.field1090[0] = 0; - this.field1090[1] = '\uffff'; + final Activity selectedActivity = item.getSelectedActivity(); + if (selectedActivity == null) + { + return 0; + } + + return selectedActivity.getXp(); } -} + + @Override + public String toString() + { + return item.name() + " x " + qty; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java new file mode 100644 index 0000000000..d6f3752742 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.ItemDefinition; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.client.game.ItemManager; + +@Getter +public enum CriticalItem +{ + /** + * Construction Items + */ + // Logs + LOGS(ItemID.LOGS, Skill.CONSTRUCTION, "Logs"), + OAK_LOGS(ItemID.OAK_LOGS, Skill.CONSTRUCTION, "Logs"), + TEAK_LOGS(ItemID.TEAK_LOGS, Skill.CONSTRUCTION, "Logs"), + MAHOGANY_LOGS(ItemID.MAHOGANY_LOGS, Skill.CONSTRUCTION, "Logs"), + // Planks + PLANK(ItemID.PLANK, Skill.CONSTRUCTION, "Planks"), + OAK_PLANK(ItemID.OAK_PLANK, Skill.CONSTRUCTION, "Planks"), + TEAK_PLANK(ItemID.TEAK_PLANK, Skill.CONSTRUCTION, "Planks"), + MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, Skill.CONSTRUCTION, "Planks"), + + /** + * Herblore Items + */ + // Grimy Herbs + GRIMY_GUAM_LEAF(ItemID.GRIMY_GUAM_LEAF, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_MARRENTILL(ItemID.GRIMY_MARRENTILL, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_TARROMIN(ItemID.GRIMY_TARROMIN, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_HARRALANDER(ItemID.GRIMY_HARRALANDER, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_RANARR_WEED(ItemID.GRIMY_RANARR_WEED, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_TOADFLAX(ItemID.GRIMY_TOADFLAX, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_IRIT_LEAF(ItemID.GRIMY_IRIT_LEAF, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_AVANTOE(ItemID.GRIMY_AVANTOE, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_KWUARM(ItemID.GRIMY_KWUARM, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_SNAPDRAGON(ItemID.GRIMY_SNAPDRAGON, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_CADANTINE(ItemID.GRIMY_CADANTINE, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_LANTADYME(ItemID.GRIMY_LANTADYME, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_DWARF_WEED(ItemID.GRIMY_DWARF_WEED, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_TORSTOL(ItemID.GRIMY_TORSTOL, Skill.HERBLORE, "Grimy Herbs"), + // Clean Herbs + GUAM_LEAF(ItemID.GUAM_LEAF, Skill.HERBLORE, "Cleaned Herbs"), + MARRENTILL(ItemID.MARRENTILL, Skill.HERBLORE, "Cleaned Herbs"), + TARROMIN(ItemID.TARROMIN, Skill.HERBLORE, "Cleaned Herbs"), + HARRALANDER(ItemID.HARRALANDER, Skill.HERBLORE, "Cleaned Herbs"), + RANARR_WEED(ItemID.RANARR_WEED, Skill.HERBLORE, "Cleaned Herbs"), + TOADFLAX(ItemID.TOADFLAX, Skill.HERBLORE, "Cleaned Herbs"), + IRIT_LEAF(ItemID.IRIT_LEAF, Skill.HERBLORE, "Cleaned Herbs"), + AVANTOE(ItemID.AVANTOE, Skill.HERBLORE, "Cleaned Herbs"), + KWUARM(ItemID.KWUARM, Skill.HERBLORE, "Cleaned Herbs"), + SNAPDRAGON(ItemID.SNAPDRAGON, Skill.HERBLORE, "Cleaned Herbs"), + CADANTINE(ItemID.CADANTINE, Skill.HERBLORE, "Cleaned Herbs"), + LANTADYME(ItemID.LANTADYME, Skill.HERBLORE, "Cleaned Herbs"), + DWARF_WEED(ItemID.DWARF_WEED, Skill.HERBLORE, "Cleaned Herbs"), + TORSTOL(ItemID.TORSTOL, Skill.HERBLORE, "Cleaned Herbs"), + // Unfinished Potions + GUAM_LEAF_POTION_UNF(ItemID.GUAM_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + + /** + * Prayer Items + */ + // Bones + BONES(ItemID.BONES, Skill.PRAYER, "Bones"), + WOLF_BONES(ItemID.WOLF_BONES, Skill.PRAYER, "Bones"), + BURNT_BONES(ItemID.BURNT_BONES, Skill.PRAYER, "Bones"), + MONKEY_BONES(ItemID.MONKEY_BONES, Skill.PRAYER, "Bones"), + BAT_BONES(ItemID.BAT_BONES, Skill.PRAYER, "Bones"), + JOGRE_BONES(ItemID.JOGRE_BONES, Skill.PRAYER, "Bones"), + BIG_BONES(ItemID.BIG_BONES, Skill.PRAYER, "Bones"), + ZOGRE_BONES(ItemID.ZOGRE_BONES, Skill.PRAYER, "Bones"), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, Skill.PRAYER, "Bones"), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, Skill.PRAYER, "Bones"), + WYVERN_BONES(ItemID.WYVERN_BONES, Skill.PRAYER, "Bones"), + DRAGON_BONES(ItemID.DRAGON_BONES, Skill.PRAYER, "Bones"), + FAYRG_BONES(ItemID.FAYRG_BONES, Skill.PRAYER, "Bones"), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, Skill.PRAYER, "Bones"), + RAURG_BONES(ItemID.RAURG_BONES, Skill.PRAYER, "Bones"), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, Skill.PRAYER, "Bones"), + OURG_BONES(ItemID.OURG_BONES, Skill.PRAYER, "Bones"), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, Skill.PRAYER, "Bones"), + // Shade Remains (Pyre Logs) + LOAR_REMAINS(ItemID.LOAR_REMAINS, Skill.PRAYER, "Shades", true), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, Skill.PRAYER, "Shades", true), + RIYL_REMAINS(ItemID.RIYL_REMAINS, Skill.PRAYER, "Shades", true), + ASYN_REMAINS(ItemID.ASYN_REMAINS, Skill.PRAYER, "Shades", true), + FIYR_REMAINS(ItemID.FIYR_REMAINS, Skill.PRAYER, "Shades", true), + // Ensouled Heads + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, Skill.PRAYER, "Ensouled Heads", true), + + /** + * Cooking Items + */ + RAW_HERRING(ItemID.RAW_HERRING, Skill.COOKING, "Fish"), + RAW_MACKEREL(ItemID.RAW_MACKEREL, Skill.COOKING, "Fish"), + RAW_TROUT(ItemID.RAW_TROUT, Skill.COOKING, "Fish"), + RAW_COD(ItemID.RAW_COD, Skill.COOKING, "Fish"), + RAW_PIKE(ItemID.RAW_PIKE, Skill.COOKING, "Fish"), + RAW_SALMON(ItemID.RAW_SALMON, Skill.COOKING, "Fish"), + RAW_TUNA(ItemID.RAW_TUNA, Skill.COOKING, "Fish"), + RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, Skill.COOKING, "Fish"), + RAW_LOBSTER(ItemID.RAW_LOBSTER, Skill.COOKING, "Fish"), + RAW_BASS(ItemID.RAW_BASS, Skill.COOKING, "Fish"), + RAW_SWORDFISH(ItemID.RAW_SWORDFISH, Skill.COOKING, "Fish"), + RAW_MONKFISH(ItemID.RAW_MONKFISH, Skill.COOKING, "Fish"), + RAW_SHARK(ItemID.RAW_SHARK, Skill.COOKING, "Fish"), + RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, Skill.COOKING, "Fish"), + RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, Skill.COOKING, "Fish"), + RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, Skill.COOKING, "Fish"), + RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, Skill.COOKING, "Fish"), + + GRAPES(ItemID.GRAPES, Skill.COOKING, "Other"), + + /** + * Crafting Items + */ + WOOL(ItemID.WOOL, Skill.CRAFTING, "Misc"), + FLAX(ItemID.FLAX, Skill.CRAFTING, "Misc"), + MOLTEN_GLASS(ItemID.MOLTEN_GLASS, Skill.CRAFTING, "Misc"), + BATTLESTAFF(ItemID.BATTLESTAFF, Skill.CRAFTING, "Misc"), + + // D'hide/Dragon Leather + GREEN_DRAGONHIDE(ItemID.GREEN_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + BLUE_DRAGONHIDE(ItemID.BLUE_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + RED_DRAGONHIDE(ItemID.RED_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + BLACK_DRAGONHIDE(ItemID.BLACK_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + + // Uncut Gems + UNCUT_OPAL(ItemID.UNCUT_OPAL, Skill.CRAFTING, "Gems"), + UNCUT_JADE(ItemID.UNCUT_JADE, Skill.CRAFTING, "Gems"), + UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, Skill.CRAFTING, "Gems"), + UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, Skill.CRAFTING, "Gems"), + UNCUT_EMERALD(ItemID.UNCUT_EMERALD, Skill.CRAFTING, "Gems"), + UNCUT_RUBY(ItemID.UNCUT_RUBY, Skill.CRAFTING, "Gems"), + UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, Skill.CRAFTING, "Gems"), + UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, Skill.CRAFTING, "Gems"), + UNCUT_ONYX(ItemID.UNCUT_ONYX, Skill.CRAFTING, "Gems"), + UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, Skill.CRAFTING, "Gems"), + + // Cut Gems + OPAL(ItemID.OPAL, Skill.CRAFTING, "Gems"), + JADE(ItemID.JADE, Skill.CRAFTING, "Gems"), + RED_TOPAZ(ItemID.RED_TOPAZ, Skill.CRAFTING, "Gems"), + SAPPHIRE(ItemID.SAPPHIRE, Skill.CRAFTING, "Gems"), + EMERALD(ItemID.EMERALD, Skill.CRAFTING, "Gems"), + RUBY(ItemID.RUBY, Skill.CRAFTING, "Gems"), + DIAMOND(ItemID.DIAMOND, Skill.CRAFTING, "Gems"), + DRAGONSTONE(ItemID.DRAGONSTONE, Skill.CRAFTING, "Gems"), + ONYX(ItemID.ONYX, Skill.CRAFTING, "Gems"), + ZENYTE(ItemID.ZENYTE, Skill.CRAFTING, "Gems"), + + /** + * Smithing Items + */ + + // Ores + IRON_ORE(ItemID.IRON_ORE, Skill.SMITHING, "Ore"), + SILVER_ORE(ItemID.SILVER_ORE, Skill.SMITHING, "Ore"), + GOLD_ORE(ItemID.GOLD_ORE, Skill.SMITHING, "Ore"), + MITHRIL_ORE(ItemID.MITHRIL_ORE, Skill.SMITHING, "Ore"), + ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, Skill.SMITHING, "Ore"), + RUNITE_ORE(ItemID.RUNITE_ORE, Skill.SMITHING, "Ore"), + + // Bars + BRONZE_BAR(ItemID.BRONZE_BAR, Skill.SMITHING, "Bars"), + IRON_BAR(ItemID.IRON_BAR, Skill.SMITHING, "Bars"), + STEEL_BAR(ItemID.STEEL_BAR, Skill.SMITHING, "Bars"), + MITHRIL_BAR(ItemID.MITHRIL_BAR, Skill.SMITHING, "Bars"), + ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, Skill.SMITHING, "Bars"), + RUNITE_BAR(ItemID.RUNITE_BAR, Skill.SMITHING, "Bars"), + + /** + * Farming Items + */ + // Seeds + ACORN(ItemID.ACORN, Skill.FARMING, "Seeds"), + WILLOW_SEED(ItemID.WILLOW_SEED, Skill.FARMING, "Seeds"), + MAPLE_SEED(ItemID.MAPLE_SEED, Skill.FARMING, "Seeds"), + YEW_SEED(ItemID.YEW_SEED, Skill.FARMING, "Seeds"), + MAGIC_SEED(ItemID.MAGIC_SEED, Skill.FARMING, "Seeds"), + APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, Skill.FARMING, "Seeds"), + BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, Skill.FARMING, "Seeds"), + ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, Skill.FARMING, "Seeds"), + CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, Skill.FARMING, "Seeds"), + PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, Skill.FARMING, "Seeds"), + PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, Skill.FARMING, "Seeds"), + PALM_TREE_SEED(ItemID.PALM_TREE_SEED, Skill.FARMING, "Seeds"), + CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, Skill.FARMING, "Seeds"), + TEAK_SEED(ItemID.TEAK_SEED, Skill.FARMING, "Seeds"), + MAHOGANY_SEED(ItemID.MAHOGANY_SEED, Skill.FARMING, "Seeds"), + SPIRIT_SEED(ItemID.SPIRIT_SEED, Skill.FARMING, "Seeds"), + + // Saplings + OAK_SAPLING(ItemID.OAK_SAPLING, Skill.FARMING, "Saplings"), + WILLOW_SAPLING(ItemID.WILLOW_SAPLING, Skill.FARMING, "Saplings"), + MAPLE_SAPLING(ItemID.MAPLE_SAPLING, Skill.FARMING, "Saplings"), + YEW_SAPLING(ItemID.YEW_SAPLING, Skill.FARMING, "Saplings"), + MAGIC_SAPLING(ItemID.MAGIC_SAPLING, Skill.FARMING, "Saplings"), + APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, Skill.FARMING, "Saplings"), + BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, Skill.FARMING, "Saplings"), + ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, Skill.FARMING, "Saplings"), + CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, Skill.FARMING, "Saplings"), + PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, Skill.FARMING, "Saplings"), + PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, Skill.FARMING, "Saplings"), + PALM_TREE_SAPLING(ItemID.PALM_SAPLING, Skill.FARMING, "Saplings"), + CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, Skill.FARMING, "Saplings"), + TEAK_SAPLING(ItemID.TEAK_SAPLING, Skill.FARMING, "Saplings"), + MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, Skill.FARMING, "Saplings"), + SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, Skill.FARMING, "Saplings"), + ; + + private final int itemID; + private final Skill skill; + private final String category; + private boolean ignoreBonus; + + @Setter + // Stores the item composition info we use since we don't operate on the game thread + private ItemInfo itemInfo = null; + + @Setter + private Activity selectedActivity; + + private static final Multimap SKILL_MAP = ArrayListMultimap.create(); + private static final Map ITEM_ID_MAP = new HashMap<>(); + + static + { + for (CriticalItem i : values()) + { + Skill s = i.getSkill(); + SKILL_MAP.put(s, i); + ITEM_ID_MAP.put(i.getItemID(), i); + } + } + + CriticalItem(int itemID, Skill skill, String category, boolean ignoreBonus) + { + this.itemID = itemID; + this.category = category; + this.skill = skill; + this.ignoreBonus = ignoreBonus; + } + + CriticalItem(int itemID, Skill skill, String category) + { + this(itemID, skill, category, false); + } + + public static Collection getBySkill(Skill skill) + { + Collection items = SKILL_MAP.get(skill); + if (items == null) + { + items = new ArrayList<>(); + } + + return items; + } + + public static CriticalItem getByItemId(int id) + { + return ITEM_ID_MAP.get(id); + } + + /** + * Attaches the Item Composition to each CriticalItem on client initial load + * + * @param m ItemManager + */ + public static void prepareItemDefinitions(ItemManager m) + { + for (CriticalItem i : values()) + { + if (i.itemInfo != null) + { + return; + } + + final ItemDefinition c = m.getItemDefinition(i.getItemID()); + i.itemInfo = new ItemInfo(c.getName(), c.isStackable()); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemInfo.java similarity index 80% rename from runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java rename to runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemInfo.java index 0cc728d0f9..8bc3651ed9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, TheStonedTurtle + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,23 +24,13 @@ */ package net.runelite.client.plugins.skillcalculator.banked.beans; -import lombok.AccessLevel; -import lombok.Getter; +import lombok.AllArgsConstructor; +import lombok.Data; -@Getter(AccessLevel.PUBLIC) -public class SecondaryItem +@Data +@AllArgsConstructor +public class ItemInfo { - private final int id; - private final int qty; - - SecondaryItem(int id, int qty) - { - this.id = id; - this.qty = qty; - } - - SecondaryItem(int id) - { - this(id, 1); - } -} + private String name; + private boolean stackable; +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java similarity index 70% rename from runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java rename to runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java index 3c1418411a..3b785f4020 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Sean Dewar + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,22 +22,15 @@ * (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.runenergy; +package net.runelite.client.plugins.skillcalculator.banked.beans; -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; -import net.runelite.client.config.ConfigItem; +import lombok.AllArgsConstructor; +import lombok.Data; -@ConfigGroup("runenergy") -public interface RunEnergyConfig extends Config +@Data +@AllArgsConstructor +class ItemStack { - @ConfigItem( - keyName = "replaceOrbText", - name = "Replace orb text with run time left", - description = "Show the remaining run time (in seconds) next in the energy orb." - ) - default boolean replaceOrbText() - { - return false; - } -} + private int id; + private int qty; +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java new file mode 100644 index 0000000000..cab6b7474f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import lombok.Getter; +import net.runelite.api.ItemID; + +@Getter +public enum Secondaries +{ + /** + * Herblore + */ + UNFINISHED_POTION(new ItemStack(ItemID.VIAL_OF_WATER, 1)), + SWAMP_TAR(new ItemStack(ItemID.SWAMP_TAR, 15)), + // Guam + ATTACK_POTION(new ItemStack(ItemID.EYE_OF_NEWT, 1)), + // Marrentil + ANTIPOISON(new ItemStack(ItemID.UNICORN_HORN_DUST, 1)), + // Tarromin + STRENGTH_POTION(new ItemStack(ItemID.LIMPWURT_ROOT, 1)), + SERUM_207(new ItemStack(ItemID.ASHES, 1)), + // Harralander + COMPOST_POTION(new ItemStack(ItemID.VOLCANIC_ASH, 1)), + RESTORE_POTION(new ItemStack(ItemID.RED_SPIDERS_EGGS, 1)), + ENERGY_POTION(new ItemStack(ItemID.CHOCOLATE_DUST, 1)), + COMBAT_POTION(new ItemStack(ItemID.GOAT_HORN_DUST, 1)), + // Ranarr Weed + DEFENCE_POTION(new ItemStack(ItemID.WHITE_BERRIES, 1)), + PRAYER_POTION(new ItemStack(ItemID.SNAPE_GRASS, 1)), + // Toadflax + AGILITY_POTION(new ItemStack(ItemID.TOADS_LEGS, 1)), + SARADOMIN_BREW(new ItemStack(ItemID.CRUSHED_NEST, 1)), + // Irit + SUPER_ATTACK(new ItemStack(ItemID.EYE_OF_NEWT, 1)), + SUPERANTIPOISON(new ItemStack(ItemID.UNICORN_HORN_DUST, 1)), + // Avantoe + FISHING_POTION(new ItemStack(ItemID.SNAPE_GRASS, 1)), + SUPER_ENERGY_POTION(new ItemStack(ItemID.MORT_MYRE_FUNGUS, 1)), + HUNTER_POTION(new ItemStack(ItemID.KEBBIT_TEETH_DUST, 1)), + // Kwuarm + SUPER_STRENGTH(new ItemStack(ItemID.LIMPWURT_ROOT, 1)), + // Snapdragon + SUPER_RESTORE(new ItemStack(ItemID.RED_SPIDERS_EGGS, 1)), + SANFEW_SERUM(new ItemStack(ItemID.SNAKE_WEED, 1), new ItemStack(ItemID.UNICORN_HORN_DUST, 1), new ItemStack(ItemID.SUPER_RESTORE4, 1), new ItemStack(ItemID.NAIL_BEAST_NAILS, 1)), + // Cadantine + SUPER_DEFENCE_POTION(new ItemStack(ItemID.WHITE_BERRIES, 1)), + // Lantadyme + ANTIFIRE_POTION(new ItemStack(ItemID.DRAGON_SCALE_DUST, 1)), + MAGIC_POTION(new ItemStack(ItemID.POTATO_CACTUS, 1)), + // Dwarf Weed + RANGING_POTION(new ItemStack(ItemID.WINE_OF_ZAMORAK, 1)), + // Torstol + ZAMORAK_BREW(new ItemStack(ItemID.JANGERBERRIES, 1)), + SUPER_COMBAT_POTION(new ItemStack(ItemID.SUPER_ATTACK4, 1), new ItemStack(ItemID.SUPER_STRENGTH4, 1), new ItemStack(ItemID.SUPER_DEFENCE4, 1)), + ANTIVENOM_PLUS(new ItemStack(ItemID.ANTIVENOM4, 1)), + + /** + * Smithing + */ + COAL_ORE(new ItemStack(ItemID.COAL, 1)), + COAL_ORE_2(new ItemStack(ItemID.COAL, 2)), + COAL_ORE_4(new ItemStack(ItemID.COAL, 4)), + COAL_ORE_6(new ItemStack(ItemID.COAL, 6)), + COAL_ORE_8(new ItemStack(ItemID.COAL, 8)), + + /** + * Crafting + */ + GOLD_BAR(new ItemStack(ItemID.GOLD_BAR, 1)), + SILVER_BAR(new ItemStack(ItemID.SILVER_BAR, 1)), + WATER_ORB(new ItemStack(ItemID.WATER_ORB, 1)), + EARTH_ORB(new ItemStack(ItemID.EARTH_ORB, 1)), + FIRE_ORB(new ItemStack(ItemID.FIRE_ORB, 1)), + AIR_ORB(new ItemStack(ItemID.AIR_ORB, 1)), + + /** + * Construction + */ + COINS_100(new ItemStack(ItemID.COINS_995, 100)), + COINS_250(new ItemStack(ItemID.COINS_995, 250)), + COINS_500(new ItemStack(ItemID.COINS_995, 500)), + COINS_1500(new ItemStack(ItemID.COINS_995, 1500)), + + /** + * Cooking + */ + JUG_OF_WATER(new ItemStack(ItemID.JUG_OF_WATER, 1)), + ; + private final ItemStack[] items; + + Secondaries(ItemStack... items) + { + this.items = items; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java new file mode 100644 index 0000000000..b9fcae99c6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.Skill; + +@AllArgsConstructor +@Getter +public enum XpModifiers +{ + LIT_GILDER_ALTAR(Skill.PRAYER, "Lit Gilded Altar (350%)", 3.5f), + ECTOFUNTUS(Skill.PRAYER, "Ectofuntus (400%)", 4), + WILDY_ALTAR(Skill.PRAYER, "Wildy Altar (700%)", 7), + + FARMERS_OUTFIT(Skill.FARMING, "Farmer's Outfit (+2.5%)", 1.025f); + + private final Skill skill; + private final String name; + private final float modifier; + + private final static Multimap MODIFIERS_MAP; + + static + { + final ImmutableMultimap.Builder map = ImmutableMultimap.builder(); + for (final XpModifiers m : values()) + { + map.put(m.skill, m); + } + MODIFIERS_MAP = map.build(); + } + + public static Collection getModifiersBySkill(final Skill skill) + { + return MODIFIERS_MAP.get(skill); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java new file mode 100644 index 0000000000..9cb03ff36b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked.components; + +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.function.BooleanSupplier; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; +import net.runelite.client.ui.ColorScheme; + +@Getter(AccessLevel.PUBLIC) +public class GridItem extends JLabel +{ + private final static String IGNORE = "Ignore Item"; + private final static String INCLUDE = "Include Item"; + + private static final Color UNSELECTED_BACKGROUND = ColorScheme.DARKER_GRAY_COLOR; + private static final Color UNSELECTED_HOVER_BACKGROUND = ColorScheme.DARKER_GRAY_HOVER_COLOR; + + private static final Color SELECTED_BACKGROUND = new Color(0, 70, 0); + private static final Color SELECTED_HOVER_BACKGROUND = new Color(0, 100, 0); + + private static final Color IGNORED_BACKGROUND = new Color(90, 0, 0); + private static final Color IGNORED_HOVER_BACKGROUND = new Color(120, 0, 0); + + /* To be executed when this element is clicked */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onSelectEvent; + + /* To be executed when this element is ignored */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onIgnoreEvent; + + private final SelectionGrid parent; + private final BankedItem bankedItem; + private int amount; + + private boolean selected = false; + private boolean ignored = false; + + private final JMenuItem IGNORE_OPTION = new JMenuItem(IGNORE); + + GridItem(final SelectionGrid parent, final BankedItem item, final AsyncBufferedImage icon, final int amount) + { + super(""); + + this.parent = parent; + this.bankedItem = item; + + this.setOpaque(true); + this.setBackground(ColorScheme.DARKER_GRAY_COLOR); + this.setBorder(BorderFactory.createEmptyBorder(5, 0, 2, 0)); + + this.setVerticalAlignment(SwingConstants.CENTER); + this.setHorizontalAlignment(SwingConstants.CENTER); + + updateIcon(icon, amount); + updateToolTip(); + + this.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (mouseEvent.getButton() == MouseEvent.BUTTON1) + { + select(); + } + } + + @Override + public void mouseEntered(MouseEvent e) + { + final GridItem item = (GridItem) e.getSource(); + item.setBackground(getHoverBackgroundColor()); + } + + @Override + public void mouseExited(MouseEvent e) + { + final GridItem item = (GridItem) e.getSource(); + item.setBackground(getBackgroundColor()); + } + }); + + IGNORE_OPTION.addActionListener(e -> + { + // Update ignored flag now so event knows new state + this.ignored = !this.ignored; + + if (onIgnoreEvent != null && !onIgnoreEvent.getAsBoolean()) + { + // Reset state + this.ignored = !this.ignored; + return; + } + + IGNORE_OPTION.setText(this.ignored ? INCLUDE : IGNORE); + this.setBackground(getBackgroundColor()); + }); + + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(IGNORE_OPTION); + + this.setComponentPopupMenu(popupMenu); + } + + private Color getBackgroundColor() + { + return ignored ? IGNORED_BACKGROUND : (selected ? SELECTED_BACKGROUND : UNSELECTED_BACKGROUND); + } + + private Color getHoverBackgroundColor() + { + return ignored ? IGNORED_HOVER_BACKGROUND : (selected ? SELECTED_HOVER_BACKGROUND : UNSELECTED_HOVER_BACKGROUND); + } + + public void select() + { + if (onSelectEvent != null && !onSelectEvent.getAsBoolean()) + { + return; + } + + selected = true; + setBackground(getBackgroundColor()); + } + + void unselect() + { + selected = false; + setBackground(getBackgroundColor()); + } + + public void updateIcon(final AsyncBufferedImage icon, final int amount) + { + icon.addTo(this); + this.amount = amount; + } + + public void updateToolTip() + { + this.setToolTipText(buildToolTip()); + } + + private String buildToolTip() + { + String tip = "" + bankedItem.getItem().getItemInfo().getName(); + + final Activity a = bankedItem.getItem().getSelectedActivity(); + if (a != null) + { + final double xp = parent.getCalc().getItemXpRate(bankedItem); + tip += "
Activity: " + a.getName(); + tip += "
Xp/Action: " + BankedCalculator.XP_FORMAT_COMMA.format(xp); + tip += "
Total Xp: " + BankedCalculator.XP_FORMAT_COMMA.format(xp * amount); + } + else + { + tip += "
Outputs: " + bankedItem.getItem().getItemInfo().getName(); + } + + return tip + ""; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java new file mode 100644 index 0000000000..c111789377 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.components; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ItemEvent; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Constants; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.CriticalItem; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.ComboBoxIconEntry; +import net.runelite.client.ui.components.ComboBoxListRenderer; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +public class ModifyPanel extends JPanel +{ + private static final Dimension ICON_SIZE = new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT); + private static final DecimalFormat FORMAT_COMMA = new DecimalFormat("#,###.#"); + + private static final Border PANEL_BORDER = new EmptyBorder(3, 0, 3, 0); + private static final Color BACKGROUND_COLOR = ColorScheme.DARKER_GRAY_COLOR; + + private final BankedCalculator calc; + private final ItemManager itemManager; + + @Getter(AccessLevel.PUBLIC) + private BankedItem bankedItem; + private Map linkedMap; + @Getter(AccessLevel.PUBLIC) + private int amount = 0; + @Getter(AccessLevel.PUBLIC) + private double total = 0; + + // Banked item information display + private final JPanel labelContainer; + private final JLabel image; + private final JShadowedLabel labelName; + private final JShadowedLabel labelValue; + + // Elements used to adjust banked item + private final JPanel adjustContainer; + + public ModifyPanel(final BankedCalculator calc, final ItemManager itemManager) + { + this.calc = calc; + this.itemManager = itemManager; + + this.setLayout(new GridBagLayout()); + this.setBorder(PANEL_BORDER); + this.setBackground(ColorScheme.DARK_GRAY_COLOR); + + // Banked item information display + labelContainer = new JPanel(); + labelContainer.setLayout(new BorderLayout()); + labelContainer.setBackground(BACKGROUND_COLOR); + labelContainer.setBorder(new EmptyBorder(5, 0, 5, 0)); + + // Icon + image = new JLabel(); + image.setMinimumSize(ICON_SIZE); + image.setMaximumSize(ICON_SIZE); + image.setPreferredSize(ICON_SIZE); + image.setHorizontalAlignment(SwingConstants.CENTER); + image.setBorder(new EmptyBorder(0, 8, 0, 0)); + + // Wrapper panel for the shadowed labels + final JPanel uiInfo = new JPanel(new GridLayout(2, 1)); + uiInfo.setBorder(new EmptyBorder(0, 5, 0, 0)); + uiInfo.setBackground(BACKGROUND_COLOR); + + labelName = new JShadowedLabel(); + labelName.setForeground(Color.WHITE); + labelName.setVerticalAlignment(SwingUtilities.BOTTOM); + + labelValue = new JShadowedLabel(); + labelValue.setFont(FontManager.getRunescapeSmallFont()); + labelValue.setVerticalAlignment(SwingUtilities.TOP); + + uiInfo.add(labelName); + uiInfo.add(labelValue); + + // Append elements to item info panel + labelContainer.add(image, BorderLayout.LINE_START); + labelContainer.add(uiInfo, BorderLayout.CENTER); + + // Container for tools to adjust banked calculation for this item + adjustContainer = new JPanel(); + adjustContainer.setLayout(new GridBagLayout()); + adjustContainer.setBackground(BACKGROUND_COLOR); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.gridx = 0; + c.gridy = 0; + c.ipady = 0; + + this.add(labelContainer, c); + c.gridy++; + this.add(adjustContainer, c); + } + + // Updates the UI for the selected item + public void setBankedItem(final BankedItem bankedItem) + { + if (bankedItem == null) + { + return; + } + + this.bankedItem = bankedItem; + if (this.calc.getConfig().cascadeBankedXp()) + { + this.linkedMap = this.calc.createLinksMap(bankedItem); + + this.amount = bankedItem.getQty(); + for (int i : linkedMap.values()) + { + this.amount += i; + } + } + else + { + this.linkedMap = new HashMap<>(); + this.amount = this.calc.getItemQty(bankedItem); + } + + updateImageTooltip(); + updateLabelContainer(); + updateAdjustContainer(); + } + + private void updateImageTooltip() + { + final StringBuilder b = new StringBuilder(""); + b.append(bankedItem.getQty()).append(" x ").append(bankedItem.getItem().getItemInfo().getName()); + + for (final Map.Entry e : this.linkedMap.entrySet()) + { + b.append("
").append(e.getValue()).append(" x ").append(e.getKey().getItemInfo().getName()); + } + + b.append(""); + this.image.setToolTipText(b.toString()); + } + + private void updateLabelContainer() + { + final CriticalItem item = bankedItem.getItem(); + + // Update image icon + final boolean stackable = item.getItemInfo().isStackable() || amount > 1; + final AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, stackable); + final Runnable resize = () -> image.setIcon(new ImageIcon(icon.getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, Image.SCALE_SMOOTH))); + icon.onChanged(resize); + resize.run(); + + final String itemName = item.getItemInfo().getName(); + labelName.setText(itemName); + + double xp = calc.getItemXpRate(bankedItem); + total = amount * xp; + + final String value = FORMAT_COMMA.format(total) + "xp"; + labelValue.setText(value); + + labelContainer.setToolTipText("" + itemName + + "
xp: " + xp + + "
Total: " + total + " activities = Activity.getByCriticalItem(bankedItem.getItem(), calc.getSkillLevel()); + if (activities == null || activities.size() == 0) + { + adjustContainer.add(new JLabel("Unknown")); + } + else if (activities.size() == 1) + { + final Activity a = activities.get(0); + + final AsyncBufferedImage img = itemManager.getImage(a.getIcon()); + final ImageIcon icon = new ImageIcon(img); + final double xp = a.getXp() * xpFactor; + final JPanel container = createShadowedLabel(icon, a.getName(), FORMAT_COMMA.format(xp) + "xp"); + + img.onChanged(() -> + { + icon.setImage(img); + container.repaint(); + }); + + adjustContainer.add(container, c); + } + else + { + final JComboBox dropdown = new JComboBox<>(); + final ComboBoxListRenderer renderer = new ComboBoxListRenderer(); + dropdown.setRenderer(renderer); + + for (final Activity option : activities) + { + final double xp = option.getXp() * xpFactor; + String name = option.getName(); + if (xp > 0) + { + name += " (" + FORMAT_COMMA.format(xp) + "xp)"; + } + + final AsyncBufferedImage img = itemManager.getImage(option.getIcon()); + final ImageIcon icon = new ImageIcon(img); + final ComboBoxIconEntry entry = new ComboBoxIconEntry(icon, name, option); + dropdown.addItem(entry); + + img.onChanged(() -> + { + icon.setImage(img); + dropdown.revalidate(); + dropdown.repaint(); + }); + + final Activity selected = bankedItem.getItem().getSelectedActivity(); + if (option.equals(selected)) + { + dropdown.setSelectedItem(entry); + } + } + + // Add click event handler now to prevent above code from triggering it. + dropdown.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED && e.getItem() instanceof ComboBoxIconEntry) + { + final ComboBoxIconEntry source = (ComboBoxIconEntry) e.getItem(); + if (source.getData() instanceof Activity) + { + final Activity selectedActivity = ((Activity) source.getData()); + calc.activitySelected(bankedItem, selectedActivity); + updateLabelContainer(); + } + } + }); + + adjustContainer.add(dropdown, c); + } + } + + private JPanel createShadowedLabel(final ImageIcon icon, final String name, final String value) + { + // Wrapper panel for the shadowed labels + final JPanel wrapper = new JPanel(new GridLayout(2, 1)); + wrapper.setBorder(new EmptyBorder(0, 5, 0, 0)); + wrapper.setBackground(BACKGROUND_COLOR); + + final JShadowedLabel nameLabel = new JShadowedLabel(name); + nameLabel.setForeground(Color.WHITE); + nameLabel.setVerticalAlignment(SwingUtilities.BOTTOM); + + final JShadowedLabel valueLabel = new JShadowedLabel(value); + valueLabel.setFont(FontManager.getRunescapeSmallFont()); + valueLabel.setVerticalAlignment(SwingUtilities.TOP); + + wrapper.add(nameLabel); + wrapper.add(valueLabel); + + final JPanel container = new JPanel(); + container.setLayout(new BorderLayout()); + container.setBackground(BACKGROUND_COLOR); + container.setBorder(new EmptyBorder(5, 0, 5, 0)); + + final JLabel image = new JLabel(); + image.setMinimumSize(ICON_SIZE); + image.setMaximumSize(ICON_SIZE); + image.setPreferredSize(ICON_SIZE); + image.setHorizontalAlignment(SwingConstants.CENTER); + image.setBorder(new EmptyBorder(0, 8, 0, 0)); + + image.setIcon(icon); + + container.add(image, BorderLayout.LINE_START); + container.add(wrapper, BorderLayout.CENTER); + + return container; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java new file mode 100644 index 0000000000..2449857920 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.components; + +import java.awt.GridLayout; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; +import javax.swing.JPanel; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; + +/** + * A grid that supports mouse events + */ +public class SelectionGrid extends JPanel +{ + private static final int ITEMS_PER_ROW = 5; + + @Getter(AccessLevel.PUBLIC) + private final Map panelMap = new LinkedHashMap<>(); + + @Getter(AccessLevel.PUBLIC) + private BankedItem selectedItem; + + @Getter(AccessLevel.PUBLIC) + private BankedItem lastIgnoredItem; + + /* To be executed when this element is clicked */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onSelectEvent; + + /* To be executed when this element is ignored */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onIgnoreEvent; + + @Getter(AccessLevel.PUBLIC) + private final BankedCalculator calc; + + public SelectionGrid(final BankedCalculator calc, final Collection items, final ItemManager itemManager) + { + this.calc = calc; + // Create a panel for every item + for (final BankedItem item : items) + { + final int qty = calc.getItemQty(item); + final boolean stackable = item.getItem().getItemInfo().isStackable() || qty > 1; + final AsyncBufferedImage img = itemManager.getImage(item.getItem().getItemID(), qty, stackable); + + final GridItem gridItem = new GridItem(this, item, img, qty); + + gridItem.setOnSelectEvent(() -> selected(item)); + gridItem.setOnIgnoreEvent(() -> ignore(item)); + panelMap.put(item, gridItem); + } + + refreshGridDisplay(); + } + + public void refreshGridDisplay() + { + this.removeAll(); + + final List items = panelMap.values().stream().filter(gi -> gi.getAmount() > 0).collect(Collectors.toList()); + + // Calculates how many rows need to be display to fit all items + final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW; + setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1)); + + for (final GridItem gridItem : items) + { + // Select the first option + if (selectedItem == null) + { + gridItem.select(); + } + + this.add(gridItem); + } + } + + private boolean selected(final BankedItem item) + { + final BankedItem old = this.selectedItem; + if (item.equals(old)) + { + return false; + } + + // Set selected item now so the boolean can see what was just clicked + this.selectedItem = item; + if (onSelectEvent != null && !onSelectEvent.getAsBoolean()) + { + this.selectedItem = old; + return false; + } + + final GridItem gridItem = panelMap.get(old); + if (gridItem != null) + { + gridItem.unselect(); + } + + return true; + } + + private boolean ignore(final BankedItem item) + { + this.lastIgnoredItem = item; + return onIgnoreEvent.getAsBoolean(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java deleted file mode 100644 index 1b12419ec0..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator.banked.ui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.Image; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.text.DecimalFormat; -import java.util.List; -import java.util.Map; -import javax.imageio.ImageIO; -import javax.inject.Singleton; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.border.MatteBorder; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.client.game.AsyncBufferedImage; -import net.runelite.client.game.ItemManager; -import net.runelite.client.plugins.skillcalculator.BankedCalculator; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; -import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.components.materialtabs.MaterialTab; -import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -import net.runelite.client.util.StackFormatter; - -@Singleton -public class CriticalItemPanel extends JPanel -{ - private static final Dimension ICON_SIZE = new Dimension(36, 36); - private static final DecimalFormat FORMAT_COMMA = new DecimalFormat("#,###.#"); - - private static final BufferedImage ICON_SETTINGS; - - private static final Border PANEL_BORDER = new EmptyBorder(3, 0, 3, 0); - private final static Color BACKGROUND_COLOR = ColorScheme.DARKER_GRAY_COLOR; - private final static Color BUTTON_HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR; - - static - { - BufferedImage i1; - try - { - synchronized (ImageIO.class) - { - i1 = ImageIO.read(BankedCalculator.class.getResourceAsStream("view-more-white.png")); - } - } - catch (IOException e) - { - throw new RuntimeException(e); - } - ICON_SETTINGS = i1; - } - - private final BankedCalculator bankedCalculator; - private final CriticalItem item; - private final ItemManager itemManager; - private double xp; - @Getter(AccessLevel.PUBLIC) - private int amount; - @Getter(AccessLevel.PUBLIC) - private double total; - private Map linkedMap; - private JShadowedLabel labelValue; - - private final JPanel infoContainer; - private final JLabel image; - private boolean infoVisibility = false; - - public CriticalItemPanel(BankedCalculator bankedCalculator, ItemManager itemManager, CriticalItem item, double xp, int amount, Map linkedMap) - { - this.bankedCalculator = bankedCalculator; - this.item = item; - this.xp = xp; - this.amount = amount; - this.total = xp * amount; - this.itemManager = itemManager; - this.linkedMap = linkedMap; - - this.setLayout(new GridBagLayout()); - this.setBorder(PANEL_BORDER); - this.setBackground(ColorScheme.DARK_GRAY_COLOR); - this.setVisible(this.amount > 0); - - infoContainer = new JPanel(); - infoContainer.setLayout(new GridBagLayout()); - infoContainer.setVisible(false); - infoContainer.setBackground(BACKGROUND_COLOR); - infoContainer.setBorder(new MatteBorder(1, 0, 0, 0, Color.GRAY)); - - // Icon - AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, item.getDefinition().isStackable() || amount > 1); - image = new JLabel(); - image.setMinimumSize(ICON_SIZE); - image.setMaximumSize(ICON_SIZE); - image.setPreferredSize(ICON_SIZE); - image.setHorizontalAlignment(SwingConstants.CENTER); - image.setBorder(new EmptyBorder(0, 8, 0, 0)); - - Runnable resize = () -> - image.setIcon(new ImageIcon(icon.getScaledInstance((int) ICON_SIZE.getWidth(), (int) ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - - // Container for Info - JPanel uiInfo = new JPanel(new GridLayout(2, 1)); - uiInfo.setBorder(new EmptyBorder(0, 5, 0, 0)); - uiInfo.setBackground(BACKGROUND_COLOR); - - JShadowedLabel labelName = new JShadowedLabel(item.getDefinition().getName()); - labelName.setForeground(Color.WHITE); - labelName.setVerticalAlignment(SwingUtilities.BOTTOM); - - labelValue = new JShadowedLabel(); - labelValue.setFont(FontManager.getRunescapeSmallFont()); - labelValue.setVerticalAlignment(SwingUtilities.TOP); - updateXp(xp); - - uiInfo.add(labelName); - uiInfo.add(labelValue); - - // Settings Button - JLabel settingsButton = new JLabel(); - settingsButton.setBorder(new EmptyBorder(0, 5, 0, 5)); - settingsButton.setIcon(new ImageIcon(ICON_SETTINGS)); - settingsButton.setOpaque(true); - settingsButton.setBackground(BACKGROUND_COLOR); - - settingsButton.addMouseListener(new MouseAdapter() - { - @Override - public void mouseEntered(MouseEvent e) - { - settingsButton.setBackground(BUTTON_HOVER_COLOR); - } - - @Override - public void mouseExited(MouseEvent e) - { - settingsButton.setBackground(BACKGROUND_COLOR); - } - - @Override - public void mouseClicked(MouseEvent e) - { - toggleInfo(); - } - }); - - // Create and append elements to container panel - JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - panel.setBackground(BACKGROUND_COLOR); - - panel.add(image, BorderLayout.LINE_START); - panel.add(uiInfo, BorderLayout.CENTER); - - // Only add button if has activity selection options or linked items - List activities = Activity.getByCriticalItem(item); - // If linked map has 1 item and it isn't this item still show breakdown (cleaned herbs into unfinished) - if ((linkedMap.size() > 1 || (linkedMap.size() == 1 && linkedMap.get(item) == null)) - || activities.size() > 1) - { - panel.add(settingsButton, BorderLayout.LINE_END); - } - - panel.setToolTipText("" + item.getDefinition().getName() - + "
xp: " + xp - + "
Total: " + StackFormatter.quantityToStackSize((long) total) + " 1 || (linkedMap.size() == 1 && linkedMap.get(item) == null)) - { - JLabel l = new JLabel("Item Breakdown"); - l.setBorder(new EmptyBorder(3, 0, 3, 0)); - l.setHorizontalAlignment(JLabel.CENTER); - infoContainer.add(l, c); - c.gridy++; - - JPanel con = new JPanel(); - con.setLayout(new GridBagLayout()); - con.setBackground(BACKGROUND_COLOR); - for (Map.Entry e : linkedMap.entrySet()) - { - // Icon - AsyncBufferedImage icon = itemManager.getImage(e.getKey().getItemID(), e.getValue(), e.getKey().getDefinition().isStackable() || e.getValue() > 1); - JLabel image = new JLabel(); - image.setMinimumSize(ICON_SIZE); - image.setMaximumSize(ICON_SIZE); - image.setPreferredSize(ICON_SIZE); - image.setHorizontalAlignment(SwingConstants.CENTER); - image.setBorder(new EmptyBorder(0, 8, 0, 0)); - - Runnable resize = () -> - image.setIcon(new ImageIcon(icon.getScaledInstance((int) ICON_SIZE.getWidth(), (int) ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - - image.setToolTipText(e.getKey().getDefinition().getName()); - - con.add(image, c); - c.gridx++; - } - c.gridx = 0; - infoContainer.add(con, c); - } - - } - - private JPanel createActivitiesPanel() - { - List activities = Activity.getByCriticalItem(item); - if (activities == null || activities.size() == 1) - { - return null; - } - - JPanel p = new JPanel(); - p.setBackground(BACKGROUND_COLOR); - p.setLayout(new BorderLayout()); - - JLabel label = new JLabel("Possible training methods"); - - MaterialTabGroup group = new MaterialTabGroup(); - group.setLayout(new GridLayout(0, 6, 0, 2)); - group.setBorder(new MatteBorder(1, 1, 1, 1, Color.BLACK)); - - Activity selected = this.bankedCalculator.getSelectedActivity(this.item); - boolean s = false; - - for (Activity option : activities) - { - AsyncBufferedImage icon = itemManager.getImage(option.getIcon()); - MaterialTab matTab = new MaterialTab("", group, null); - matTab.setHorizontalAlignment(SwingUtilities.RIGHT); - matTab.setToolTipText(option.getName()); - - Runnable resize = () -> - matTab.setIcon(new ImageIcon(icon.getScaledInstance(24, 24, Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - - group.addTab(matTab); - - // Select first option by default - if (!s) - { - s = true; - group.select(matTab); - } - - // Select the option if its their selected activity - if (option.equals(selected)) - { - group.select(matTab); - } - - // Add click event handler now to prevent above code from triggering it. - matTab.setOnSelectEvent(() -> - { - bankedCalculator.activitySelected(item, option); - return true; - }); - } - - p.add(label, BorderLayout.NORTH); - p.add(group, BorderLayout.SOUTH); - - return p; - } - - public void updateXp(double newXpRate) - { - xp = newXpRate; - total = xp * amount; - labelValue.setText(FORMAT_COMMA.format(total) + "xp"); - } - - public void updateAmount(int newAmount, boolean forceVisible) - { - this.setVisible(newAmount > 0 || forceVisible); - this.amount = newAmount; - AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, item.getDefinition().isStackable() || amount > 1); - Runnable resize = () -> - image.setIcon(new ImageIcon(icon.getScaledInstance((int) ICON_SIZE.getWidth(), (int) ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - } - - public void updateLinkedMap(Map newLinkedMap) - { - this.linkedMap = newLinkedMap; - - int sum = 0; - for (Integer v : newLinkedMap.values()) - { - sum += v; - } - this.updateAmount(sum, false); - - this.updateXp(xp); - - // Refresh info panel if visible - if (infoVisibility) - { - createInfoPanel(); - } - } - - public void recalculate() - { - updateXp(xp); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java index 0d5b1cad40..4c2d36aa68 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java @@ -24,10 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.beans; -import lombok.AccessLevel; import lombok.Getter; -@Getter(AccessLevel.PUBLIC) +@Getter public class SkillData { private SkillDataEntry[] actions; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java index 6cce75a62e..675fcb51db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java @@ -24,10 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.beans; -import lombok.AccessLevel; import lombok.Getter; -@Getter(AccessLevel.PUBLIC) +@Getter public class SkillDataBonus { private String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java index 05ec9c878a..b21188d913 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java @@ -24,10 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.beans; -import lombok.AccessLevel; import lombok.Getter; -@Getter(AccessLevel.PUBLIC) +@Getter public class SkillDataEntry { private String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index 07e68ee198..bbb246f495 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -166,6 +166,17 @@ public interface SlayerConfig extends Config return true; } + @ConfigItem( + position = 14, + keyName = "pointsCommand", + name = "Points Command", + description = "Configures whether the slayer points command is enabled
!points" + ) + default boolean pointsCommand() + { + return true; + } + // Stored data @ConfigItem( keyName = "taskName", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index 2c9e181905..eee2b5beeb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -139,6 +139,7 @@ public class SlayerPlugin extends Plugin private static final String TASK_COMMAND_STRING = "!task"; private static final Pattern TASK_STRING_VALIDATION = Pattern.compile("[^a-zA-Z0-9' -]"); private static final int TASK_STRING_MAX_LENGTH = 50; + private static final String POINTS_COMMAND_STRING = "!points"; // Superiors @VisibleForTesting @@ -280,6 +281,8 @@ public class SlayerPlugin extends Plugin private boolean taskCommand; private String taskName; private String taskLocation; + @Setter(AccessLevel.PACKAGE) + private boolean pointsCommand; private int amount; private int initialAmount; private int lastCertainAmount; @@ -316,6 +319,8 @@ public class SlayerPlugin extends Plugin clientToolbar.addNavigation(navButton); chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit); + + chatCommandManager.registerCommandAsync(POINTS_COMMAND_STRING, this::pointsLookup); //here } @Override @@ -329,6 +334,7 @@ public class SlayerPlugin extends Plugin clearTrackedNPCs(); chatCommandManager.unregisterCommand(TASK_COMMAND_STRING); + chatCommandManager.unregisterCommand(POINTS_COMMAND_STRING); clientToolbar.removeNavigation(navButton); } @@ -1121,6 +1127,44 @@ public class SlayerPlugin extends Plugin client.refreshChat(); } + void pointsLookup(ChatMessage chatMessage, String message) + { + if (!this.pointsCommand) + { + return; + } + + ChatMessageType type = chatMessage.getType(); + + final String player; + if (type.equals(ChatMessageType.PRIVATECHATOUT)) + { + player = client.getLocalPlayer().getName(); + } + else + { + player = Text.removeTags(chatMessage.getName()) + .replace('\u00A0', ' '); + } + + if (Integer.toString(getPoints()) == null) + { + return; + } + + String response = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Slayer Points: ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString(getPoints())) + .build(); + + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setRuneLiteFormatMessage(response); + chatMessageManager.update(messageNode); + client.refreshChat(); + } + /* package access method for changing the pause state of the time tracker for the current task */ void setPaused(boolean paused) { @@ -1208,6 +1252,7 @@ public class SlayerPlugin extends Plugin this.drawMinimapNames = config.drawMinimapNames(); this.weaknessPrompt = config.weaknessPrompt(); this.taskCommand = config.taskCommand(); + this.pointsCommand = config.pointsCommand(); this.taskName = config.taskName(); this.amount = config.amount(); this.initialAmount = config.initialAmount(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsConfig.java similarity index 56% rename from runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java rename to runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsConfig.java index 2e8b577381..c05c47645d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsConfig.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Abex + * Copyright (c) 2018, Sean Dewar * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,29 +23,47 @@ * (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.regenmeter; +package net.runelite.client.plugins.statusorbs; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Stub; -@ConfigGroup("regenmeter") -public interface RegenMeterConfig extends Config +@ConfigGroup("statusorbs") +public interface StatusOrbsConfig extends Config { @ConfigItem( - keyName = "showHitpoints", - name = "Show hitpoints regen", - description = "Show a ring around the hitpoints orb") - default boolean showHitpoints() + keyName = "hp", + name = "Hitpoints", + description = "", + position = 0 + ) + default Stub hp() + { + return new Stub(); + } + + @ConfigItem( + keyName = "dynamicHpHeart", + name = "Dynamic hitpoints heart", + description = "Changes the HP heart color to match players current affliction", + parent = "hp", + position = 1 + ) + default boolean dynamicHpHeart() { return true; } @ConfigItem( - keyName = "showSpecial", - name = "Show Spec. Attack regen", - description = "Show a ring around the Special Attack orb") - default boolean showSpecial() + keyName = "showHitpoints", + name = "Show hitpoints regen", + description = "Show a ring around the hitpoints orb", + parent = "hp", + position = 2 + ) + default boolean showHitpoints() { return true; } @@ -52,7 +71,10 @@ public interface RegenMeterConfig extends Config @ConfigItem( keyName = "showWhenNoChange", name = "Show hitpoints regen at full hitpoints", - description = "Always show the hitpoints regen orb, even if there will be no stat change") + description = "Always show the hitpoints regen orb, even if there will be no stat change", + parent = "hp", + position = 3 + ) default boolean showWhenNoChange() { return false; @@ -61,10 +83,70 @@ public interface RegenMeterConfig extends Config @ConfigItem( keyName = "notifyBeforeHpRegenDuration", name = "Hitpoint Regen Notification (seconds)", - description = "Notify approximately when your next hitpoint is about to regen. A value of 0 will disable notification." + description = "Notify approximately when your next hitpoint is about to regen. A value of 0 will disable notification.", + parent = "hp", + position = 4 ) default int getNotifyBeforeHpRegenSeconds() { return 0; } -} + + @ConfigItem( + keyName = "spec", + name = "Special attack", + description = "", + position = 5 + ) + default Stub spec() + { + return new Stub(); + } + + @ConfigItem( + keyName = "showSpecial", + name = "Show Spec. Attack regen", + description = "Show a ring around the Special Attack orb", + parent = "spec", + position = 6 + ) + default boolean showSpecial() + { + return true; + } + + @ConfigItem( + keyName = "run", + name = "Run energy", + description = "", + position = 7 + ) + default Stub run() + { + return new Stub(); + } + + @ConfigItem( + keyName = "showRun", + name = "Show run energy regen", + description = "Show a ring around the run regen orb", + position = 8, + parent = "run" + ) + default boolean showRun() + { + return true; + } + + @ConfigItem( + keyName = "replaceOrbText", + name = "Replace run orb text with run time left", + description = "Show the remaining run time (in seconds) next in the energy orb", + position = 9, + parent = "run" + ) + default boolean replaceOrbText() + { + return false; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java new file mode 100644 index 0000000000..901b2aa771 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2018 Abex + * Copyright (c) 2018, Sean Dewar + * 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.statusorbs; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.Arc2D; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Point; +import net.runelite.api.Skill; +import net.runelite.api.VarPlayer; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +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.tooltip.Tooltip; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; +import net.runelite.client.util.Graceful; +import org.apache.commons.lang3.StringUtils; + +public class StatusOrbsOverlay extends Overlay +{ + private static final Color HITPOINTS_COLOR = brighter(0x9B0703); + private static final Color SPECIAL_COLOR = brighter(0x1E95B0); + private static final Color RUN_COLOR = new Color(255, 215, 0); + private static final Color OVERLAY_COLOR = new Color(255, 255, 255, 60); + private static final double DIAMETER = 26D; + private static final int OFFSET = 27; + + private final Client client; + private final StatusOrbsPlugin plugin; + private final TooltipManager tooltipManager; + + private long last = System.nanoTime(); + private double percentHp; + private double lastHp; + private double percentSpec; + private double lastSpec; + private double percentRun; + private double lastRun; + + private static Color brighter(int color) + { + float[] hsv = new float[3]; + Color.RGBtoHSB(color >>> 16, (color >> 8) & 0xFF, color & 0xFF, hsv); + return Color.getHSBColor(hsv[0], 1.f, 1.f); + } + + @Inject + public StatusOrbsOverlay(Client client, StatusOrbsPlugin plugin, TooltipManager tooltipManager) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.plugin = plugin; + this.tooltipManager = tooltipManager; + } + + @Override + public Dimension render(Graphics2D g) + { + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + + long current = System.nanoTime(); + double ms = (current - last) / (double) 1000000; + + if (plugin.isShowHitpoints()) + { + if (lastHp == plugin.getHitpointsPercentage() && plugin.getHitpointsPercentage() != 0) + { + percentHp += ms * plugin.getHpPerMs(); + } + else + { + percentHp = plugin.getHitpointsPercentage(); + lastHp = plugin.getHitpointsPercentage(); + } + renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, percentHp, HITPOINTS_COLOR); + } + + if (plugin.isShowSpecial()) + { + if (client.getVar(VarPlayer.SPECIAL_ATTACK_ENABLED) == 1) + { + final Widget widget = client.getWidget(WidgetInfo.MINIMAP_SPEC_ORB); + + if (widget != null && !widget.isHidden()) + { + final Rectangle bounds = widget.getBounds(); + g.setColor(OVERLAY_COLOR); + g.fillOval( + bounds.x + OFFSET, + bounds.y + (int) (bounds.height / 2 - (DIAMETER) / 2), + (int) DIAMETER, (int) DIAMETER); + } + } + + if (lastSpec == plugin.getSpecialPercentage() && plugin.getSpecialPercentage() != 0) + { + percentSpec += ms * plugin.getSpecPerMs(); + } + else + { + percentSpec = plugin.getSpecialPercentage(); + lastSpec = plugin.getSpecialPercentage(); + } + + renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, percentSpec, SPECIAL_COLOR); + } + + if (plugin.isReplaceOrbText()) + { + final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB); + + if (runOrb == null || runOrb.isHidden()) + { + return null; + } + + final Rectangle bounds = runOrb.getBounds(); + + if (bounds.getX() <= 0) + { + return null; + } + + final Point mousePosition = client.getMouseCanvasPosition(); + + if (bounds.contains(mousePosition.getX(), mousePosition.getY())) + { + StringBuilder sb = new StringBuilder(); + sb.append("Weight: ").append(client.getWeight()).append(" kg
"); + + if (plugin.isReplaceOrbText()) + { + sb.append("Run Energy: ").append(client.getEnergy()).append("%"); + } + else + { + sb.append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false)); + } + + int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining(); + if (secondsUntil100 > 0) + { + final int minutes = (int) Math.floor(secondsUntil100 / 60.0); + final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0)); + + sb.append("
").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0")); + } + + tooltipManager.add(new Tooltip(sb.toString())); + } + } + + if (plugin.isShowRun()) + { + if (lastRun == plugin.getRunPercentage() && plugin.getRunPercentage() != 0) + { + double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360000.0; + + if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT))) + { + recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect + } + + percentRun += ms * recoverRate; + } + else + { + percentRun = plugin.getRunPercentage(); + lastRun = plugin.getRunPercentage(); + } + renderRegen(g, WidgetInfo.MINIMAP_RUN_ORB, percentRun, RUN_COLOR); + } + + last = current; + + return null; + } + + private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color) + { + Widget widget = client.getWidget(widgetInfo); + if (widget == null || widget.isHidden()) + { + return; + } + Rectangle bounds = widget.getBounds(); + + Arc2D.Double arc = new Arc2D.Double(bounds.x + OFFSET, bounds.y + (bounds.height / 2 - DIAMETER / 2), DIAMETER, DIAMETER, 90.d, -360.d * percent, Arc2D.OPEN); + final Stroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + g.setStroke(STROKE); + g.setColor(color); + g.draw(arc); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java new file mode 100644 index 0000000000..3e3e671f4a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2019, Owain van Brakel + * Copyright (c) 2018, TheStonedTurtle + * Copyright (c) 2018 Abex + * Copyright (c) 2018, Zimaya + * Copyright (c) 2017, Adam + * 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.statusorbs; + +import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.GameState; +import net.runelite.api.InventoryID; +import net.runelite.api.Prayer; +import net.runelite.api.Skill; +import net.runelite.api.SpriteID; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.Notifier; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.Graceful; +import net.runelite.client.util.ImageUtil; +import org.apache.commons.lang3.StringUtils; + +@PluginDescriptor( + name = "Status Orbs", + description = "Configure settings for the Minimap orbs", + tags = {"minimap", "orb", "regen", "energy", "special"} +) +public class StatusOrbsPlugin extends Plugin +{ + private static final BufferedImage HEART_DISEASE; + private static final BufferedImage HEART_POISON; + private static final BufferedImage HEART_VENOM; + + static + { + HEART_DISEASE = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-DISEASE.png"), 26, 26); + HEART_POISON = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-POISON.png"), 26, 26); + HEART_VENOM = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-VENOM.png"), 26, 26); + } + + private static final int SPEC_REGEN_TICKS = 50; + private static final int NORMAL_HP_REGEN_TICKS = 100; + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private ConfigManager configManager; + + @Inject + private StatusOrbsConfig config; + + @Inject + private StatusOrbsOverlay overlay; + + @Inject + private OverlayManager overlayManager; + + @Inject + private Notifier notifier; + + @Getter + private double hitpointsPercentage; + + @Getter + private double specialPercentage; + + @Getter + private double runPercentage; + + @Getter + private double hpPerMs; + + @Getter + private double specPerMs = (double) 1 / (SPEC_REGEN_TICKS * 600); + + // RegenMeter + private int ticksSinceSpecRegen; + private int ticksSinceHPRegen; + private boolean wasRapidHeal; + private double ticksSinceRunRegen; + + // Run Energy + private int lastEnergy = 0; + private boolean localPlayerRunningToDestination; + private WorldPoint currPoint; + private WorldPoint prevLocalPlayerLocation; + + private BufferedImage heart; + + private boolean dynamicHpHeart; + @Getter(AccessLevel.PACKAGE) + private boolean showHitpoints; + private boolean showWhenNoChange; + private int getNotifyBeforeHpRegenSeconds; + @Getter(AccessLevel.PACKAGE) + private boolean showSpecial; + @Getter(AccessLevel.PACKAGE) + private boolean showRun; + @Getter(AccessLevel.PACKAGE) + private boolean replaceOrbText; + + @Provides + StatusOrbsConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(StatusOrbsConfig.class); + } + + @Override + protected void startUp() throws Exception + { + migrateConfigs(); + updateConfig(); + overlayManager.add(overlay); + if (this.dynamicHpHeart && client.getGameState().equals(GameState.LOGGED_IN)) + { + clientThread.invoke(this::checkHealthIcon); + } + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + localPlayerRunningToDestination = false; + prevLocalPlayerLocation = null; + resetRunOrbText(); + if (this.dynamicHpHeart) + { + clientThread.invoke(this::resetHealthIcon); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("statusorbs")) + { + updateConfig(); + switch (event.getKey()) + { + case "replaceOrbText": + if (!this.replaceOrbText) + { + resetRunOrbText(); + } + break; + case "dynamicHpHeart": + if (this.dynamicHpHeart) + { + checkHealthIcon(); + } + else + { + resetHealthIcon(); + } + break; + } + } + } + + @Subscribe + private void onVarbitChanged(VarbitChanged e) + { + if (this.dynamicHpHeart) + { + checkHealthIcon(); + } + + boolean isRapidHeal = client.isPrayerActive(Prayer.RAPID_HEAL); + if (wasRapidHeal != isRapidHeal) + { + ticksSinceHPRegen = 0; + } + wasRapidHeal = isRapidHeal; + } + + @Subscribe + private void onGameStateChanged(GameStateChanged ev) + { + if (ev.getGameState() == GameState.HOPPING || ev.getGameState() == GameState.LOGIN_SCREEN) + { + ticksSinceHPRegen = -2; // For some reason this makes this accurate + ticksSinceSpecRegen = 0; + ticksSinceRunRegen = -1; + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT) == 1000) + { + // The recharge doesn't tick when at 100% + ticksSinceSpecRegen = 0; + } + else + { + ticksSinceSpecRegen = (ticksSinceSpecRegen + 1) % SPEC_REGEN_TICKS; + } + specialPercentage = ticksSinceSpecRegen / (double) SPEC_REGEN_TICKS; + + int ticksPerHPRegen = NORMAL_HP_REGEN_TICKS; + hpPerMs = ticksPerHPRegen / (double) 6000000; + if (client.isPrayerActive(Prayer.RAPID_HEAL)) + { + ticksPerHPRegen /= 2; + hpPerMs *= 2; + } + + ticksSinceHPRegen = (ticksSinceHPRegen + 1) % ticksPerHPRegen; + hitpointsPercentage = ticksSinceHPRegen / (double) ticksPerHPRegen; + + int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS); + int maxHP = client.getRealSkillLevel(Skill.HITPOINTS); + if (currentHP == maxHP && !this.showWhenNoChange) + { + hitpointsPercentage = 0; + } + else if (currentHP > maxHP) + { + // Show it going down + hitpointsPercentage = 1 - hitpointsPercentage; + } + + // Run Energy + localPlayerRunningToDestination = + prevLocalPlayerLocation != null && + client.getLocalDestinationLocation() != null && + prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; + + if (this.getNotifyBeforeHpRegenSeconds > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen)) + { + notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!"); + } + + localPlayerRunningToDestination = + prevLocalPlayerLocation != null && + client.getLocalDestinationLocation() != null && + prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; + + prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation(); + + if (this.replaceOrbText) + { + setRunOrbText(getEstimatedRunTimeRemaining(true)); + } + + int currEnergy = client.getEnergy(); + currPoint = client.getLocalPlayer().getWorldLocation(); + if (currEnergy == 100 || (prevLocalPlayerLocation != null && currPoint.distanceTo(prevLocalPlayerLocation) > 1) || currEnergy < lastEnergy) + { + ticksSinceRunRegen = 0; + } + else if (currEnergy > lastEnergy) + { + if (runPercentage < 1) + { + ticksSinceRunRegen = (1 - runPercentage) / runRegenPerTick(); + ticksSinceRunRegen = ticksSinceRunRegen > 1 ? 1 : ticksSinceRunRegen; + } + else + { + ticksSinceRunRegen = (runPercentage - 1) / runRegenPerTick(); + } + } + else + { + ticksSinceRunRegen += 1; + } + runPercentage = ticksSinceRunRegen * runRegenPerTick(); + prevLocalPlayerLocation = currPoint; + lastEnergy = currEnergy; + } + + private boolean shouldNotifyHpRegenThisTick(int ticksPerHPRegen) + { + // if the configured duration lies between two ticks, choose the earlier tick + final int ticksBeforeHPRegen = ticksPerHPRegen - ticksSinceHPRegen; + final int notifyTick = (int) Math.ceil(this.getNotifyBeforeHpRegenSeconds * 1000d / Constants.GAME_TICK_LENGTH); + return ticksBeforeHPRegen == notifyTick; + } + + private void setRunOrbText(String text) + { + Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT); + + if (runOrbText != null) + { + runOrbText.setText(text); + } + } + + private void resetRunOrbText() + { + setRunOrbText(Integer.toString(client.getEnergy())); + } + + String getEstimatedRunTimeRemaining(boolean inSeconds) + { + // Calculate the amount of energy lost every 2 ticks (0.6 seconds). + // Negative weight has the same depletion effect as 0 kg. + final int effectiveWeight = Math.max(client.getWeight(), 0); + double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64; + + if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0) + { + lossRate *= 0.3; // Stamina effect reduces energy depletion to 30% + } + + // Calculate the number of seconds left + final double secondsLeft = (client.getEnergy() * 0.6) / lossRate; + + // Return the text + if (inSeconds) + { + return (int) Math.floor(secondsLeft) + "s"; + } + else + { + final int minutes = (int) Math.floor(secondsLeft / 60.0); + final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0)); + + return minutes + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0"); + } + } + + int getEstimatedRecoverTimeRemaining() + { + if (localPlayerRunningToDestination) + { + return -1; + } + + // Calculate the amount of energy recovered every second + double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0; + + if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT))) + { + recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect + } + + // Calculate the number of seconds left + final double secondsLeft = (100 - client.getEnergy()) / recoverRate; + return (int) secondsLeft; + } + + /** + * Check player afflictions to determine health icon + */ + private void checkHealthIcon() + { + BufferedImage newHeart; + + int poison = client.getVar(VarPlayer.IS_POISONED); + if (poison >= 1000000) + { + newHeart = HEART_VENOM; + } + else if (poison > 0) + { + newHeart = HEART_POISON; + } + else if (client.getVar(VarPlayer.DISEASE_VALUE) > 0) + { + newHeart = HEART_DISEASE; + } + else + { + heart = null; + resetHealthIcon(); + return; + } + + // Only update sprites when the heart icon actually changes + if (newHeart != heart) + { + heart = newHeart; + client.getWidgetSpriteCache().reset(); + client.getSpriteOverrides().put(SpriteID.MINIMAP_ORB_HITPOINTS_ICON, ImageUtil.getImageSprite(heart, client)); + } + } + + private double runRegenPerTick() + { + double recoverRate = (client.getBoostedSkillLevel(Skill.AGILITY) / 6d + 8) / 100; + + if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT))) + { + return recoverRate * 1.3; + } + return recoverRate; + } + + /** + * Ensure the HP Heart is the default Sprite + */ + private void resetHealthIcon() + { + client.getWidgetSpriteCache().reset(); + client.getSpriteOverrides().remove(SpriteID.MINIMAP_ORB_HITPOINTS_ICON); + } + + /** + * Migrates configs from runenergy and regenmeter to this plugin and deletes the old config values. + * This method should be removed after a reasonable amount of time. + */ + @Deprecated + private void migrateConfigs() + { + migrateConfig("regenmeter", "showHitpoints"); + migrateConfig("regenmeter", "showSpecial"); + migrateConfig("regenmeter", "showWhenNoChange"); + migrateConfig("regenmeter", "notifyBeforeHpRegenDuration"); + + migrateConfig("runenergy", "replaceOrbText"); + } + + /** + * Wrapper for migrating individual config options + * This method should be removed after a reasonable amount of time. + * + * @param group old group name + * @param key key name to migrate + */ + @Deprecated + private void migrateConfig(String group, String key) + { + String value = configManager.getConfiguration(group, key); + if (value != null) + { + configManager.setConfiguration("statusorbs", key, value); + configManager.unsetConfiguration(group, key); + } + } + + private void updateConfig() + { + this.dynamicHpHeart = config.dynamicHpHeart(); + this.showHitpoints = config.showHitpoints(); + this.showWhenNoChange = config.showWhenNoChange(); + this.getNotifyBeforeHpRegenSeconds = config.getNotifyBeforeHpRegenSeconds(); + this.showSpecial = config.showSpecial(); + this.showRun = config.showRun(); + this.replaceOrbText = config.replaceOrbText(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java new file mode 100644 index 0000000000..7c94851bc9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.ui.components; + +import javax.annotation.Nullable; +import javax.swing.Icon; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Used with ComboBoxListRenderer to render an icon next to the text of the list entry. + * Also supports adding a data object to be used for more complex selection logic + */ +@AllArgsConstructor +@Getter +public class ComboBoxIconEntry +{ + private Icon icon; + private String text; + @Nullable + private Object data; +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java index 5aeb710115..907170f412 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java @@ -30,6 +30,7 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.border.EmptyBorder; +import lombok.Setter; import net.runelite.client.ui.ColorScheme; import net.runelite.client.util.Text; @@ -41,6 +42,8 @@ import net.runelite.client.util.Text; */ public final class ComboBoxListRenderer extends JLabel implements ListCellRenderer { + @Setter + private String defaultText = "Select an option..."; @Override public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus) @@ -57,12 +60,24 @@ public final class ComboBoxListRenderer extends JLabel implements ListCellRender } setBorder(new EmptyBorder(5, 5, 5, 0)); + setIcon(null); String text; - if (o instanceof Enum) + // If using setSelectedItem(null) or setSelectedIndex(-1) show default text until a selection is made + if (index == -1 && o == null) + { + text = defaultText; + } + else if (o instanceof Enum) { text = Text.titleCase((Enum) o); } + else if (o instanceof ComboBoxIconEntry) + { + ComboBoxIconEntry e = (ComboBoxIconEntry) o; + text = e.getText(); + setIcon(e.getIcon()); + } else { text = o.toString(); diff --git a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java index edaedc856f..e837bb54d9 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java +++ b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java @@ -118,7 +118,7 @@ public class GameEventManager if (itemContainer != null) { - eventBus.post(new ItemContainerChanged(itemContainer)); + eventBus.post(new ItemContainerChanged(inventory.getId(), itemContainer)); } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/Graceful.java b/runelite-client/src/main/java/net/runelite/client/util/Graceful.java new file mode 100644 index 0000000000..4cd05ec128 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/Graceful.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 raiyni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.util; + +import com.google.common.collect.ImmutableSet; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import static net.runelite.api.ItemID.*; + +public enum Graceful +{ + // TODO: It would be nice if we have the IDs for just the equipped variants of the Graceful set items. + HOOD( + GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592, + GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627, + GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063 + ), + + TOP( + GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596, + GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631, + GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069 + ), + + LEGS( + GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598, + GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633, + GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072 + ), + + GLOVES( + GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600, + GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635, + GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075 + ), + + BOOTS( + GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602, + GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637, + GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078 + ), + + // Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect + CAPE( + GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594, + GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629, + GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066, + AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE + ); + + private final ImmutableSet ids; + + Graceful(Integer... ids) + { + this.ids = ImmutableSet.copyOf(ids); + } + + public static boolean hasFullSet(final ItemContainer equipment) + { + if (equipment == null) + { + return false; + } + + final Item[] items = equipment.getItems(); + + if (equipment == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) + { + return false; + } + + return HOOD.ids.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) + && TOP.ids.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) + && LEGS.ids.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) + && GLOVES.ids.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) + && BOOTS.ids.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) + && CAPE.ids.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java b/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java index 0e4af9137e..2756320990 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java @@ -30,7 +30,7 @@ import com.sun.jna.platform.win32.WinDef; import java.util.Arrays; import java.util.List; -class IcmpEchoReply extends Structure +public class IcmpEchoReply extends Structure { private static final int IP_OPTION_INFO_SIZE = 1 + 1 + 1 + 1 + (Pointer.SIZE == 8 ? 12 : 4); // on 64bit vms add 4 byte padding public static final int SIZE = 4 + 4 + 4 + 2 + 2 + Pointer.SIZE + IP_OPTION_INFO_SIZE; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/banked.png b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/banked.png new file mode 100644 index 0000000000..5256088e5f Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/banked.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-DISEASE.png b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-DISEASE.png new file mode 100644 index 0000000000..f2ff5ac911 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-DISEASE.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-POISON.png b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-POISON.png new file mode 100644 index 0000000000..a4b05c1ca1 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-POISON.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-VENOM.png b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-VENOM.png new file mode 100644 index 0000000000..df6816bb74 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-VENOM.png differ diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java new file mode 100644 index 0000000000..4025ccf1b7 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import net.runelite.api.Client; +import net.runelite.api.Item; +import net.runelite.api.ItemDefinition; +import net.runelite.api.ItemID; +import net.runelite.client.game.ItemManager; +import static net.runelite.client.plugins.itemskeptondeath.ItemsKeptOnDeathPlugin.DeathItems; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ItemsKeptOnDeathPluginTest +{ + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ItemManager itemManager; + + @Inject + private ItemsKeptOnDeathPlugin plugin; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + resetBuffs(); + } + + private void resetBuffs() + { + plugin.isSkulled = false; + plugin.protectingItem = false; + plugin.wildyLevel = -1; + } + + // Mocks an item and the necessary itemManager functions for it + private Item mItem(final int id, final int qty, final String name, final boolean tradeable, final int price) + { + // Mock Item Composition and necessary ItemManager methods for this item + ItemDefinition c = mock(ItemDefinition.class); + when(c.getId()) + .thenReturn(id); + when(c.getName()) + .thenReturn(name); + when(c.isTradeable()) + .thenReturn(tradeable); + when(c.getPrice()) + .thenReturn(price); + + if (!tradeable) + { + when(c.getNote()).thenReturn(-1); + when(c.getLinkedNoteId()).thenReturn(-1); + } + + when(itemManager.getItemDefinition(id)).thenReturn(c); + when(itemManager.canonicalize(id)).thenReturn(id); + when(itemManager.getItemPrice(id, true)).thenReturn(price); + + return mockItem(id, qty); + } + + // Creates a mocked item + private Item mockItem(final int id, final int qty) + { + Item item = mock(Item.class); + + when(item.getId()).thenReturn(id); + when(item.getQuantity()).thenReturn(qty); + + return item; + } + + @Test + public void deathPriceTestRegularItems() + { + final Item acs = mItem(ItemID.ARMADYL_CHAINSKIRT, 1, "Armadyl chainskirt", true, 27837495); + assertEquals(27837495, plugin.getDeathPrice(acs)); + + final Item karambwan = mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608); + assertEquals(608, plugin.getDeathPrice(karambwan)); + + final Item defender = mItem(ItemID.RUNE_DEFENDER, 1, "Rune defender", false, 35000); + assertEquals(35000, plugin.getDeathPrice(defender)); + } + + @Test + public void deathPriceTestItemMapping() + { + mItem(ItemID.OCCULT_NECKLACE, 1, "Occult necklace", true, 1000000); + mItem(ItemID.OCCULT_ORNAMENT_KIT, 1, "Occult ornament kit", true, 3000000); + final Item occult = mItem(ItemID.OCCULT_NECKLACE_OR, 1, "Occult necklace (or)", false, 0); + assertEquals(4000000, plugin.getDeathPrice(occult)); + + mItem(ItemID.BLACK_MASK, 1, "Black mask", true, 1000000); + final Item blackMask8 = mItem(ItemID.BLACK_MASK_8, 1, "Black mask (8)", false, 0); + assertEquals(1000000, plugin.getDeathPrice(blackMask8)); + final Item slayerHelm = mItem(ItemID.SLAYER_HELMET, 1, "Slayer helmet", false, 0); + assertEquals(1000000, plugin.getDeathPrice(slayerHelm)); + } + + @Test + public void deathPriceTestFixedPriceItems() + { + mItem(ItemID.KARILS_COIF_0, 1, "Karil's coif 0", true, 35000); + final Item coif = mItem(ItemID.KARILS_COIF_100, 1, "Karil's coif 100", false, 0); + final int coifOffset = FixedPriceItem.KARILS_COIF_100.getOffset(); + assertEquals(35000 + coifOffset, plugin.getDeathPrice(coif)); + + mItem(ItemID.AHRIMS_ROBETOP_0, 1, "Ahrim's robetop 0", true, 2500000); + final Item robetop = mItem(ItemID.AHRIMS_ROBETOP_25, 1, "Ahrim's robetop 100", false, 0); + final int robetopOffset = FixedPriceItem.AHRIMS_ROBETOP_25.getOffset(); + assertEquals(2500000 + robetopOffset, plugin.getDeathPrice(robetop)); + + mItem(ItemID.AMULET_OF_GLORY, 1, "Amulet of glory", true, 13000); + final Item glory = mItem(ItemID.AMULET_OF_GLORY3, 1, "Amulet of glory(3)", true, 0); + final int gloryOffset = FixedPriceItem.AMULET_OF_GLORY3.getOffset(); + assertEquals(13000 + gloryOffset, plugin.getDeathPrice(glory)); + + mItem(ItemID.COMBAT_BRACELET, 1, "Combat bracelet", true, 13500); + final Item brace = mItem(ItemID.COMBAT_BRACELET1, 1, "Combat bracelet(1)", true, 0); + final int braceletOffset = FixedPriceItem.COMBAT_BRACELET1.getOffset(); + assertEquals(13500 + braceletOffset, plugin.getDeathPrice(brace)); + } + + @Test + public void deathPriceTestDynamicPriceItems() + { + final Item rod8 = mItem(ItemID.RING_OF_DUELING8, 1, "Ring of dueling(8)", true, 725); + final Item rod3 = mItem(ItemID.RING_OF_DUELING3, 1, "Ring of dueling(3)", true, 0); + final Item rod1 = mItem(ItemID.RING_OF_DUELING1, 1, "Ring of dueling(1)", true, 0); + // Dynamic price items + final int rodPrice = 725 / 8; + assertEquals(rodPrice, plugin.getDeathPrice(rod1)); + assertEquals(725, plugin.getDeathPrice(rod8)); + assertEquals(rodPrice * 3, plugin.getDeathPrice(rod3)); + + final Item nop5 = mItem(ItemID.NECKLACE_OF_PASSAGE5, 1, "Necklace of passage(5)", true, 1250); + final Item nop4 = mItem(ItemID.NECKLACE_OF_PASSAGE4, 1, "Necklace of passage(4)", true, 0); + final Item nop2 = mItem(ItemID.NECKLACE_OF_PASSAGE2, 1, "Necklace of passage(2)", true, 0); + + final int nopPrice = 1250 / 5; + assertEquals(nopPrice * 2, plugin.getDeathPrice(nop2)); + assertEquals(nopPrice * 4, plugin.getDeathPrice(nop4)); + assertEquals(1250, plugin.getDeathPrice(nop5)); + } + + private Item[] getFourExpensiveItems() + { + return new Item[] + { + mItem(ItemID.TWISTED_BOW, 1, "Twister bow", true, Integer.MAX_VALUE), + mItem(ItemID.SCYTHE_OF_VITUR, 1, "Scythe of vitur", true, Integer.MAX_VALUE), + mItem(ItemID.ELYSIAN_SPIRIT_SHIELD, 1, "Elysian spirit shield", true, 800000000), + mItem(ItemID.ARCANE_SPIRIT_SHIELD, 1, "Arcane spirit shield", true, 250000000) + }; + } + + @Test + public void alwaysLostTestRunePouch() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.RUNE_POUCH, 1, "Rune pouch", false, 1) + }; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertFalse(deathItems.isHasAlwaysLost()); + } + + @Test + public void alwaysLostTestRunePouchWildy() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.RUNE_POUCH, 1, "Rune pouch", false, 1) + }; + + plugin.wildyLevel = 1; + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertTrue(deathItems.isHasAlwaysLost()); + } + + @Test + public void alwaysLostTestLootBag() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.LOOTING_BAG, 1, "Looting bag", false, 1) + }; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertTrue(deathItems.isHasAlwaysLost()); + + } + + @Test + public void alwaysLostTestLootBagWildy() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.LOOTING_BAG, 1, "Looting bag", false, 1) + }; + + plugin.wildyLevel = 1; + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertTrue(deathItems.isHasAlwaysLost()); + } + + private Item[] getClueBoxTestInventory() + { + return new Item[] + { + mItem(ItemID.BLACK_DHIDE_BODY, 1, "Black d'hide body", true, 7552), + mItem(ItemID.ARMADYL_CHAINSKIRT, 1, "Armadyl chainskirt", true, 27837495), + mItem(ItemID.PEGASIAN_BOOTS, 1, "Pegasian boots", true, 30542187), + mItem(ItemID.DRAGON_SCIMITAR, 1, "Dragon scimitar", true, 63123), + + mItem(ItemID.HELM_OF_NEITIZNOT, 1, "Helm of neitiznot", true, 45519), + mItem(ItemID.RUNE_DEFENDER, 1, "Rune defender", false, 35000), + mItem(ItemID.SPADE, 1, "Spade", true, 104), + mItem(ItemID.CLUE_SCROLL_EASY, 1, "Clue scroll (easy)", false, 50), + + mItem(ItemID.CLUE_BOX, 1, "Clue box", false, 50), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.LAW_RUNE, 200, "Law rune", true, 212), + mItem(ItemID.DUST_RUNE, 200, "Dust rune", true, 3), + + mItem(ItemID.CLUE_SCROLL_MASTER, 1, "Clue scroll (master)", false, 50), + mItem(ItemID.CLUELESS_SCROLL, 1, "Clueless scroll", false, 50), + }; + } + + @Test + public void isClueBoxableTest() + { + getClueBoxTestInventory(); + mItem(ItemID.REWARD_CASKET_EASY, 1, "Reward casket (easy)", false, 50); + + assertTrue(plugin.isClueBoxable(ItemID.CLUE_SCROLL_EASY)); + assertTrue(plugin.isClueBoxable(ItemID.CLUE_SCROLL_MASTER)); + assertTrue(plugin.isClueBoxable(ItemID.REWARD_CASKET_EASY)); + + assertFalse(plugin.isClueBoxable(ItemID.CLUELESS_SCROLL)); + assertFalse(plugin.isClueBoxable(ItemID.LAW_RUNE)); + assertFalse(plugin.isClueBoxable(ItemID.SPADE)); + } + + @Test + public void clueBoxTestDefault() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.RUNE_DEFENDER, 1), + new ItemStack(ItemID.CLUE_SCROLL_EASY, 1), + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1), + new ItemStack(ItemID.CLUELESS_SCROLL, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + assertEquals((inv.length + equip.length) - expectedKept.size(), lost.size()); + } + + @Test + public void clueBoxTestDeepWildy() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 21; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + @Test + public void clueBoxTestDeepWildyProtectItem() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 21; + plugin.protectingItem = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.HELM_OF_NEITIZNOT, 1), + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) // Clue box + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + @Test + public void clueBoxTestDeepWildySkulled() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 21; + plugin.isSkulled = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Collections.singletonList( + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals(lost.size(), (inv.length + equip.length) - keptOffset); + } + + @Test + public void clueBoxTestLowWildy() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.RUNE_DEFENDER, 1), // Rune defender protected because of broken variant + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals(lost.size(), (inv.length + equip.length) - keptOffset); + } + + @Test + public void clueBoxTestLowWildyProtectItem() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + plugin.protectingItem = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.HELM_OF_NEITIZNOT, 1), + new ItemStack(ItemID.RUNE_DEFENDER, 1), // Rune defender protected because of broken variant + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + @Test + public void clueBoxTestLowWildySkulled() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + plugin.isSkulled = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.RUNE_DEFENDER, 1), // Rune defender protected because of broken variant + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + private Item[] getClueBoxCasketTestInventory() + { + // Reward caskets can stack but the clue box should only protect one + return new Item[] + { + mItem(ItemID.BLACK_DHIDE_BODY, 1, "Black d'hide body", true, 7552), + mItem(ItemID.ARMADYL_CHAINSKIRT, 1, "Armadyl chainskirt", true, 27837495), + mItem(ItemID.PEGASIAN_BOOTS, 1, "Pegasian boots", true, 30542187), + mItem(ItemID.DRAGON_SCIMITAR, 1, "Dragon scimitar", true, 63123), + + mItem(ItemID.SPADE, 1, "Spade", true, 104), + mItem(ItemID.CLUE_SCROLL_EASY, 1, "Clue scroll (easy)", false, 50), + mItem(ItemID.REWARD_CASKET_EASY, 20, "Reward casket (easy)", false, 50), + mItem(ItemID.CLUE_BOX, 1, "Clue box", false, 50), + + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + + mItem(ItemID.LAW_RUNE, 200, "Law rune", true, 212), + mItem(ItemID.DUST_RUNE, 200, "Dust rune", true, 3), + }; + } + + @Test + public void clueBoxTestCasketProtect() + { + final Item[] inv = getClueBoxCasketTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.REWARD_CASKET_EASY, 1) // Clue box + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size() - 1; // We are still losing some reward caskets. + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + private Item[] getFullGracefulItems() + { + return new Item[] + { + mItem(ItemID.GRACEFUL_HOOD, 1, "Graceful hood", false, 35), + mItem(ItemID.GRACEFUL_CAPE, 1, "Graceful cape", false, 40), + mItem(ItemID.GRACEFUL_TOP, 1, "Graceful top", false, 55), + mItem(ItemID.GRACEFUL_LEGS, 1, "Graceful legs", false, 60), + mItem(ItemID.GRACEFUL_BOOTS, 1, "Graceful boots", false, 40), + mItem(ItemID.GRACEFUL_GLOVES, 1, "Graceful gloves", false, 30), + }; + } + + @Test + public void gracefulValueTest() + { + final Item[] inv = getFullGracefulItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.AMULET_OF_GLORY6, 1, "Amulet of glory (6)", true, 20000) + }; + + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.AMULET_OF_GLORY6, 1), + new ItemStack(ItemID.GRACEFUL_CAPE, 1), + new ItemStack(ItemID.GRACEFUL_TOP, 1), + new ItemStack(ItemID.GRACEFUL_LEGS, 1), + new ItemStack(ItemID.GRACEFUL_BOOTS, 1), + new ItemStack(ItemID.GRACEFUL_HOOD, 1), + new ItemStack(ItemID.GRACEFUL_GLOVES, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + assertEquals((inv.length + equip.length) - expectedKept.size(), lost.size()); + } + + @Test + public void gracefulValueTestWildy() + { + final Item[] inv = getFullGracefulItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.AMULET_OF_GLORY6, 1, "Amulet of glory (6)", true, 20000) + }; + + plugin.wildyLevel = 1; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.AMULET_OF_GLORY6, 1), + new ItemStack(ItemID.GRACEFUL_CAPE, 1), + new ItemStack(ItemID.GRACEFUL_TOP, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + assertEquals((inv.length + equip.length) - expectedKept.size(), lost.size()); + } + + @Test + public void lostIfNotProtectedTestLost() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.SHADOW_SWORD, 1, "Shadow sword", false, 1) + }; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List lost = deathItems.getLostItems(); + assertTrue(lost.contains(new ItemStack(ItemID.SHADOW_SWORD, 1))); + } + + @Test + public void lostIfNotProtectedTestKept() + { + final Item[] inv = new Item[] + { + mItem(ItemID.SHADOW_SWORD, 1, "Shadow sword", false, 1) + }; + final Item[] equip = new Item[0]; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + assertTrue(kept.contains(new ItemStack(ItemID.SHADOW_SWORD, 1))); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java index 7f4c3bcbb0..d14e3ae301 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java @@ -162,7 +162,7 @@ public class MotherlodePluginTest when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); // Trigger comparison - motherlodePlugin.onItemContainerChanged(new ItemContainerChanged(inventory)); + motherlodePlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.INVENTORY.getId(), inventory)); verify(motherlodeSession).updateOreFound(ItemID.RUNITE_ORE, 1); verify(motherlodeSession).updateOreFound(ItemID.GOLDEN_NUGGET, 4); diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java index 43d8582787..cca4cae90d 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -163,6 +163,9 @@ public abstract class RSClientMixin implements RSClient @Inject private static boolean interpolateObjectAnimations; + @Inject + private static boolean interpolateWidgetAnimations; + @Inject private static RSPlayer[] oldPlayers = new RSPlayer[2048]; @@ -307,6 +310,20 @@ public abstract class RSClientMixin implements RSClient interpolateObjectAnimations = interpolate; } + @Inject + @Override + public boolean isInterpolateWidgetAnimations() + { + return interpolateWidgetAnimations; + } + + @Inject + @Override + public void setInterpolateWidgetAnimations(boolean interpolate) + { + interpolateWidgetAnimations = interpolate; + } + @Inject @Override public void setInventoryDragDelay(int delay) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java index 74c1bf29a3..25ddd7e681 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java @@ -24,11 +24,13 @@ */ package net.runelite.mixins; +import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.events.ItemContainerChanged; -import net.runelite.api.mixins.FieldHook; +import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Replace; import net.runelite.api.mixins.Shadow; import net.runelite.rs.api.RSClient; import net.runelite.rs.api.RSGroundItem; @@ -41,7 +43,7 @@ public abstract class RSItemContainerMixin implements RSItemContainer private static RSClient client; @Inject - private int rl$lastCycle; + static private int rl$lastCycle; @Inject @Override @@ -62,21 +64,27 @@ public abstract class RSItemContainerMixin implements RSItemContainer return items; } - @FieldHook("quantities") - @Inject - public void stackSizesChanged(int idx) + @Copy("itemContainerSetItem") + static void rs$itemContainerSetItem(int itemContainerId, int index, int itemId, int itemQuantity) { + + } + + @Replace("itemContainerSetItem") + static void rl$itemContainerSetItem(int itemContainerId, int index, int itemId, int itemQuantity) + { + rs$itemContainerSetItem(itemContainerId, index, itemId, itemQuantity); + int cycle = client.getGameCycle(); if (rl$lastCycle == cycle) { // Limit item container updates to one per cycle + // No need to repeatedly update. The game just needs to know that containers changed once per cycle return; } rl$lastCycle = cycle; - - ItemContainerChanged event = new ItemContainerChanged(this); + ItemContainerChanged event = new ItemContainerChanged(itemContainerId, client.getItemContainer(InventoryID.getValue(itemContainerId))); client.getCallbacks().postDeferred(event); } - } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java index 2433ed1d2b..4bf1ab4538 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java @@ -15,10 +15,10 @@ public abstract class RSSequenceDefinitionMixin implements RSSequenceDefinition @Shadow("client") private static RSClient client; - @Copy("animateSequence2") + @Copy("applyTransformations") public abstract RSModel rs$applyTransformations(RSModel model, int actionFrame, RSSequenceDefinition poseSeq, int poseFrame); - @Replace("animateSequence2") + @Replace("applyTransformations") public RSModel rl$applyTransformations(RSModel model, int actionFrame, RSSequenceDefinition poseSeq, int poseFrame) { // reset frame ids because we're not interpolating this @@ -35,185 +35,253 @@ public abstract class RSSequenceDefinitionMixin implements RSSequenceDefinition return rs$applyTransformations(model, actionFrame, poseSeq, poseFrame); } - @Copy("animateSequence") + @Copy("transformActorModel") public abstract RSModel rs$transformActorModel(RSModel model, int frameIdx); - @Replace("animateSequence") + @Replace("transformActorModel") public RSModel rl$transformActorModel(RSModel model, int frame) { - // check if the frame has been modified - if (frame < 0) - { - // remove flag to check if the frame has been modified - int packed = frame ^ Integer.MIN_VALUE; - int interval = packed >> 16; - frame = packed & 0xFFFF; - int nextFrame = frame + 1; - if (nextFrame >= getFrameIDs().length) - { - // dont interpolate last frame - nextFrame = -1; - } - int[] frameIds = getFrameIDs(); - int frameId = frameIds[frame]; - RSFrames frames = client.getFrames(frameId >> 16); - int frameIdx = frameId & 0xFFFF; - - int nextFrameIdx = -1; - RSFrames nextFrames = null; - if (nextFrame != -1) - { - int nextFrameId = frameIds[nextFrame]; - nextFrames = client.getFrames(nextFrameId >> 16); - nextFrameIdx = nextFrameId & 0xFFFF; - } - - if (frames == null) - { - // not sure what toSharedModel does but it is needed - return model.toSharedModel(true); - } - else - { - RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); - animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, - getFrameLenths()[frame]); - return animatedModel; - } - } - else + // check if the frame has not been modified + if (frame >= 0) { return rs$transformActorModel(model, frame); } + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) + { + // dont interpolate last frame + nextFrame = -1; + } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + // not sure what toSharedModel does but it is needed + return model.toSharedModel(true); + } + + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + return animatedModel; } - @Copy("animateObject") + @Copy("transformObjectModel") public abstract RSModel rs$transformObjectModel(RSModel model, int frame, int rotation); - @Replace("animateObject") + @Replace("transformObjectModel") public RSModel rl$transformObjectModel(RSModel model, int frame, int rotation) { - // check if the frame has been modified - if (frame < 0) - { - // remove flag to check if the frame has been modified - int packed = frame ^ Integer.MIN_VALUE; - int interval = packed >> 16; - frame = packed & 0xFFFF; - - int nextFrame = frame + 1; - if (nextFrame >= getFrameIDs().length) - { - // dont interpolate last frame - nextFrame = -1; - } - int[] frameIds = getFrameIDs(); - int frameId = frameIds[frame]; - RSFrames frames = client.getFrames(frameId >> 16); - int frameIdx = frameId & 0xFFFF; - - int nextFrameIdx = -1; - RSFrames nextFrames = null; - if (nextFrame != -1) - { - int nextFrameId = frameIds[nextFrame]; - nextFrames = client.getFrames(nextFrameId >> 16); - nextFrameIdx = nextFrameId & 0xFFFF; - } - - if (frames == null) - { - return model.toSharedModel(true); - } - else - { - RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); - // reset rotation before animating - rotation &= 3; - if (rotation == 1) - { - animatedModel.rotateY270Ccw(); - } - else if (rotation == 2) - { - animatedModel.rotateY180Ccw(); - } - else if (rotation == 3) - { - animatedModel.rotateY90Ccw(); - } - animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, - getFrameLenths()[frame]); - // reapply rotation after animating - if (rotation == 1) - { - animatedModel.rotateY90Ccw(); - } - else if (rotation == 2) - { - animatedModel.rotateY180Ccw(); - } - else if (rotation == 3) - { - animatedModel.rotateY270Ccw(); - } - return animatedModel; - } - } - else + // check if the frame has not been modified + if (frame >= 0) { return rs$transformObjectModel(model, frame, rotation); } + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) + { + // dont interpolate last frame + nextFrame = -1; + } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + return model.toSharedModel(true); + } + + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); + // reset rotation before animating + rotation &= 3; + if (rotation == 1) + { + animatedModel.rotateY270Ccw(); + } + else if (rotation == 2) + { + animatedModel.rotateY180Ccw(); + } + else if (rotation == 3) + { + animatedModel.rotateY90Ccw(); + } + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + // reapply rotation after animating + if (rotation == 1) + { + animatedModel.rotateY90Ccw(); + } + else if (rotation == 2) + { + animatedModel.rotateY180Ccw(); + } + else if (rotation == 3) + { + animatedModel.rotateY270Ccw(); + } + return animatedModel; } - @Copy("animateSpotAnimation") - public abstract RSModel rs$transformSpotAnimModel(RSModel model, int frame); + @Copy("transformSpotAnimationModel") + public abstract RSModel rs$transformSpotAnimationModel(RSModel model, int frame); - @Replace("animateSpotAnimation") - public RSModel rl$transformSpotAnimModel(RSModel model, int frame) + @Replace("transformSpotAnimationModel") + public RSModel rl$transformSpotAnimationModel(RSModel model, int frame) { - // check if the frame has been modified - if (frame < 0) + // check if the frame has not been modified + if (frame >= 0) { - // remove flag to check if the frame has been modified - int packed = frame ^ Integer.MIN_VALUE; - int interval = packed >> 16; - frame = packed & 0xFFFF; - int nextFrame = frame + 1; - if (nextFrame >= getFrameIDs().length) - { - // dont interpolate last frame - nextFrame = -1; - } - int[] frameIds = getFrameIDs(); - int frameId = frameIds[frame]; - RSFrames frames = client.getFrames(frameId >> 16); - int frameIdx = frameId & 0xFFFF; - - int nextFrameIdx = -1; - RSFrames nextFrames = null; - if (nextFrame != -1) - { - int nextFrameId = frameIds[nextFrame]; - nextFrames = client.getFrames(nextFrameId >> 16); - nextFrameIdx = nextFrameId & 0xFFFF; - } - - if (frames == null) - { - return model.toSharedSpotAnimModel(true); - } - else - { - RSModel animatedModel = model.toSharedSpotAnimModel(!frames.getFrames()[frameIdx].isShowing()); - animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, - getFrameLenths()[frame]); - return animatedModel; - } + return rs$transformSpotAnimationModel(model, frame); } - else + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) { - return rs$transformSpotAnimModel(model, frame); + // dont interpolate last frame + nextFrame = -1; } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + return model.toSharedSpotAnimModel(true); + } + + RSModel animatedModel = model.toSharedSpotAnimModel(!frames.getFrames()[frameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + return animatedModel; + } + + @Copy("transformWidgetModel") + public abstract RSModel rs$transformWidgetModel(RSModel model, int frame); + + @Replace("transformWidgetModel") + public RSModel rl$transformWidgetModel(RSModel model, int frame) + { + // check if the frame has not been modified + if (frame >= 0) + { + return rs$transformWidgetModel(model, frame); + } + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) + { + // dont interpolate last frame + nextFrame = -1; + } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + return model.toSharedModel(true); + } + + RSFrames chatFrames = null; + int chatFrameIdx = 0; + if (getChatFrameIds() != null && frame < getChatFrameIds().length) + { + int chatFrameId = getChatFrameIds()[frame]; + chatFrames = client.getFrames(chatFrameId >> 16); + chatFrameIdx = chatFrameId & 0xFFFF; + } + if (chatFrames != null && chatFrameIdx != 0xFFFF) + { + RSFrames nextChatFrames = null; + int nextChatFrameIdx = -1; + if (nextFrame != -1 && nextFrame < getChatFrameIds().length) + { + int chatFrameId = getChatFrameIds()[nextFrame]; + nextChatFrames = client.getFrames(chatFrameId >> 16); + nextChatFrameIdx = chatFrameId & 0xFFFF; + } + // not sure if this can even happen but the client checks for this so to be safe + if (nextChatFrameIdx == 0xFFFF) + { + nextChatFrames = null; + } + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing() + & !chatFrames.getFrames()[chatFrameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + animatedModel.interpolateFrames(chatFrames, chatFrameIdx, nextChatFrames, nextChatFrameIdx, + interval, getFrameLenths()[frame]); + return animatedModel; + } + + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + return animatedModel; } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java index 208c813667..55db558d24 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java @@ -30,6 +30,8 @@ import net.runelite.api.Point; import net.runelite.api.WidgetNode; import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.events.WidgetPositioned; +import net.runelite.api.mixins.Copy; +import net.runelite.api.mixins.Replace; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; @@ -44,8 +46,11 @@ import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; import net.runelite.api.mixins.Shadow; import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSModel; import net.runelite.rs.api.RSNode; import net.runelite.rs.api.RSNodeHashTable; +import net.runelite.rs.api.RSPlayerAppearance; +import net.runelite.rs.api.RSSequenceDefinition; import net.runelite.rs.api.RSWidget; @Mixin(RSWidget.class) @@ -581,4 +586,17 @@ public abstract class RSWidgetMixin implements RSWidget Arrays.fill(getChildren(), null); } } + + @Copy("getModel") + public abstract RSModel rs$getModel(RSSequenceDefinition sequence, int frame, boolean alternate, RSPlayerAppearance playerComposition); + + @Replace("getModel") + public RSModel rl$getModel(RSSequenceDefinition sequence, int frame, boolean alternate, RSPlayerAppearance playerComposition) + { + if (frame != -1 && client.isInterpolateWidgetAnimations()) + { + frame = frame | getModelFrameCycle() << 16 | Integer.MIN_VALUE; + } + return rs$getModel(sequence, frame, alternate, playerComposition); + } } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java b/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java index 97c8d9a91a..ce8d1bae49 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java @@ -24,4 +24,7 @@ public interface RSSequenceDefinition @Import("frameLengths") int[] getFrameLenths(); + + @Import("chatFrameIds") + int[] getChatFrameIds(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java index 85c45da695..286eae5ce9 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java @@ -475,4 +475,10 @@ public interface RSWidget extends Widget @Import("noScrollThrough") @Override void setNoScrollThrough(boolean noScrollThrough); + + @Import("modelFrame") + int getModelFrame(); + + @Import("modelFrameCycle") + int getModelFrameCycle(); } diff --git a/runescape-client/src/main/java/NPCDefinition.java b/runescape-client/src/main/java/NPCDefinition.java index f12aa595fa..e26103531a 100644 --- a/runescape-client/src/main/java/NPCDefinition.java +++ b/runescape-client/src/main/java/NPCDefinition.java @@ -424,11 +424,11 @@ public class NPCDefinition extends DualNode { Model var10; if (var1 != null && var3 != null) { - var10 = var1.animateSequence2(var5, var2, var3, var4); + var10 = var1.applyTransformations(var5, var2, var3, var4); } else if (var1 != null) { - var10 = var1.animateSequence(var5, var2); + var10 = var1.transformActorModel(var5, var2); } else if (var3 != null) { - var10 = var3.animateSequence(var5, var4); + var10 = var3.transformActorModel(var5, var4); } else { var10 = var5.toSharedSequenceModel(true); } diff --git a/runescape-client/src/main/java/ObjectDefinition.java b/runescape-client/src/main/java/ObjectDefinition.java index 6a6a68db6f..e8d3c9e0cf 100644 --- a/runescape-client/src/main/java/ObjectDefinition.java +++ b/runescape-client/src/main/java/ObjectDefinition.java @@ -662,7 +662,7 @@ public class ObjectDefinition extends DualNode { return var11; } else { if (var7 != null) { - var11 = var7.animateObject(var11, var8, var2); + var11 = var7.transformObjectModel(var11, var8, var2); } else { var11 = var11.toSharedSequenceModel(true); } diff --git a/runescape-client/src/main/java/PlayerAppearance.java b/runescape-client/src/main/java/PlayerAppearance.java index ad5de3e57f..d308ce0f27 100644 --- a/runescape-client/src/main/java/PlayerAppearance.java +++ b/runescape-client/src/main/java/PlayerAppearance.java @@ -321,11 +321,11 @@ public class PlayerAppearance { } else { Model var16; if (var1 != null && var3 != null) { - var16 = var1.animateSequence2(var15, var2, var3, var4); + var16 = var1.applyTransformations(var15, var2, var3, var4); } else if (var1 != null) { - var16 = var1.animateSequence(var15, var2); + var16 = var1.transformActorModel(var15, var2); } else { - var16 = var3.animateSequence(var15, var4); + var16 = var3.transformActorModel(var15, var4); } return var16; diff --git a/runescape-client/src/main/java/SequenceDefinition.java b/runescape-client/src/main/java/SequenceDefinition.java index 3db6636931..323bb88dce 100644 --- a/runescape-client/src/main/java/SequenceDefinition.java +++ b/runescape-client/src/main/java/SequenceDefinition.java @@ -38,8 +38,8 @@ public class SequenceDefinition extends DualNode { @Export("frameIds") public int[] frameIds; @ObfuscatedName("g") - @Export("frameIds2") - int[] frameIds2; + @Export("chatFrameIds") + int[] chatFrameIds; @ObfuscatedName("l") @Export("frameLengths") public int[] frameLengths; @@ -179,14 +179,14 @@ public class SequenceDefinition extends DualNode { this.field783 = var1.readUnsignedByte(); } else if (var2 == 12) { var4 = var1.readUnsignedByte(); - this.frameIds2 = new int[var4]; + this.chatFrameIds = new int[var4]; for (var5 = 0; var5 < var4; ++var5) { - this.frameIds2[var5] = var1.readUnsignedShort(); + this.chatFrameIds[var5] = var1.readUnsignedShort(); } for (var5 = 0; var5 < var4; ++var5) { - var3 = this.frameIds2; + var3 = this.chatFrameIds; var3[var5] += var1.readUnsignedShort() << 16; } } else if (var2 == 13) { @@ -230,8 +230,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;II)Ldu;", garbageValue = "128527714" ) - @Export("animateSequence") - public Model animateSequence(Model model, int frame) { + @Export("transformActorModel") + public Model transformActorModel(Model model, int frame) { frame = this.frameIds[frame]; Frames var3 = ItemContainer.getFrames(frame >> 16); frame &= 65535; @@ -249,8 +249,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;IIB)Ldu;", garbageValue = "-65" ) - @Export("animateObject") - Model animateObject(Model model, int frame, int orientation) { + @Export("transformObjectModel") + Model transformObjectModel(Model model, int frame, int orientation) { frame = this.frameIds[frame]; Frames var4 = ItemContainer.getFrames(frame >> 16); frame &= 65535; @@ -285,8 +285,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;II)Ldu;", garbageValue = "-1692496767" ) - @Export("animateSpotAnimation") - Model animateSpotAnimation(Model model, int frame) { + @Export("transformSpotAnimationModel") + Model transformSpotAnimationModel(Model model, int frame) { frame = this.frameIds[frame]; Frames var3 = ItemContainer.getFrames(frame >> 16); frame &= 65535; @@ -304,13 +304,13 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;ILjh;II)Ldu;", garbageValue = "-386360993" ) - @Export("animateSequence2") - public Model animateSequence2(Model model, int frame, SequenceDefinition sequence, int sequenceFrame) { + @Export("applyTransformations") + public Model applyTransformations(Model model, int frame, SequenceDefinition sequence, int sequenceFrame) { frame = this.frameIds[frame]; Frames var5 = ItemContainer.getFrames(frame >> 16); frame &= 65535; if (var5 == null) { - return sequence.animateSequence(model, sequenceFrame); + return sequence.transformActorModel(model, sequenceFrame); } else { sequenceFrame = sequence.frameIds[sequenceFrame]; Frames var6 = ItemContainer.getFrames(sequenceFrame >> 16); @@ -333,8 +333,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;II)Ldu;", garbageValue = "-15433768" ) - @Export("animateWidget") - public Model animateWidget(Model model, int frame) { + @Export("transformWidgetModel") + public Model transformWidgetModel(Model model, int frame) { int var3 = this.frameIds[frame]; Frames var4 = ItemContainer.getFrames(var3 >> 16); var3 &= 65535; @@ -343,8 +343,8 @@ public class SequenceDefinition extends DualNode { } else { Frames var5 = null; int var6 = 0; - if (this.frameIds2 != null && frame < this.frameIds2.length) { - var6 = this.frameIds2[frame]; + if (this.chatFrameIds != null && frame < this.chatFrameIds.length) { + var6 = this.chatFrameIds[frame]; var5 = ItemContainer.getFrames(var6 >> 16); var6 &= 65535; } diff --git a/runescape-client/src/main/java/SpotAnimationDefinition.java b/runescape-client/src/main/java/SpotAnimationDefinition.java index 3d7668daf8..d7d530aba5 100644 --- a/runescape-client/src/main/java/SpotAnimationDefinition.java +++ b/runescape-client/src/main/java/SpotAnimationDefinition.java @@ -196,7 +196,7 @@ public class SpotAnimationDefinition extends DualNode { Model var5; if (this.sequence != -1 && var1 != -1) { - var5 = WorldMapAreaData.getSequenceDefinition(this.sequence).animateSpotAnimation(var2, var1); + var5 = WorldMapAreaData.getSequenceDefinition(this.sequence).transformSpotAnimationModel(var2, var1); } else { var5 = var2.toSharedSpotAnimationModel(true); } diff --git a/runescape-client/src/main/java/Widget.java b/runescape-client/src/main/java/Widget.java index 69b5dca3b4..7b91706a4a 100644 --- a/runescape-client/src/main/java/Widget.java +++ b/runescape-client/src/main/java/Widget.java @@ -1376,11 +1376,11 @@ public class Widget extends Node { garbageValue = "1082545676" ) @Export("getModel") - public Model getModel(SequenceDefinition sequence, int frame, boolean var3, PlayerAppearance appearance) { + public Model getModel(SequenceDefinition sequence, int frame, boolean alternate, PlayerAppearance appearance) { field957 = false; int var5; int var6; - if (var3) { + if (alternate) { var5 = this.modelType2; var6 = this.modelId2; } else { @@ -1445,7 +1445,7 @@ public class Widget extends Node { } if (sequence != null) { - var7 = sequence.animateWidget(var7, frame); + var7 = sequence.transformWidgetModel(var7, frame); } return var7;