Merge branch 'master' into NPC-UPDATE-0cbfb2e4d

This commit is contained in:
Owain van Brakel
2019-10-26 20:09:28 +02:00
committed by GitHub
22 changed files with 2710 additions and 945 deletions

View File

@@ -6,7 +6,7 @@ jobs:
pr-lint:
name: PR title
runs-on: ubuntu-latest
steps:
- name: PR title lint
if: github.event_name == 'pull_request'
@@ -15,49 +15,55 @@ jobs:
title-regex: '^([\w-/]+): \w+'
build:
runs-on: windows-latest
runs-on: ubuntu-latest
name: Build
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v1
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Set up JDK 11
uses: actions/setup-java@master
uses: actions/setup-java@v1
with:
java-version: 11
- name: Assembling
run: gradlew assemble --console=plain
run: ./gradlew assemble --console=plain
- name: Building
run: gradlew build --stacktrace -x test -x checkstyleMain --console=plain
run: ./gradlew build --stacktrace -x test -x checkstyleMain --console=plain
test:
runs-on: windows-latest
runs-on: ubuntu-latest
name: Test
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v1
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Set up JDK 11
uses: actions/setup-java@master
uses: actions/setup-java@v1
with:
java-version: 11
- name: Assembling
run: gradlew assemble --console=plain
run: ./gradlew assemble --console=plain
- name: Testing
run: gradlew test --stacktrace --console=plain
run: ./gradlew test --stacktrace --console=plain
checkstyle:
name: Checkstyle
runs-on: windows-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v1
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Set up JDK 11
uses: actions/setup-java@master
uses: actions/setup-java@v1
with:
java-version: 11
- name: Assembling
run: gradlew assemble --console=plain
run: ./gradlew assemble --console=plain
- name: Checking code conventions
run: gradlew checkstyleMain --console=plain
run: ./gradlew checkstyleMain --console=plain
approve:
name: Approve
@@ -67,6 +73,6 @@ jobs:
steps:
- name: Approve pull request
if: github.event_name == 'pull_request' && github.actor == 'OpenOSRS'
uses: hmarr/auto-approve-action@master
uses: hmarr/auto-approve-action@v2.0.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
github-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v1
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Update Gradle Wrapper
@@ -28,17 +28,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Update Gradle dependencies
run: ./gradlew useLatestVersions --console=plain
- name: Create Gradle dependencies update Pull Request
uses: Owain94/create-pull-request@master
env:
GITHUB_TOKEN: ${{ secrets.OpenOSRS }}
PULL_REQUEST_BRANCH: GRADLE-DEPENDENCY-UPDATE
PULL_REQUEST_TITLE: 'project: Update gradle dependencies'
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle dependencies versions
COMMIT_MESSAGE: 'project: Update gradle dependencies'
PULL_REQUEST_LABELS: automated pull request, gradle
- uses: actions/checkout@v1
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Update Gradle dependencies
run: ./gradlew useLatestVersions --console=plain
- name: Create Gradle dependencies update Pull Request
uses: Owain94/create-pull-request@master
env:
GITHUB_TOKEN: ${{ secrets.OpenOSRS }}
PULL_REQUEST_BRANCH: GRADLE-DEPENDENCY-UPDATE
PULL_REQUEST_TITLE: 'project: Update gradle dependencies'
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle dependencies versions
COMMIT_MESSAGE: 'project: Update gradle dependencies'
PULL_REQUEST_LABELS: automated pull request, gradle

View File

@@ -9,9 +9,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v1
- name: Set up JDK 11
uses: actions/setup-java@master
uses: actions/setup-java@v1
with:
java-version: 11
- name: Make gradlew executable
@@ -36,4 +36,4 @@ jobs:
PULL_REQUEST_TITLE: 'Client: Update NPC stats'
PULL_REQUEST_BODY: This is an auto-generated PR with changes from the OSRS wiki
COMMIT_MESSAGE: 'Client: Update NPC stats'
PULL_REQUEST_LABELS: automated pull request, NPC stats
PULL_REQUEST_LABELS: automated pull request, NPC stats

View File

@@ -19,4 +19,4 @@ jobs:
exempt-issue-label: 'awaiting-approval'
exempt-pr-label: 'awaiting-approval'
days-before-stale: 60
days-before-close: 30
days-before-close: 30

View File

@@ -273,6 +273,7 @@ public final class AnimationID
// INFERNO animations
public static final int JAL_NIB = 7574;
public static final int JAL_MEJRAH = 7578;
public static final int JAL_MEJRAH_STAND = 7577;
public static final int JAL_AK_RANGE_ATTACK = 7581;
public static final int JAL_AK_MELEE_ATTACK = 7582;
public static final int JAL_AK_MAGIC_ATTACK = 7583;

View File

@@ -33,6 +33,7 @@ import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@@ -41,16 +42,19 @@ import net.runelite.client.ui.overlay.components.ImageComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
@Singleton
public class InfernoJadOverlay extends Overlay
public class InfernoInfoBoxOverlay extends Overlay
{
private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150);
private final Client client;
private final InfernoPlugin plugin;
private final SpriteManager spriteManager;
private final PanelComponent imagePanelComponent = new PanelComponent();
private BufferedImage prayMeleeSprite;
private BufferedImage prayRangedSprite;
private BufferedImage prayMagicSprite;
@Inject
private InfernoJadOverlay(final Client client, final InfernoPlugin plugin, final SpriteManager spriteManager)
private InfernoInfoBoxOverlay(final Client client, final InfernoPlugin plugin, final SpriteManager spriteManager)
{
setPosition(OverlayPosition.BOTTOM_RIGHT);
setPriority(OverlayPriority.HIGH);
@@ -62,48 +66,56 @@ public class InfernoJadOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isShowPrayerHelp() || (plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTTOM_RIGHT
&& plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTH))
if (plugin.getPrayerDisplayMode() != InfernoPrayerDisplayMode.BOTTOM_RIGHT
&& plugin.getPrayerDisplayMode() != InfernoPrayerDisplayMode.BOTH)
{
return null;
}
InfernoJad.Attack attack = null;
int leastTicks = 999;
for (InfernoJad jad : plugin.getJads())
{
if (jad.getNextAttack() == null || jad.getTicksTillNextAttack() < 1)
{
continue;
}
if (jad.getTicksTillNextAttack() < leastTicks)
{
leastTicks = jad.getTicksTillNextAttack();
attack = jad.getNextAttack();
}
}
if (attack == null)
{
return null;
}
final BufferedImage prayerImage = getPrayerImage(attack);
imagePanelComponent.getChildren().clear();
imagePanelComponent.getChildren().add(new ImageComponent(prayerImage));
imagePanelComponent.setBackgroundColor(client.isPrayerActive(attack.getPrayer())
? ComponentConstants.STANDARD_BACKGROUND_COLOR
: NOT_ACTIVATED_BACKGROUND_COLOR);
if (plugin.getClosestAttack() != null)
{
final BufferedImage prayerImage = getPrayerImage(plugin.getClosestAttack());
imagePanelComponent.getChildren().add(new ImageComponent(prayerImage));
imagePanelComponent.setBackgroundColor(client.isPrayerActive(plugin.getClosestAttack().getPrayer())
? ComponentConstants.STANDARD_BACKGROUND_COLOR
: NOT_ACTIVATED_BACKGROUND_COLOR);
}
else
{
imagePanelComponent.setBackgroundColor(ComponentConstants.STANDARD_BACKGROUND_COLOR);
}
return imagePanelComponent.render(graphics);
}
private BufferedImage getPrayerImage(InfernoJad.Attack attack)
private BufferedImage getPrayerImage(InfernoNPC.Attack attack)
{
final int prayerSpriteID = attack == InfernoJad.Attack.MAGIC ? SpriteID.PRAYER_PROTECT_FROM_MAGIC : SpriteID.PRAYER_PROTECT_FROM_MISSILES;
return spriteManager.getSprite(prayerSpriteID, 0);
if (prayMeleeSprite == null)
{
prayMeleeSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MELEE, 0);
}
if (prayRangedSprite == null)
{
prayRangedSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0);
}
if (prayMagicSprite == null)
{
prayMagicSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0);
}
switch (attack)
{
case MELEE:
return prayMeleeSprite;
case RANGED:
return prayRangedSprite;
case MAGIC:
return prayMagicSprite;
}
return prayMagicSprite;
}
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
* 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.inferno;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.AnimationID;
import net.runelite.api.NPC;
import net.runelite.api.Prayer;
@Getter(AccessLevel.PACKAGE)
public class InfernoJad
{
private static final int TICKS_AFTER_ANIMATION = 4;
private NPC npc;
private Attack nextAttack;
private int ticksTillNextAttack;
InfernoJad(NPC npc)
{
this.npc = npc;
nextAttack = null;
ticksTillNextAttack = -1;
}
void updateNextAttack(Attack nextAttack)
{
this.nextAttack = nextAttack;
this.ticksTillNextAttack = TICKS_AFTER_ANIMATION;
}
void gameTick()
{
if (ticksTillNextAttack < 0)
{
return;
}
this.ticksTillNextAttack--;
if (ticksTillNextAttack < 0)
{
nextAttack = null;
}
}
@Getter(AccessLevel.PACKAGE)
enum Attack
{
MAGIC(AnimationID.JALTOK_JAD_MAGE_ATTACK, Prayer.PROTECT_FROM_MAGIC),
RANGE(AnimationID.JALTOK_JAD_RANGE_ATTACK, Prayer.PROTECT_FROM_MISSILES);
private final int animation;
private final Prayer prayer;
Attack(final int animation, final Prayer prayer)
{
this.animation = animation;
this.prayer = prayer;
}
}
}

View File

@@ -1,15 +1,15 @@
/*
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
* 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.
* 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 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
@@ -25,162 +25,376 @@
package net.runelite.client.plugins.inferno;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Prayer;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import org.apache.commons.lang3.ArrayUtils;
public class InfernoNPC
{
public enum Attackstyle
{
MAGE("Mage", Color.CYAN),
RANGE("Range", Color.GREEN),
MELEE("Melee", Color.WHITE),
RANDOM("Random", Color.ORANGE);
@Getter(AccessLevel.PACKAGE)
private String name;
@Getter(AccessLevel.PACKAGE)
private Color color;
Attackstyle(String s, Color c)
{
this.name = s;
this.color = c;
}
}
@Getter(AccessLevel.PACKAGE)
private NPC npc;
@Getter(AccessLevel.PACKAGE)
private String name;
private Type type;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private Attackstyle attackstyle;
private Attack nextAttack;
@Getter(AccessLevel.PACKAGE)
private int attackTicks;
@Getter(AccessLevel.PACKAGE)
private int priority;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private int ticksTillAttack = -1;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private boolean attacking = false;
@Getter(AccessLevel.PACKAGE)
private int attackAnimation;
@Getter(AccessLevel.PACKAGE)
private boolean isMidAttack = false;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private int distanceToPlayer = 0;
@Getter(AccessLevel.PACKAGE)
int textLocHeight;
private int ticksTillNextAttack;
private int lastAnimation;
private boolean lastCanAttack;
//0 = not in LOS, 1 = in LOS after move, 2 = in LOS
private final Map<WorldPoint, Integer> safeSpotCache;
InfernoNPC(NPC npc)
{
this.npc = npc;
textLocHeight = npc.getLogicalHeight() + 40;
switch (npc.getId())
this.type = Type.typeFromId(npc.getId());
this.nextAttack = type.getDefaultAttack();
this.ticksTillNextAttack = 0;
this.lastAnimation = -1;
this.lastCanAttack = false;
this.safeSpotCache = new HashMap();
}
void updateNextAttack(Attack nextAttack, int ticksTillNextAttack)
{
this.nextAttack = nextAttack;
this.ticksTillNextAttack = ticksTillNextAttack;
}
private void updateNextAttack(Attack nextAttack)
{
this.nextAttack = nextAttack;
}
boolean canAttack(Client client, WorldPoint target)
{
if (safeSpotCache.containsKey(target))
{
case NpcID.JALAKREKKET:
attackTicks = 4;
name = "lil mel";
attackAnimation = 7582;
attackstyle = Attackstyle.MELEE;
priority = 7;
break;
return safeSpotCache.get(target) == 2;
}
case NpcID.JALAKREKXIL:
attackTicks = 4;
name = "lil range";
attackAnimation = 7583;
attackstyle = Attackstyle.RANGE;
priority = 6;
break;
boolean hasLos = new WorldArea(target, 1, 1).hasLineOfSightTo(client, this.getNpc().getWorldArea());
boolean hasRange = this.getType().getDefaultAttack() == Attack.MELEE ? this.getNpc().getWorldArea().isInMeleeDistance(target)
: this.getNpc().getWorldArea().distanceTo(target) <= this.getType().getRange();
case NpcID.JALAKREKMEJ:
attackTicks = 4;
name = "lil mage";
attackAnimation = 7581;
attackstyle = Attackstyle.MAGE;
priority = 5;
break;
if (hasLos && hasRange)
{
safeSpotCache.put(target, 2);
}
case NpcID.JALMEJRAH:
attackTicks = 3;
name = "bat";
attackAnimation = 7578;
attackstyle = Attackstyle.RANGE;
priority = 4;
break;
return hasLos && hasRange;
}
case NpcID.JALAK:
attackTicks = 6;
name = "blob";
attackAnimation = 7583; // also 7581
attackstyle = Attackstyle.RANDOM;
priority = 3;
break;
boolean canMoveToAttack(Client client, WorldPoint target, List<WorldPoint> obstacles)
{
if (safeSpotCache.containsKey(target))
{
return safeSpotCache.get(target) == 1 || safeSpotCache.get(target) == 2;
}
case NpcID.JALIMKOT:
attackTicks = 4;
name = "meleer";
attackAnimation = 7597;
attackstyle = Attackstyle.MELEE;
priority = 2;
break;
final List<WorldPoint> realObstacles = new ArrayList<>();
for (WorldPoint obstacle : obstacles)
{
if (this.getNpc().getWorldArea().toWorldPointList().contains(obstacle))
{
continue;
}
case NpcID.JALXIL:
attackTicks = 4;
name = "ranger";
attackAnimation = 7605;
attackstyle = Attackstyle.RANGE;
priority = 1;
break;
realObstacles.add(obstacle);
}
case NpcID.JALZEK:
attackTicks = 4;
name = "mager";
attackAnimation = 7610;
attackstyle = Attackstyle.MAGE;
priority = 0;
break;
final WorldArea targetArea = new WorldArea(target, 1, 1);
WorldArea currentWorldArea = this.getNpc().getWorldArea();
default:
attackTicks = 0;
int steps = 0;
while (true)
{
// Prevent infinite loop in case of pathfinding failure
steps++;
if (steps > 30)
{
return false;
}
final WorldArea predictedWorldArea = currentWorldArea.calculateNextTravellingPoint(client, targetArea, true, x ->
{
for (WorldPoint obstacle : realObstacles)
{
if (new WorldArea(x, 1, 1).intersectsWith(new WorldArea(obstacle, 1, 1)))
{
return false;
}
}
return true;
});
// Will only happen when NPC is underneath player or moving out of scene (but this will never show on overlay)
if (predictedWorldArea == null)
{
safeSpotCache.put(target, 1);
return true;
}
if (predictedWorldArea == currentWorldArea)
{
safeSpotCache.put(target, 0);
return false;
}
boolean hasLos = new WorldArea(target, 1, 1).hasLineOfSightTo(client, predictedWorldArea);
boolean hasRange = this.getType().getDefaultAttack() == Attack.MELEE ? predictedWorldArea.isInMeleeDistance(target)
: predictedWorldArea.distanceTo(target) <= this.getType().getRange();
if (hasLos && hasRange)
{
safeSpotCache.put(target, 1);
return true;
}
currentWorldArea = predictedWorldArea;
}
}
public String info()
private boolean couldAttackPrevTick(Client client, WorldPoint lastPlayerLocation)
{
String info = "";
if (attacking)
{
info += ticksTillAttack;
}
//info += " D: " + distanceToPlayer;
return info;
return new WorldArea(lastPlayerLocation, 1, 1).hasLineOfSightTo(client, this.getNpc().getWorldArea());
}
void attacked()
void gameTick(Client client, WorldPoint lastPlayerLocation, boolean finalPhase)
{
ticksTillAttack = attackTicks;
attacking = true;
safeSpotCache.clear();
if (ticksTillNextAttack > 0)
{
this.ticksTillNextAttack--;
}
//Jad animation detection
if (this.getType() == Type.JAD && this.getNpc().getAnimation() != -1 && this.getNpc().getAnimation() != this.lastAnimation)
{
final InfernoNPC.Attack currentAttack = InfernoNPC.Attack.attackFromId(this.getNpc().getAnimation());
if (currentAttack != null && currentAttack != Attack.UNKNOWN)
{
this.updateNextAttack(currentAttack, this.getType().getTicksAfterAnimation());
}
}
if (ticksTillNextAttack <= 0)
{
switch (this.getType())
{
case ZUK:
if (this.getNpc().getAnimation() == AnimationID.TZKAL_ZUK)
{
if (finalPhase)
{
this.updateNextAttack(this.getType().getDefaultAttack(), 7);
}
else
{
this.updateNextAttack(this.getType().getDefaultAttack(), 10);
}
}
break;
case JAD:
if (this.getNextAttack() != Attack.UNKNOWN)
{
// Jad's cycle continuous after his animation + attack but there's no animation to alert it
this.updateNextAttack(this.getType().getDefaultAttack(), 8);
}
break;
case BLOB:
//RS pathfinding + LOS = hell, so if it can attack you the tick you were on previously, start attack cycle
if (!this.lastCanAttack && this.couldAttackPrevTick(client, lastPlayerLocation))
{
this.updateNextAttack(Attack.UNKNOWN, 3);
}
//If there's no animation when coming out of the safespot, the blob is detecting prayer
else if (!this.lastCanAttack && this.canAttack(client, client.getLocalPlayer().getWorldLocation()))
{
this.updateNextAttack(Attack.UNKNOWN, 4);
}
//This will activate another attack cycle
else if (this.getNpc().getAnimation() != -1)
{
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
break;
case BAT:
// Range + LOS check for bat because it suffers from the defense animation bug, also dont activate on "stand" animation
if (this.canAttack(client, client.getLocalPlayer().getWorldLocation())
&& this.getNpc().getAnimation() != AnimationID.JAL_MEJRAH_STAND && this.getNpc().getAnimation() != -1)
{
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
break;
case MELEE:
case RANGER:
case MAGE:
// For the meleer, ranger and mage the attack animation is always prioritized so only check for those
// Normal attack animation, doesnt suffer from defense animation bug. Activate usual attack cycle
if (this.getNpc().getAnimation() == AnimationID.JAL_IMKOT
|| this.getNpc().getAnimation() == AnimationID.JAL_XIL_RANGE_ATTACK || this.getNpc().getAnimation() == AnimationID.JAL_XIL_MELEE_ATTACK
|| this.getNpc().getAnimation() == AnimationID.JAL_ZEK_MAGE_ATTACK || this.getNpc().getAnimation() == AnimationID.JAL_ZEK_MELEE_ATTACK)
{
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
// Burrow into ground animation for meleer
else if (this.getNpc().getAnimation() == 7600)
{
this.updateNextAttack(this.getType().getDefaultAttack(), 12);
}
// Respawn enemy animation for mage
else if (this.getNpc().getAnimation() == 7611)
{
this.updateNextAttack(this.getType().getDefaultAttack(), 8);
}
break;
default:
if (this.getNpc().getAnimation() != -1)
{
// This will activate another attack cycle
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
break;
}
}
//Blob prayer detection
if (this.getType() == Type.BLOB && this.getTicksTillNextAttack() == 3
&& client.getLocalPlayer().getWorldLocation().distanceTo(this.getNpc().getWorldArea()) <= Type.BLOB.getRange())
{
InfernoNPC.Attack nextBlobAttack = InfernoNPC.Attack.UNKNOWN;
if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
{
nextBlobAttack = InfernoNPC.Attack.MAGIC;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
{
nextBlobAttack = InfernoNPC.Attack.RANGED;
}
this.updateNextAttack(nextBlobAttack);
}
// This is for jad (jad's animation lasts till after the attack is launched, which fucks up the attack cycle)
lastAnimation = this.getNpc().getAnimation();
// This is for blob (to check if player just came out of safespot)
lastCanAttack = this.canAttack(client, client.getLocalPlayer().getWorldLocation());
}
@Getter(AccessLevel.PACKAGE)
enum Attack
{
MELEE(Prayer.PROTECT_FROM_MELEE,
Color.ORANGE,
Color.RED,
new int[]{
AnimationID.JAL_NIB,
AnimationID.JAL_AK_MELEE_ATTACK,
AnimationID.JAL_IMKOT,
AnimationID.JAL_XIL_MELEE_ATTACK,
AnimationID.JAL_ZEK_MELEE_ATTACK, //TODO: Yt-HurKot attack animation
}),
RANGED(Prayer.PROTECT_FROM_MISSILES,
Color.GREEN,
new Color(0, 128, 0),
new int[]{
AnimationID.JAL_MEJRAH,
AnimationID.JAL_AK_RANGE_ATTACK,
AnimationID.JAL_XIL_RANGE_ATTACK,
AnimationID.JALTOK_JAD_RANGE_ATTACK,
}),
MAGIC(Prayer.PROTECT_FROM_MAGIC,
Color.CYAN,
Color.BLUE,
new int[]{
AnimationID.JAL_AK_MAGIC_ATTACK,
AnimationID.JAL_ZEK_MAGE_ATTACK,
AnimationID.JALTOK_JAD_MAGE_ATTACK
}),
UNKNOWN(null, Color.WHITE, Color.GRAY, new int[]{});
private final Prayer prayer;
private final Color normalColor;
private final Color criticalColor;
private final int[] animationIds;
Attack(Prayer prayer, Color normalColor, Color criticalColor, int[] animationIds)
{
this.prayer = prayer;
this.normalColor = normalColor;
this.criticalColor = criticalColor;
this.animationIds = animationIds;
}
static Attack attackFromId(int animationId)
{
for (Attack attack : Attack.values())
{
if (ArrayUtils.contains(attack.getAnimationIds(), animationId))
{
return attack;
}
}
return null;
}
}
@Getter(AccessLevel.PACKAGE)
enum Type
{
NIBBLER(new int[]{NpcID.JALNIB}, Attack.MELEE, 4, 99, 100),
BAT(new int[]{NpcID.JALMEJRAH}, Attack.RANGED, 3, 4, 7),
BLOB(new int[]{NpcID.JALAK}, Attack.UNKNOWN, 6, 15, 4),
MELEE(new int[]{NpcID.JALIMKOT}, Attack.MELEE, 4, 1, 3),
RANGER(new int[]{NpcID.JALXIL, NpcID.JALXIL_7702}, Attack.RANGED, 4, 98, 2),
MAGE(new int[]{NpcID.JALZEK, NpcID.JALZEK_7703}, Attack.MAGIC, 4, 98, 1),
JAD(new int[]{NpcID.JALTOKJAD, NpcID.JALTOKJAD_7704}, Attack.UNKNOWN, 3, 99, 0),
HEALER_JAD(new int[]{NpcID.YTHURKOT, NpcID.YTHURKOT_7701, NpcID.YTHURKOT_7705}, Attack.MELEE, 4, 1, 6),
ZUK(new int[]{NpcID.TZKALZUK}, Attack.UNKNOWN, 10, 99, 99),
HEALER_ZUK(new int[]{NpcID.JALMEJJAK}, Attack.UNKNOWN, -1, 99, 100);
private final int[] npcIds;
private final Attack defaultAttack;
private final int ticksAfterAnimation;
private final int range;
private final int priority;
Type(int[] npcIds, Attack defaultAttack, int ticksAfterAnimation, int range, int priority)
{
this.npcIds = npcIds;
this.defaultAttack = defaultAttack;
this.ticksAfterAnimation = ticksAfterAnimation;
this.range = range;
this.priority = priority;
}
static Type typeFromId(int npcId)
{
for (Type type : Type.values())
{
if (ArrayUtils.contains(type.getNpcIds(), npcId))
{
return type;
}
}
return null;
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.inferno;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.table.TableAlignment;
import net.runelite.client.ui.overlay.components.table.TableComponent;
@Singleton
public class InfernoNibblerOverlay extends Overlay
{
private final Client client;
private final InfernoPlugin plugin;
private final PanelComponent panelComponent = new PanelComponent();
@Inject
public InfernoNibblerOverlay(final Client client, final InfernoPlugin plugin)
{
this.client = client;
this.plugin = plugin;
setPosition(OverlayPosition.TOP_LEFT);
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isDisplayNibblerOverlay() || plugin.getNibblers().size() == 0 || client.getMapRegions()[0] != 9043)
{
return null;
}
panelComponent.getChildren().clear();
TableComponent tableComponent = new TableComponent();
tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT);
tableComponent.addRow("Nibblers Left: ", Integer.toString(plugin.getNibblers().size()));
panelComponent.getChildren().add(tableComponent);
return panelComponent.render(graphics);
}
}

View File

@@ -1,56 +1,46 @@
/*
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.inferno;
import com.google.common.base.Strings;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.Prayer;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode;
import net.runelite.client.plugins.inferno.displaymodes.InfernoSafespotDisplayMode;
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.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
@Singleton
public class InfernoOverlay extends Overlay
{
private final Client client;
private static final int TICK_PIXEL_SIZE = 60;
private static final int BOX_WIDTH = 10;
private static final int BOX_HEIGHT = 5;
private final InfernoPlugin plugin;
private final Client client;
@Inject
public InfernoOverlay(final Client client, final InfernoPlugin plugin)
private InfernoOverlay(final Client client, final InfernoPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGHEST);
this.client = client;
this.plugin = plugin;
}
@@ -58,55 +48,426 @@ public class InfernoOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
if (!client.isInInstancedRegion() || client.getMapRegions()[0] != 9043)
final Widget meleePrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MELEE);
final Widget rangePrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
final Widget magicPrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC);
if (plugin.isIndicateObstacles())
{
return null;
renderObstacles(graphics);
}
for (InfernoNPC monster : plugin.getMonsters().values())
if (plugin.getSafespotDisplayMode() == InfernoSafespotDisplayMode.AREA)
{
NPC npc = monster.getNpc();
//if (npc == null || !config.showPrayer()) return;
LocalPoint lp = npc.getLocalLocation();
if (lp != null)
renderAreaSafepots(graphics);
}
else if (plugin.getSafespotDisplayMode() == InfernoSafespotDisplayMode.INDIVIDUAL_TILES)
{
renderIndividualTilesSafespots(graphics);
}
for (InfernoNPC infernoNPC : plugin.getInfernoNpcs())
{
if (infernoNPC.getNpc().getConvexHull() != null)
{
Point point = Perspective.localToCanvas(client, lp, client.getPlane(), npc.getLogicalHeight());
if (point != null)
if (plugin.isIndicateNonSafespotted() && plugin.isNormalSafespots(infernoNPC)
&& infernoNPC.canAttack(client, client.getLocalPlayer().getWorldLocation()))
{
if (monster.getTicksTillAttack() == 1 || (monster.getName().equals("blob") && monster.getTicksTillAttack() <= 3))
{
renderTextLocation(graphics, monster, monster.info(), Color.GREEN);
}
else
{
renderTextLocation(graphics, monster, monster.info(), Color.RED);
}
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.RED);
}
if (plugin.isIndicateTemporarySafespotted() && plugin.isNormalSafespots(infernoNPC)
&& infernoNPC.canMoveToAttack(client, client.getLocalPlayer().getWorldLocation(), plugin.getObstacles()))
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.YELLOW);
}
if (plugin.isIndicateSafespotted() && plugin.isNormalSafespots(infernoNPC))
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.GREEN);
}
if (plugin.isIndicateNibblers() && infernoNPC.getType() == InfernoNPC.Type.NIBBLER
&& (!plugin.isIndicateCentralNibbler() || plugin.getCentralNibbler() != infernoNPC))
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
}
if (plugin.isIndicateCentralNibbler() && infernoNPC.getType() == InfernoNPC.Type.NIBBLER
&& plugin.getCentralNibbler() == infernoNPC)
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.BLUE);
}
if (plugin.isIndicateActiveHealersJad() && infernoNPC.getType() == InfernoNPC.Type.HEALER_JAD
&& infernoNPC.getNpc().getInteracting() != client.getLocalPlayer())
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
}
if (plugin.isIndicateActiveHealersZuk() && infernoNPC.getType() == InfernoNPC.Type.HEALER_ZUK
&& infernoNPC.getNpc().getInteracting() != client.getLocalPlayer())
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
}
}
if (plugin.isIndicateNpcPosition(infernoNPC))
{
renderNpcLocation(graphics, infernoNPC);
}
if (plugin.isTicksOnNpc(infernoNPC) && infernoNPC.getTicksTillNextAttack() > 0)
{
renderTicksOnNpc(graphics, infernoNPC);
}
}
if ((plugin.getPrayerDisplayMode() == InfernoPrayerDisplayMode.PRAYER_TAB
|| plugin.getPrayerDisplayMode() == InfernoPrayerDisplayMode.BOTH)
&& (meleePrayerWidget != null && !meleePrayerWidget.isHidden()
&& rangePrayerWidget != null && !rangePrayerWidget.isHidden()
&& magicPrayerWidget != null && !magicPrayerWidget.isHidden()))
{
renderPrayerIconOverlay(graphics);
if (plugin.isDescendingBoxes())
{
renderDescendingBoxes(graphics);
}
}
return null;
}
// renders text location
private static void renderTextLocation(Graphics2D graphics, InfernoNPC actor, String text, Color color)
private void renderObstacles(Graphics2D graphics)
{
graphics.setFont(new Font("Arial", Font.BOLD, 15));
Point textLocation = actor.getNpc().getCanvasTextLocation(graphics, text, actor.textLocHeight + 40);
if (Strings.isNullOrEmpty(text))
for (WorldPoint worldPoint : plugin.getObstacles())
{
return;
}
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
if (textLocation != null)
{
int x = textLocation.getX();
int y = textLocation.getY();
if (localPoint == null)
{
continue;
}
graphics.setColor(Color.BLACK);
graphics.drawString(text, x + 1, y + 1);
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
graphics.setColor(color);
graphics.drawString(text, x, y);
if (tilePoly == null)
{
continue;
}
OverlayUtil.renderPolygon(graphics, tilePoly, Color.BLUE);
}
}
private void renderAreaSafepots(Graphics2D graphics)
{
for (int safeSpotId : plugin.getSafeSpotAreas().keySet())
{
if (safeSpotId > 6)
{
continue;
}
Color colorEdge1 = null;
Color colorEdge2 = null;
Color colorFill = null;
switch (safeSpotId)
{
case 0:
colorEdge1 = Color.WHITE;
colorFill = Color.WHITE;
break;
case 1:
colorEdge1 = Color.RED;
colorFill = Color.RED;
break;
case 2:
colorEdge1 = Color.GREEN;
colorFill = Color.GREEN;
break;
case 3:
colorEdge1 = Color.BLUE;
colorFill = Color.BLUE;
break;
case 4:
colorEdge1 = Color.RED;
colorEdge2 = Color.GREEN;
colorFill = Color.YELLOW;
break;
case 5:
colorEdge1 = Color.RED;
colorEdge2 = Color.BLUE;
colorFill = new Color(255, 0, 255);
break;
case 6:
colorEdge1 = Color.GREEN;
colorEdge2 = Color.BLUE;
colorFill = new Color(0, 255, 255);
break;
default:
continue;
}
//Add all edges, calculate average edgeSize and indicate tiles
final List<int[][]> allEdges = new ArrayList<>();
int edgeSizeSquared = 0;
for (WorldPoint worldPoint : plugin.getSafeSpotAreas().get(safeSpotId))
{
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
if (localPoint == null)
{
continue;
}
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
if (tilePoly == null)
{
continue;
}
OverlayUtil.renderAreaTilePolygon(graphics, tilePoly, colorFill);
final int[][] edge1 = new int[][]{{tilePoly.xpoints[0], tilePoly.ypoints[0]}, {tilePoly.xpoints[1], tilePoly.ypoints[1]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[0] - tilePoly.xpoints[1], 2) + Math.pow(tilePoly.ypoints[0] - tilePoly.ypoints[1], 2);
allEdges.add(edge1);
final int[][] edge2 = new int[][]{{tilePoly.xpoints[1], tilePoly.ypoints[1]}, {tilePoly.xpoints[2], tilePoly.ypoints[2]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[1] - tilePoly.xpoints[2], 2) + Math.pow(tilePoly.ypoints[1] - tilePoly.ypoints[2], 2);
allEdges.add(edge2);
final int[][] edge3 = new int[][]{{tilePoly.xpoints[2], tilePoly.ypoints[2]}, {tilePoly.xpoints[3], tilePoly.ypoints[3]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[2] - tilePoly.xpoints[3], 2) + Math.pow(tilePoly.ypoints[2] - tilePoly.ypoints[3], 2);
allEdges.add(edge3);
final int[][] edge4 = new int[][]{{tilePoly.xpoints[3], tilePoly.ypoints[3]}, {tilePoly.xpoints[0], tilePoly.ypoints[0]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[3] - tilePoly.xpoints[0], 2) + Math.pow(tilePoly.ypoints[3] - tilePoly.ypoints[0], 2);
allEdges.add(edge4);
}
if (allEdges.size() <= 0)
{
continue;
}
edgeSizeSquared /= allEdges.size();
//Find and indicate unique edges
final int toleranceSquared = (int) Math.ceil(edgeSizeSquared / 6);
for (int i = 0; i < allEdges.size(); i++)
{
int[][] baseEdge = allEdges.get(i);
boolean duplicate = false;
for (int j = 0; j < allEdges.size(); j++)
{
if (i == j)
{
continue;
}
int[][] checkEdge = allEdges.get(j);
if (edgeEqualsEdge(baseEdge, checkEdge, toleranceSquared))
{
duplicate = true;
break;
}
}
if (!duplicate)
{
OverlayUtil.renderFullLine(graphics, baseEdge, colorEdge1);
if (colorEdge2 != null)
{
OverlayUtil.renderDashedLine(graphics, baseEdge, colorEdge2);
}
}
}
}
}
private void renderIndividualTilesSafespots(Graphics2D graphics)
{
for (WorldPoint worldPoint : plugin.getSafeSpotMap().keySet())
{
final int safeSpotId = plugin.getSafeSpotMap().get(worldPoint);
if (safeSpotId > 6)
{
continue;
}
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
if (localPoint == null)
{
continue;
}
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
if (tilePoly == null)
{
continue;
}
Color color;
switch (safeSpotId)
{
case 0:
color = Color.WHITE;
break;
case 1:
color = Color.RED;
break;
case 2:
color = Color.GREEN;
break;
case 3:
color = Color.BLUE;
break;
case 4:
color = new Color(255, 255, 0);
break;
case 5:
color = new Color(255, 0, 255);
break;
case 6:
color = new Color(0, 255, 255);
break;
default:
continue;
}
OverlayUtil.renderPolygon(graphics, tilePoly, color);
}
}
private void renderTicksOnNpc(Graphics2D graphics, InfernoNPC infernoNPC)
{
final Color color = (infernoNPC.getTicksTillNextAttack() == 1
|| (infernoNPC.getType() == InfernoNPC.Type.BLOB && infernoNPC.getTicksTillNextAttack() == 4))
? infernoNPC.getNextAttack().getCriticalColor() : infernoNPC.getNextAttack().getNormalColor();
final Point canvasPoint = infernoNPC.getNpc().getCanvasTextLocation(
graphics, String.valueOf(infernoNPC.getTicksTillNextAttack()), 0);
OverlayUtil.renderTextLocation(graphics, String.valueOf(infernoNPC.getTicksTillNextAttack()),
plugin.getTextSize(), plugin.getFontStyle().getFont(), color, canvasPoint, false, 0);
}
private void renderNpcLocation(Graphics2D graphics, InfernoNPC infernoNPC)
{
final LocalPoint localPoint = LocalPoint.fromWorld(client, infernoNPC.getNpc().getWorldLocation());
if (localPoint != null)
{
final Polygon tilePolygon = Perspective.getCanvasTilePoly(client, localPoint);
if (tilePolygon != null)
{
OverlayUtil.renderPolygon(graphics, tilePolygon, Color.BLUE);
}
}
}
private void renderDescendingBoxes(Graphics2D graphics)
{
for (Integer tick : plugin.getUpcomingAttacks().keySet())
{
final Map<InfernoNPC.Attack, Integer> attackPriority = plugin.getUpcomingAttacks().get(tick);
int bestPriority = 999;
InfernoNPC.Attack bestAttack = null;
for (Map.Entry<InfernoNPC.Attack, Integer> attackEntry : attackPriority.entrySet())
{
if (attackEntry.getValue() < bestPriority)
{
bestAttack = attackEntry.getKey();
bestPriority = attackEntry.getValue();
}
}
for (InfernoNPC.Attack currentAttack : attackPriority.keySet())
{
//TODO: Config values for these colors
final Color color = (tick == 1 && currentAttack == bestAttack) ? Color.RED : Color.ORANGE;
final Widget prayerWidget = client.getWidget(currentAttack.getPrayer().getWidgetInfo());
int baseX = (int) prayerWidget.getBounds().getX();
baseX += prayerWidget.getBounds().getWidth() / 2;
baseX -= BOX_WIDTH / 2;
int baseY = (int) prayerWidget.getBounds().getY() - tick * TICK_PIXEL_SIZE - BOX_HEIGHT;
baseY += TICK_PIXEL_SIZE - ((plugin.getLastTick() + 600 - System.currentTimeMillis()) / 600.0 * TICK_PIXEL_SIZE);
final Rectangle boxRectangle = new Rectangle(BOX_WIDTH, BOX_HEIGHT);
boxRectangle.translate(baseX, baseY);
if (currentAttack == bestAttack)
{
OverlayUtil.renderFilledPolygon(graphics, boxRectangle, color);
}
else if (plugin.isIndicateNonPriorityDescendingBoxes())
{
OverlayUtil.renderOutlinePolygon(graphics, boxRectangle, color);
}
}
}
}
private void renderPrayerIconOverlay(Graphics2D graphics)
{
if (plugin.getClosestAttack() != null)
{
// Prayer indicator in prayer tab
InfernoNPC.Attack prayerForAttack = null;
if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
{
prayerForAttack = InfernoNPC.Attack.MAGIC;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
{
prayerForAttack = InfernoNPC.Attack.RANGED;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MELEE))
{
prayerForAttack = InfernoNPC.Attack.MELEE;
}
if (plugin.getClosestAttack() != prayerForAttack || plugin.isIndicateWhenPrayingCorrectly())
{
final Widget prayerWidget = client.getWidget(plugin.getClosestAttack().getPrayer().getWidgetInfo());
final Rectangle prayerRectangle = new Rectangle((int) prayerWidget.getBounds().getWidth(),
(int) prayerWidget.getBounds().getHeight());
prayerRectangle.translate((int) prayerWidget.getBounds().getX(), (int) prayerWidget.getBounds().getY());
//TODO: Config values for these colors
Color prayerColor;
if (plugin.getClosestAttack() == prayerForAttack)
{
prayerColor = Color.GREEN;
}
else
{
prayerColor = Color.RED;
}
OverlayUtil.renderOutlinePolygon(graphics, prayerRectangle, prayerColor);
}
}
}
private boolean edgeEqualsEdge(int[][] edge1, int[][] edge2, int toleranceSquared)
{
return (pointEqualsPoint(edge1[0], edge2[0], toleranceSquared) && pointEqualsPoint(edge1[1], edge2[1], toleranceSquared))
|| (pointEqualsPoint(edge1[0], edge2[1], toleranceSquared) && pointEqualsPoint(edge1[1], edge2[0], toleranceSquared));
}
private boolean pointEqualsPoint(int[] point1, int[] point2, int toleranceSquared)
{
double distanceSquared = Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2);
return distanceSquared <= toleranceSquared;
}
}

View File

@@ -1,138 +0,0 @@
package net.runelite.client.plugins.inferno;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Prayer;
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.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
public class InfernoPrayerOverlay extends Overlay
{
private static final int TICK_PIXEL_SIZE = 60;
private static final int BLOB_WIDTH = 10;
private static final int BLOB_HEIGHT = 5;
private final InfernoPlugin plugin;
private final Client client;
@Inject
private InfernoPrayerOverlay(final Client client, final InfernoPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGHEST);
this.client = client;
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
if (client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC).isHidden()
|| client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES).isHidden())
{
return null;
}
InfernoJad.Attack prayerForAttack = null;
if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
{
prayerForAttack = InfernoJad.Attack.MAGIC;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
{
prayerForAttack = InfernoJad.Attack.RANGE;
}
InfernoJad.Attack closestAttack = null;
int leastTicks = 999;
for (InfernoJad jad : plugin.getJads())
{
if (jad.getNextAttack() == null || jad.getTicksTillNextAttack() < 1)
{
continue;
}
if (jad.getTicksTillNextAttack() < leastTicks)
{
leastTicks = jad.getTicksTillNextAttack();
closestAttack = jad.getNextAttack();
}
if (!plugin.isDescendingBoxes() || !plugin.isShowPrayerHelp()
|| (plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.PRAYER_TAB
&& plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTH))
{
continue;
}
final Widget prayerWidget = jad.getNextAttack() == InfernoJad.Attack.MAGIC
? client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC) : client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
int baseX = (int) prayerWidget.getBounds().getX();
baseX += prayerWidget.getBounds().getWidth() / 2;
baseX -= BLOB_WIDTH / 2;
int baseY = (int) prayerWidget.getBounds().getY() - jad.getTicksTillNextAttack() * TICK_PIXEL_SIZE - BLOB_HEIGHT;
baseY += TICK_PIXEL_SIZE - ((plugin.getLastTick() + 600 - System.currentTimeMillis()) / 600.0 * TICK_PIXEL_SIZE);
final Polygon blob = new Polygon(new int[]{0, BLOB_WIDTH, BLOB_WIDTH, 0}, new int[]{0, 0, BLOB_HEIGHT, BLOB_HEIGHT}, 4);
blob.translate(baseX, baseY);
OverlayUtil.renderPolygon(graphics, blob, Color.ORANGE);
}
if (plugin.isShowPrayerHelp() && closestAttack != null
&& (closestAttack != prayerForAttack || plugin.isIndicateWhenPrayingCorrectly())
&& (plugin.getPrayerOverlayMode() == InfernoPrayerOverlayMode.PRAYER_TAB
|| plugin.getPrayerOverlayMode() == InfernoPrayerOverlayMode.BOTH))
{
final Widget prayerWidget = closestAttack == InfernoJad.Attack.MAGIC
? client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC) : client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
final Polygon prayer = new Polygon(
new int[]{0, (int) prayerWidget.getBounds().getWidth(), (int) prayerWidget.getBounds().getWidth(), 0},
new int[]{0, 0, (int) prayerWidget.getBounds().getHeight(), (int) prayerWidget.getBounds().getHeight()},
4);
prayer.translate((int) prayerWidget.getBounds().getX(), (int) prayerWidget.getBounds().getY());
Color prayerColor;
if (closestAttack == prayerForAttack)
{
prayerColor = Color.GREEN;
}
else
{
prayerColor = Color.RED;
}
OverlayUtil.renderPolygon(graphics, prayer, prayerColor);
}
if (plugin.isIndicateActiveHealers())
{
for (NPC healer : plugin.getActiveHealers())
{
if (healer.getConvexHull() == null)
{
continue;
}
OverlayUtil.renderPolygon(graphics, healer.getConvexHull(), Color.CYAN);
}
}
return null;
}
}

View File

@@ -1,19 +0,0 @@
package net.runelite.client.plugins.inferno;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum InfernoPrayerOverlayMode
{
PRAYER_TAB("Prayer Tab"),
BOTTOM_RIGHT("Bottom Right"),
BOTH("Both");
private final String name;
@Override
public String toString()
{
return name;
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2019, Kyleeld <https://github.com/kyleeld>
* Copyright (c) 2019, openosrs <https://openosrs.com>
* Copyright (c) 2019, RuneLitePlus <https://runelitepl.us>
*
* All rights reserved.
*
@@ -28,18 +28,21 @@ package net.runelite.client.plugins.inferno;
import com.google.common.collect.ImmutableMap;
import java.awt.Color;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.plugins.inferno.displaymodes.InfernoNamingDisplayMode;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
class InfernoWaveMappings
{
@Getter(AccessLevel.PACKAGE)
private static final ImmutableMap<Integer, int[]> waveMapping;
private static final Map<Integer, int[]> waveMapping;
@Getter(AccessLevel.PACKAGE)
private static final ImmutableMap<Integer, String> npcNameMapping;
private static final Map<Integer, String> npcNameMappingComplex;
@Getter(AccessLevel.PACKAGE)
private static final Map<Integer, String> npcNameMappingSimple;
static
{
@@ -108,30 +111,43 @@ class InfernoWaveMappings
waveMapBuilder.put(61, new int[]{32, 32, 32, 85, 165, 240, 370, 490});
waveMapBuilder.put(62, new int[]{32, 32, 32, 85, 85, 165, 240, 370, 490});
waveMapBuilder.put(63, new int[]{32, 32, 32, 165, 165, 240, 370, 490});
waveMapBuilder.put(64, new int[]{32, 32, 32, 85, 240, 240, 370, 490});
waveMapBuilder.put(65, new int[]{32, 32, 32, 85, 370, 370, 490});
waveMapBuilder.put(66, new int[]{32, 32, 32, 85, 490, 490});
waveMapBuilder.put(64, new int[]{32, 32, 32, 240, 240, 370, 490});
waveMapBuilder.put(65, new int[]{32, 32, 32, 370, 370, 490});
waveMapBuilder.put(66, new int[]{32, 32, 32, 490, 490});
waveMapBuilder.put(67, new int[]{900});
waveMapBuilder.put(68, new int[]{900, 900, 900});
waveMapBuilder.put(69, new int[]{1400});
waveMapping = waveMapBuilder.build();
ImmutableMap.Builder<Integer, String> nameMapBuilder = new ImmutableMap.Builder<>();
ImmutableMap.Builder<Integer, String> nameMapBuilderSimple = new ImmutableMap.Builder<>();
nameMapBuilder.put(32, "Jal-Nib - Level 32");
nameMapBuilder.put(85, "Jal-MejRah - Level 85");
nameMapBuilder.put(165, "Jal-Ak - Level 165");
nameMapBuilder.put(240, "Jal-ImKot - Level 240");
nameMapBuilder.put(370, "Jal-Xil - Level 370");
nameMapBuilder.put(490, "Jal-Zek - Level 490");
nameMapBuilder.put(900, "JalTok-Jad - Level 900");
nameMapBuilder.put(1400, "TzKal-Zuk - Level 1400");
nameMapBuilderSimple.put(32, "Nibbler");
nameMapBuilderSimple.put(85, "Bat");
nameMapBuilderSimple.put(165, "Blob");
nameMapBuilderSimple.put(240, "Meleer");
nameMapBuilderSimple.put(370, "Ranger");
nameMapBuilderSimple.put(490, "Mage");
nameMapBuilderSimple.put(900, "Jad");
nameMapBuilderSimple.put(1400, "Zuk");
npcNameMapping = nameMapBuilder.build();
npcNameMappingSimple = nameMapBuilderSimple.build();
ImmutableMap.Builder<Integer, String> nameMapBuilderComplex = new ImmutableMap.Builder<>();
nameMapBuilderComplex.put(32, "Jal-Nib");
nameMapBuilderComplex.put(85, "Jal-MejRah");
nameMapBuilderComplex.put(165, "Jal-Ak");
nameMapBuilderComplex.put(240, "Jal-ImKot");
nameMapBuilderComplex.put(370, "Jal-Xil");
nameMapBuilderComplex.put(490, "Jal-Zek");
nameMapBuilderComplex.put(900, "JalTok-Jad");
nameMapBuilderComplex.put(1400, "TzKal-Zuk");
npcNameMappingComplex = nameMapBuilderComplex.build();
}
static void addWaveComponent(PanelComponent panelComponent, String header, int wave, Color titleColor, Color color)
static void addWaveComponent(InfernoPlugin plugin, PanelComponent panelComponent, String header, int wave, Color titleColor, Color color)
{
int[] monsters = waveMapping.get(wave);
@@ -160,7 +176,23 @@ class InfernoWaveMappings
TitleComponent.TitleComponentBuilder builder = TitleComponent.builder();
builder.text(count + "x " + npcNameMapping.get(monsterType));
String npcNameText = "";
if (plugin.getNpcNaming() == InfernoNamingDisplayMode.SIMPLE)
{
npcNameText += npcNameMappingSimple.get(monsterType);
}
else
{
npcNameText += npcNameMappingComplex.get(monsterType);
}
if (plugin.isNpcLevels())
{
npcNameText += " (" + monsterType + ")";
}
builder.text(count + "x " + npcNameText);
builder.color(color);
panelComponent.getChildren().add(builder.build());

View File

@@ -8,6 +8,7 @@ import java.awt.Graphics2D;
import lombok.AccessLevel;
import lombok.Setter;
import static net.runelite.client.plugins.inferno.InfernoWaveMappings.addWaveComponent;
import net.runelite.client.plugins.inferno.displaymodes.InfernoWaveDisplayMode;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@@ -47,6 +48,7 @@ public class InfernoWaveOverlay extends Overlay
displayMode == InfernoWaveDisplayMode.BOTH)
{
addWaveComponent(
plugin,
panelComponent,
"Current Wave (Wave " + plugin.getCurrentWaveNumber() + ")",
plugin.getCurrentWaveNumber(),
@@ -59,6 +61,7 @@ public class InfernoWaveOverlay extends Overlay
displayMode == InfernoWaveDisplayMode.BOTH)
{
addWaveComponent(
plugin,
panelComponent,
"Next Wave (Wave " + plugin.getNextWaveNumber() + ")",
plugin.getNextWaveNumber(),

View File

@@ -0,0 +1,7 @@
package net.runelite.client.plugins.inferno.displaymodes;
public enum InfernoNamingDisplayMode
{
SIMPLE,
COMPLEX
}

View File

@@ -0,0 +1,8 @@
package net.runelite.client.plugins.inferno.displaymodes;
public enum InfernoPrayerDisplayMode
{
PRAYER_TAB,
BOTTOM_RIGHT,
BOTH
}

View File

@@ -0,0 +1,25 @@
package net.runelite.client.plugins.inferno.displaymodes;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PACKAGE)
public enum InfernoSafespotDisplayMode
{
OFF("Off"),
INDIVIDUAL_TILES("Individual tiles"),
AREA("Area (lower fps)");
final private String name;
InfernoSafespotDisplayMode(String name)
{
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
}

View File

@@ -22,7 +22,7 @@
* (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.inferno;
package net.runelite.client.plugins.inferno.displaymodes;
import lombok.RequiredArgsConstructor;

View File

@@ -0,0 +1,25 @@
package net.runelite.client.plugins.inferno.displaymodes;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PACKAGE)
public enum InfernoZukShieldDisplayMode
{
OFF("Off"),
LIVE("Live (follow shield)"),
PREDICT("Predict (NOT WORKING YET)");
final private String name;
InfernoZukShieldDisplayMode(String name)
{
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
}

View File

@@ -72,6 +72,49 @@ public class OverlayUtil
graphics.setStroke(originalStroke);
}
public static void renderOutlinePolygon(Graphics2D graphics, Shape poly, Color color)
{
graphics.setColor(color);
final Stroke originalStroke = graphics.getStroke();
graphics.setStroke(new BasicStroke(2));
graphics.draw(poly);
graphics.setStroke(originalStroke);
}
public static void renderFilledPolygon(Graphics2D graphics, Shape poly, Color color)
{
graphics.setColor(color);
final Stroke originalStroke = graphics.getStroke();
graphics.setStroke(new BasicStroke(2));
graphics.draw(poly);
graphics.fill(poly);
graphics.setStroke(originalStroke);
}
public static void renderAreaTilePolygon(Graphics2D graphics, Shape poly, Color color)
{
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 10));
graphics.fill(poly);
}
public static void renderFullLine(Graphics2D graphics, int[][] line, Color color)
{
graphics.setColor(color);
final Stroke originalStroke = graphics.getStroke();
graphics.setStroke(new BasicStroke(2));
graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]);
graphics.setStroke(originalStroke);
}
public static void renderDashedLine(Graphics2D graphics, int[][] line, Color color)
{
graphics.setColor(color);
final Stroke originalStroke = graphics.getStroke();
graphics.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0));
graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]);
graphics.setStroke(originalStroke);
}
public static void renderPolygonThin(Graphics2D graphics, Polygon poly, Color color)
{
graphics.setColor(color);