diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 691a56bcdb..451d893b38 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 }}" \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 619e3921c6..b478c70501 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -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 \ No newline at end of file diff --git a/.github/workflows/scraper.yml b/.github/workflows/scraper.yml index 62e7f738a2..591bbd73f6 100644 --- a/.github/workflows/scraper.yml +++ b/.github/workflows/scraper.yml @@ -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 \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index f6ec51e2e4..31269fc102 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -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 \ No newline at end of file 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 84233ef310..58f9a56ac3 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -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; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoConfig.java index cd3eba1852..6658a37345 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoConfig.java @@ -25,56 +25,208 @@ package net.runelite.client.plugins.inferno; import java.awt.Color; +import java.awt.Font; +import lombok.AllArgsConstructor; +import lombok.Getter; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; -import net.runelite.client.config.ConfigTitleSection; -import net.runelite.client.config.Title; +import net.runelite.client.config.ConfigSection; +import net.runelite.client.plugins.inferno.displaymodes.InfernoNamingDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoSafespotDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoWaveDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoZukShieldDisplayMode; @ConfigGroup("inferno") public interface InfernoConfig extends Config { - @ConfigTitleSection( - keyName = "prayer", - position = 0, + + @ConfigSection( name = "Prayer", - description = "" + description = "Configuration options forPprayer", + position = 0, + keyName = "PrayerSection" ) - default Title prayer() + default boolean prayerSection() { - return new Title(); + return false; + } + + @ConfigSection( + name = "Safespots", + description = "Configuration options for Safespots", + position = 1, + keyName = "SafespotsSection" + ) + default boolean safespotsSection() + { + return false; + } + + @ConfigSection( + name = "Waves", + description = "Configuration options for Waves", + position = 2, + keyName = "WavesSection" + ) + default boolean wavesSection() + { + return false; + } + + @ConfigSection( + name = "ExtraSection", + description = "Configuration options for Extras", + position = 3, + keyName = "ExtraSection" + ) + default boolean extraSection() + { + return false; + } + + @ConfigSection( + name = "Nibblers", + description = "Configuration options for Nibblers", + position = 4, + keyName = "NibblersSection" + ) + default boolean nibblersSection() + { + return false; + } + + @ConfigSection( + name = "Bats", + description = "Configuration options for Bats", + position = 5, + keyName = "BatsSection" + ) + default boolean batsSection() + { + return false; + } + + @ConfigSection( + name = "Blobs", + description = "Configuration options for Blobs", + position = 6, + keyName = "BlobsSection" + ) + default boolean blobsSection() + { + return false; + } + + @ConfigSection( + name = "Meleers", + description = "Configuration options for Meleers", + position = 7, + keyName = "MeleersSection" + ) + default boolean meleersSection() + { + return false; + } + + @ConfigSection( + name = "Rangers", + description = "Configuration options for Rangers", + position = 8, + keyName = "RangersSection" + ) + default boolean rangersSection() + { + return false; + } + + @ConfigSection( + name = "Magers", + description = "Configuration options for Magers", + position = 9, + keyName = "MagersSection" + ) + default boolean magersSection() + { + return false; + } + + @ConfigSection( + name = "Jad", + description = "Configuration options for Jad", + position = 10, + keyName = "JadSection" + ) + default boolean jadSection() + { + return false; + } + + @ConfigSection( + name = "Jad Healers", + description = "Configuration options for Jad Healers", + position = 11, + keyName = "JadHealersSection" + ) + default boolean jadHealersSection() + { + return false; + } + + @ConfigSection( + name = "Zuk", + description = "Configuration options for Zuk", + position = 12, + keyName = "ZukSection" + ) + default boolean zukSection() + { + return false; + } + + @ConfigSection( + name = "Zuk Healers", + description = "Configuration options for Zuk Healers", + position = 13, + keyName = "ZukHealersSection" + ) + default boolean zukHealersSection() + { + return false; + } + + + @ConfigItem( + position = 0, + keyName = "prayerDisplayMode", + name = "Prayer Display Mode", + description = "Display prayer indicator in the prayer tab or in the bottom right corner of the screen", + section = "PrayerSection" + ) + default InfernoPrayerDisplayMode prayerDisplayMode() + { + return InfernoPrayerDisplayMode.BOTH; } @ConfigItem( position = 1, - keyName = "Prayer Helper", - name = "Prayer Helper", - description = "Indicates the correct prayer", - titleSection = "prayer" + keyName = "indicateWhenPrayingCorrectly", + name = "Indicate When Praying Correctly", + description = "Indicate the correct prayer, even if you are already praying that prayer", + section = "PrayerSection" ) - default boolean showPrayerHelp() + default boolean indicateWhenPrayingCorrectly() { return true; } @ConfigItem( position = 2, - keyName = "prayerHelperMode", - name = "Prayer Helper Mode", - description = "Display prayer indicator in the prayer tab or in the bottom right corner of the screen", - titleSection = "prayer" - ) - default InfernoPrayerOverlayMode prayerOverlayMode() - { - return InfernoPrayerOverlayMode.PRAYER_TAB; - } - - @ConfigItem( - position = 3, keyName = "descendingBoxes", name = "Descending Boxes", description = "Draws timing boxes above the prayer icons, as if you were playing Piano Tiles", - titleSection = "prayer" + section = "PrayerSection" ) default boolean descendingBoxes() { @@ -82,69 +234,83 @@ public interface InfernoConfig extends Config } @ConfigItem( - position = 4, - keyName = "indicateWhenPrayingCorrectly", - name = "Indicate When Praying Correctly", - description = "Indicate the correct prayer, even if you are already praying that prayer", - titleSection = "prayer" + position = 3, + keyName = "indicateNonPriorityDescendingBoxes", + name = "Indicate Non-Priority Boxes", + description = "Render descending boxes for prayers that are not the priority prayer for that tick", + section = "PrayerSection" ) - default boolean indicateWhenPrayingCorrectly() + default boolean indicateNonPriorityDescendingBoxes() { - return false; + return true; } - @ConfigTitleSection( - keyName = "monsters", - position = 5, - name = "Monsters", - description = "" + @ConfigItem( + position = 4, + keyName = "safespotDisplayMode", + name = "Tile Safespots", + description = "Indicate safespots on the ground: safespot (white), pray melee (red), pray range (green), pray magic (blue) and combinations of those", + section = "SafespotsSection" ) - default Title monsters() + default InfernoSafespotDisplayMode safespotDisplayMode() { - return new Title(); + return InfernoSafespotDisplayMode.AREA; + } + + @ConfigItem( + position = 5, + keyName = "safespotsCheckSize", + name = "Tile Safespots Check Size", + description = "The size of the area around the player that should be checked for safespots (SIZE x SIZE area)", + section = "SafespotsSection" + ) + default int safespotsCheckSize() + { + return 6; } @ConfigItem( position = 6, - keyName = "Nibbler Overlay", - name = "Nibbler Overlay", - description = "Shows if there are any Nibblers left", - titleSection = "monsters" + keyName = "indicateNonSafespotted", + name = "Non-safespotted NPC's Overlay", + description = "Red overlay for NPC's that can attack you", + section = "SafespotsSection" ) - default boolean displayNibblerOverlay() + default boolean indicateNonSafespotted() { return false; } @ConfigItem( position = 7, - keyName = "indicateActiveHealers", - name = "Indicate Active Healers", - description = "Indicate healers that are still healing Jad", - titleSection = "monsters" + keyName = "indicateTemporarySafespotted", + name = "Temporary safespotted NPC's Overlay", + description = "Orange overlay for NPC's that have to move to attack you", + section = "SafespotsSection" ) - default boolean indicateActiveHealers() + default boolean indicateTemporarySafespotted() { - return true; - } - - @ConfigTitleSection( - keyName = "waves", - position = 8, - name = "Waves", - description = "" - ) - default Title waves() - { - return new Title(); + return false; } @ConfigItem( - position = 9, + position = 8, + keyName = "indicateSafespotted", + name = "Safespotted NPC's Overlay", + description = "Green overlay for NPC's that are safespotted (can't attack you)", + section = "SafespotsSection" + ) + default boolean indicateSafespotted() + { + return false; + } + + @ConfigItem( + position = 0, keyName = "waveDisplay", - name = "Wave display", + name = "Wave Display", description = "Shows monsters that will spawn on the selected wave(s).", - titleSection = "waves" + section = "WavesSection" ) default InfernoWaveDisplayMode waveDisplay() { @@ -152,11 +318,35 @@ public interface InfernoConfig extends Config } @ConfigItem( - position = 10, + position = 1, + keyName = "npcNaming", + name = "NPC Naming", + description = "Simple (ex: Bat) or Complex (ex: Jal-MejRah) NPC naming", + section = "WavesSection" + ) + default InfernoNamingDisplayMode npcNaming() + { + return InfernoNamingDisplayMode.SIMPLE; + } + + @ConfigItem( + position = 2, + keyName = "npcLevels", + name = "NPC Levels", + description = "Show the combat level of the NPC next to their name", + section = "WavesSection" + ) + default boolean npcLevels() + { + return false; + } + + @ConfigItem( + position = 3, keyName = "getWaveOverlayHeaderColor", name = "Wave Header", description = "Color for Wave Header", - titleSection = "waves" + section = "WavesSection" ) default Color getWaveOverlayHeaderColor() { @@ -164,14 +354,611 @@ public interface InfernoConfig extends Config } @ConfigItem( - position = 11, + position = 4, keyName = "getWaveTextColor", name = "Wave Text Color", description = "Color for Wave Texts", - titleSection = "waves" + section = "WavesSection" ) default Color getWaveTextColor() { return Color.WHITE; } -} \ No newline at end of file + + @ConfigItem( + position = 0, + keyName = "indicateObstacles", + name = "Obstacles", + description = "Indicate obstacles that NPC's cannot pass through", + section = "ExtraSection" + ) + default boolean indicateObstacles() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "indicateNibblers", + name = "Indicate Nibblers", + description = "Indicate's nibblers that are alive", + section = "NibblersSection" + ) + default boolean indicateNibblers() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "hideJalNibDeath", + name = "Hide On Death", + description = "Hide Nibblers on death animation", + section = "NibblersSection" + ) + default boolean hideNibblerDeath() + { + return false; + } + + @ConfigItem( + position = 2, + keyName = "indicateCentralNibbler", + name = "Indicate Central Nibbler", + description = "Indicate the most central nibbler. If multiple nibblers will freeze the same amount of other nibblers, " + + "the nibbler closest to the player's location is chosen.", + section = "NibblersSection" + ) + default boolean indicateCentralNibbler() + { + return true; + } + + @ConfigItem( + position = 0, + keyName = "prayerBat", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "BatsSection" + ) + default boolean prayerBat() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "ticksOnNpcBat", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "BatsSection" + ) + default boolean ticksOnNpcBat() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "safespotsBat", + name = "Safespots", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "BatsSection" + ) + default boolean safespotsBat() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "indicateNpcPositionBat", + name = "Indicate Main Tile", + description = "Indicate the main tile for multi-tile NPC's. This tile is used for and pathfinding.", + section = "BatsSection" + ) + default boolean indicateNpcPositionBat() + { + return false; + } + + @ConfigItem( + position = 4, + keyName = "hideJalMejRahDeath", + name = "Hide On Death", + description = "Hide Jal-MejRah on death animation", + section = "BatsSection" + ) + default boolean hideBatDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "prayerBlob", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "BlobsSection" + ) + default boolean prayerBlob() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "indicateBlobDetectionTick", + name = "Indicate Blob Dection Tick", + description = "Show a prayer indicator (default: magic) for the tick on which the blob will detect prayer", + section = "BlobsSection" + ) + default boolean indicateBlobDetectionTick() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "ticksOnNpcBlob", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "BlobsSection" + ) + default boolean ticksOnNpcBlob() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "safespotsBlob", + name = "Safespots", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "BlobsSection" + ) + default boolean safespotsBlob() + { + return true; + } + + @ConfigItem( + position = 4, + keyName = "indicateNpcPositionBlob", + name = "Indicate Main Tile", + description = "Indicate the main tile for multi-tile NPC's. This tile is used for pathfinding.", + section = "BlobsSection" + ) + default boolean indicateNpcPositionBlob() + { + return false; + } + + @ConfigItem( + position = 5, + keyName = "hideJalAkDeath", + name = "Hide Blob On Death", + description = "Hide Jal-Ak on death animation", + section = "BlobsSection" + ) + default boolean hideBlobDeath() + { + return false; + } + + + @ConfigItem( + position = 6, + keyName = "hideJalAkRekXilDeath", + name = "Hide Small Range Blob On Death", + description = "Hide Jal-AkRek-Xil on death animation", + section = "BlobsSection" + ) + default boolean hideBlobSmallRangedDeath() + { + return false; + } + + @ConfigItem( + position = 7, + keyName = "hideJalAkRekMejDeath", + name = "Hide Small Magic Blob On Death", + description = "Hide Jal-AkRek-Mej on death animation", + section = "BlobsSection" + ) + default boolean hideBlobSmallMagicDeath() + { + return false; + } + + @ConfigItem( + position = 8, + keyName = "hideJalAkRekKetDeath", + name = "Hide Small Melee Blob On Death", + description = "Hide Jal-AkRek-Ket on death animation", + section = "BlobsSection" + ) + default boolean hideBlobSmallMeleeDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "prayerMeleer", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "MeleersSection" + ) + default boolean prayerMeleer() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "ticksOnNpcMeleer", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "MeleersSection" + ) + default boolean ticksOnNpcMeleer() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "safespotsMeleer", + name = "Safespots", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "MeleersSection" + ) + default boolean safespotsMeleer() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "indicateNpcPositionMeleer", + name = "Indicate Main Tile", + description = "Indicate the main tile for multi-tile NPC's. This tile is used for pathfinding.", + section = "MeleersSection" + ) + default boolean indicateNpcPositionMeleer() + { + return false; + } + + @ConfigItem( + position = 4, + keyName = "hideJalImKotDeath", + name = "Hide On Death", + description = "Hide Jal-ImKot on death animation", + section = "MeleersSection" + ) + default boolean hideMeleerDeath() + { + return false; + } + + + @ConfigItem( + position = 0, + keyName = "prayerRanger", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "RangersSection" + ) + default boolean prayerRanger() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "ticksOnNpcRanger", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "RangersSection" + ) + default boolean ticksOnNpcRanger() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "safespotsRanger", + name = "Safespots", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "RangersSection" + ) + default boolean safespotsRanger() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "indicateNpcPositionRanger", + name = "Indicate Main Tile", + description = "Indicate the main tile for multi-tile NPC's. This tile is used for pathfinding.", + section = "RangersSection" + ) + default boolean indicateNpcPositionRanger() + { + return false; + } + + @ConfigItem( + position = 4, + keyName = "hideJalXilDeath", + name = "Hide On Death", + description = "Hide Jal-Xil on death animation", + section = "RangersSection" + ) + default boolean hideRangerDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "prayerMage", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "MagersSection" + ) + default boolean prayerMage() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "ticksOnNpcMage", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "MagersSection" + ) + default boolean ticksOnNpcMage() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "safespotsMage", + name = "Safespots", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "MagersSection" + ) + default boolean safespotsMage() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "indicateNpcPositionMage", + name = "Indicate Main Tile", + description = "Indicate the main tile for multi-tile NPC's. This tile is used for pathfinding.", + section = "MagersSection" + ) + default boolean indicateNpcPositionMage() + { + return false; + } + + @ConfigItem( + position = 4, + keyName = "hideJalZekDeath", + name = "Hide On Death", + description = "Hide Jal-Zek on death animation", + section = "MagersSection" + ) + default boolean hideMagerDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "prayerHealersJad", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "JadHealersSection" + ) + default boolean prayerHealerJad() + { + return false; + } + + @ConfigItem( + position = 1, + keyName = "ticksOnNpcHealersJad", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "JadHealersSection" + ) + default boolean ticksOnNpcHealerJad() + { + return false; + } + + @ConfigItem( + position = 2, + keyName = "safespotsHealersJad", + name = "Safespots", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "JadHealersSection" + ) + default boolean safespotsHealerJad() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "indicateActiveHealersJad", + name = "Indicate Active Healers", + description = "Indicate healers that are still healing Jad", + section = "JadHealersSection" + ) + default boolean indicateActiveHealerJad() + { + return true; + } + + @ConfigItem( + position = 4, + keyName = "hideYtHurKotDeath", + name = "Hide On Death", + description = "Hide Yt-HurKot on death animation", + section = "JadHealersSection" + ) + default boolean hideHealerJadDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "prayerJad", + name = "Prayer Helper", + description = "Indicate the correct prayer when this NPC attacks", + section = "JadSection" + ) + default boolean prayerJad() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "ticksOnNpcJad", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "JadSection" + ) + default boolean ticksOnNpcJad() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "safespotsJad", + name = "Safespots (Melee Range Only)", + description = "Enable or disable safespot calculation for this specific NPC. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "JadSection" + ) + default boolean safespotsJad() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "hideJalTokJadDeath", + name = "Hide On Death", + description = "Hide JalTok-Jad on death animation", + section = "JadSection" + ) + default boolean hideJadDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "indicateActiveHealersZuk", + name = "Indicate Active Healers (UNTESTED)", + description = "Indicate healers that are still healing Zuk", + section = "ZukHealersSection" + ) + default boolean indicateActiveHealerZuk() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "hideJalMejJakDeath", + name = "Hide On Death", + description = "Hide Jal-MejJak on death animation", + section = "ZukHealersSection" + ) + default boolean hideHealerZukDeath() + { + return false; + } + + @ConfigItem( + position = 0, + keyName = "ticksOnNpcZuk", + name = "Ticks on NPC", + description = "Draws the amount of ticks before an NPC is going to attack on the NPC", + section = "ZukSection" + ) + default boolean ticksOnNpcZuk() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "safespotsZukShieldBeforeHealers", + name = "Safespots (Before Healers)", + description = "Indicate the zuk shield safespots. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "ZukSection" + ) + default InfernoZukShieldDisplayMode safespotsZukShieldBeforeHealers() + { + return InfernoZukShieldDisplayMode.PREDICT; + } + + @ConfigItem( + position = 2, + keyName = "safespotsZukShieldAfterHealers", + name = "Safespots (After Healers)", + description = "Indicate the zuk shield safespots. 'Tile Safespots' in the 'Safespots' category needs to be turned on for this to take effect.", + section = "ZukSection" + ) + default InfernoZukShieldDisplayMode safespotsZukShieldAfterHealers() + { + return InfernoZukShieldDisplayMode.LIVE; + } + + @ConfigItem( + position = 3, + keyName = "hideTzKalZukDeath", + name = "Hide On Death", + description = "Hide TzKal-Zuk on death animation", + section = "ZukSection" + ) + default boolean hideZukDeath() + { + return false; + } + + @Getter + @AllArgsConstructor + enum FontStyle + { + BOLD("Bold", Font.BOLD), + ITALIC("Italic", Font.ITALIC), + PLAIN("Plain", Font.PLAIN); + + private String name; + private int font; + + @Override + public String toString() + { + return getName(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoJadOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoInfoBoxOverlay.java similarity index 62% rename from runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoJadOverlay.java rename to runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoInfoBoxOverlay.java index 404f128e56..41e6f2411e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoJadOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoInfoBoxOverlay.java @@ -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; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoJad.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoJad.java deleted file mode 100644 index 3da29b866f..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoJad.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2017, Devin French - * 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; - } - } -} - diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNPC.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNPC.java index 1bcd8702f9..6e6e122785 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNPC.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNPC.java @@ -1,15 +1,15 @@ /* - * Copyright (c) 2019, Jacky + * Copyright (c) 2017, Devin French * 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 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 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 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; + } } } + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNibblerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNibblerOverlay.java deleted file mode 100644 index a55b0f9f2e..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoNibblerOverlay.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019, Jacky - * 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); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoOverlay.java index 75f6587bc5..c710e00eb2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoOverlay.java @@ -1,56 +1,46 @@ -/* - * Copyright (c) 2019, Jacky - * 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 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 attackPriority = plugin.getUpcomingAttacks().get(tick); + int bestPriority = 999; + InfernoNPC.Attack bestAttack = null; + + for (Map.Entry 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; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPlugin.java index 447eaf3b78..64b88c9cc7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPlugin.java @@ -29,20 +29,18 @@ import java.awt.Color; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.ListIterator; import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import lombok.AccessLevel; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Actor; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; -import net.runelite.api.HeadIcon; import net.runelite.api.NPC; import net.runelite.api.NpcID; +import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ConfigChanged; @@ -55,6 +53,11 @@ import net.runelite.client.eventbus.EventBus; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginType; +import net.runelite.client.plugins.inferno.displaymodes.InfernoNamingDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoSafespotDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoWaveDisplayMode; +import net.runelite.client.plugins.inferno.displaymodes.InfernoZukShieldDisplayMode; import net.runelite.client.ui.overlay.OverlayManager; import org.apache.commons.lang3.ArrayUtils; @@ -83,13 +86,10 @@ public class InfernoPlugin extends Plugin private InfernoWaveOverlay waveOverlay; @Inject - private InfernoJadOverlay jadOverlay; + private InfernoInfoBoxOverlay jadOverlay; @Inject - private InfernoPrayerOverlay prayerOverlay; - - @Inject - private InfernoNibblerOverlay nibblerOverlay; + private InfernoOverlay prayerOverlay; @Inject private InfernoConfig config; @@ -98,49 +98,140 @@ public class InfernoPlugin extends Plugin private EventBus eventBus; @Getter(AccessLevel.PACKAGE) - private int currentWave = -1; - + private InfernoConfig.FontStyle fontStyle = InfernoConfig.FontStyle.BOLD; @Getter(AccessLevel.PACKAGE) - private final Map monsters = new HashMap<>(); + private int textSize = 32; - @Getter(AccessLevel.PACKAGE) - private final Map> monsterCurrentAttackMap = new HashMap<>(6); - - @Getter(AccessLevel.PACKAGE) - private final List nibblers = new ArrayList<>(); - - @Getter(AccessLevel.PACKAGE) - private final InfernoNPC[] priorityNPC = new InfernoNPC[4]; - - @Getter(AccessLevel.PACKAGE) - private final List jads = new ArrayList<>(); - - @Getter(AccessLevel.PACKAGE) - private final List activeHealers = new ArrayList<>(); - - @Getter - private long lastTick; + private WorldPoint lastLocation = new WorldPoint(0, 0, 0); @Getter(AccessLevel.PACKAGE) private int currentWaveNumber; - private final List waveMonsters = new ArrayList<>(); + @Getter(AccessLevel.PACKAGE) + private final List infernoNpcs = new ArrayList<>(); @Getter(AccessLevel.PACKAGE) - private boolean displayNibblerOverlay; + private final Map> upcomingAttacks = new HashMap<>(); @Getter(AccessLevel.PACKAGE) - private boolean showPrayerHelp; + private InfernoNPC.Attack closestAttack = null; + @Getter(AccessLevel.PACKAGE) - private InfernoPrayerOverlayMode prayerOverlayMode; - private InfernoWaveDisplayMode waveDisplay; - private Color getWaveOverlayHeaderColor; - private Color getWaveTextColor; + private final List obstacles = new ArrayList<>(); + + @Getter(AccessLevel.PACKAGE) + private boolean finalPhase = false; + private NPC zukShield = null; + private WorldPoint zukShieldLastPosition = null; + private WorldPoint zukShieldBase = null; + private int zukShieldCornerTicks = -2; + + @Getter(AccessLevel.PACKAGE) + private InfernoNPC centralNibbler = null; + + // 0 = total safespot + // 1 = pray melee + // 2 = pray range + // 3 = pray magic + // 4 = pray melee, range + // 5 = pray melee, magic + // 6 = pray range, magic + // 7 = pray all + @Getter(AccessLevel.PACKAGE) + private final Map safeSpotMap = new HashMap<>(); + @Getter(AccessLevel.PACKAGE) + private final Map> safeSpotAreas = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private long lastTick; + + @Getter(AccessLevel.PACKAGE) + private InfernoPrayerDisplayMode prayerDisplayMode; @Getter(AccessLevel.PACKAGE) private boolean descendingBoxes; @Getter(AccessLevel.PACKAGE) - private boolean indicateWhenPrayingCorrectly; + private boolean indicateNonPriorityDescendingBoxes; @Getter(AccessLevel.PACKAGE) - private boolean indicateActiveHealers; + private boolean indicateBlobDetectionTick; + @Getter(AccessLevel.PACKAGE) + private boolean indicateWhenPrayingCorrectly; + + private InfernoWaveDisplayMode waveDisplay; + @Getter(AccessLevel.PACKAGE) + private InfernoNamingDisplayMode npcNaming; + @Getter(AccessLevel.PACKAGE) + private boolean npcLevels; + private Color getWaveOverlayHeaderColor; + private Color getWaveTextColor; + + @Getter(AccessLevel.PACKAGE) + private InfernoSafespotDisplayMode safespotDisplayMode; + @Getter(AccessLevel.PACKAGE) + private int safespotsCheckSize; + @Getter(AccessLevel.PACKAGE) + private boolean indicateNonSafespotted; + @Getter(AccessLevel.PACKAGE) + private boolean indicateTemporarySafespotted; + @Getter(AccessLevel.PACKAGE) + private boolean indicateSafespotted; + @Getter(AccessLevel.PACKAGE) + private boolean indicateObstacles; + + @Getter(AccessLevel.PACKAGE) + private boolean indicateNibblers; + @Getter(AccessLevel.PACKAGE) + private boolean indicateCentralNibbler; + + @Getter(AccessLevel.PACKAGE) + private boolean indicateActiveHealersJad; + @Getter(AccessLevel.PACKAGE) + private boolean indicateActiveHealersZuk; + + private boolean indicateNpcPositionBat; + private boolean indicateNpcPositionBlob; + private boolean indicateNpcPositionMeleer; + private boolean indicateNpcPositionRanger; + private boolean indicateNpcPositionMage; + + private boolean ticksOnNpcBat; + private boolean ticksOnNpcBlob; + private boolean ticksOnNpcMeleer; + private boolean ticksOnNpcRanger; + private boolean ticksOnNpcMage; + private boolean ticksOnNpcHealerJad; + private boolean ticksOnNpcJad; + private boolean ticksOnNpcZuk; + + private boolean safespotsBat; + private boolean safespotsBlob; + private boolean safespotsMeleer; + private boolean safespotsRanger; + private boolean safespotsMage; + private boolean safespotsHealerJad; + private boolean safespotsJad; + private InfernoZukShieldDisplayMode safespotsZukShieldBeforeHealers; + private InfernoZukShieldDisplayMode safespotsZukShieldAfterHealers; + + private boolean prayerBat; + private boolean prayerBlob; + private boolean prayerMeleer; + private boolean prayerRanger; + private boolean prayerMage; + private boolean prayerHealerJad; + private boolean prayerJad; + + private boolean hideNibblerDeath; + private boolean hideBatDeath; + private boolean hideBlobDeath; + private boolean hideBlobSmallRangedDeath; + private boolean hideBlobSmallMagicDeath; + private boolean hideBlobSmallMeleeDeath; + private boolean hideMeleerDeath; + private boolean hideRangerDeath; + private boolean hideMagerDeath; + private boolean hideJadDeath; + private boolean hideHealerJadDeath; + private boolean hideHealerZukDeath; + private boolean hideZukDeath; @Provides InfernoConfig provideConfig(ConfigManager configManager) @@ -155,11 +246,12 @@ public class InfernoPlugin extends Plugin addSubscriptions(); waveOverlay.setDisplayMode(this.waveDisplay); + waveOverlay.setWaveHeaderColor(this.getWaveOverlayHeaderColor); + waveOverlay.setWaveTextColor(this.getWaveTextColor); if (isInInferno()) { overlayManager.add(infernoOverlay); - overlayManager.add(nibblerOverlay); if (this.waveDisplay != InfernoWaveDisplayMode.NONE) { @@ -168,14 +260,7 @@ public class InfernoPlugin extends Plugin overlayManager.add(jadOverlay); overlayManager.add(prayerOverlay); - } - - waveOverlay.setWaveHeaderColor(this.getWaveOverlayHeaderColor); - waveOverlay.setWaveTextColor(this.getWaveTextColor); - - for (int i = 1; i <= 6; i++) - { - monsterCurrentAttackMap.put(i, new ArrayList<>()); + hideNpcDeaths(); } } @@ -185,11 +270,13 @@ public class InfernoPlugin extends Plugin eventBus.unregister(this); overlayManager.remove(infernoOverlay); - overlayManager.remove(nibblerOverlay); overlayManager.remove(waveOverlay); overlayManager.remove(jadOverlay); overlayManager.remove(prayerOverlay); + currentWaveNumber = -1; + + showNpcDeaths(); } private void addSubscriptions() @@ -211,6 +298,8 @@ public class InfernoPlugin extends Plugin } updateConfig(); + hideNpcDeaths(); + showNpcDeaths(); if (event.getKey().endsWith("color")) { @@ -230,75 +319,111 @@ public class InfernoPlugin extends Plugin } } - private void onNpcSpawned(NpcSpawned event) + private void onGameTick(GameTick GameTickEvent) { - if (client.getMapRegions()[0] != 9043) + if (!isInInferno()) { return; } - NPC npc = event.getNpc(); - if (isValidInfernoMob(npc)) + lastTick = System.currentTimeMillis(); + + upcomingAttacks.clear(); + calculateUpcomingAttacks(); + + closestAttack = null; + calculateClosestAttack(); + + safeSpotMap.clear(); + calculateSafespots(); + + safeSpotAreas.clear(); + calculateSafespotAreas(); + + obstacles.clear(); + calculateObstacles(); + + centralNibbler = null; + calculateCentralNibbler(); + } + + private void onNpcSpawned(NpcSpawned event) + { + if (!isInInferno()) { - monsters.put(npc, new InfernoNPC(npc)); - log.debug(String.valueOf(monsters.size())); + return; } - if (npc.getId() == NpcID.JALNIB) + if (event.getNpc().getId() == NpcID.ANCESTRAL_GLYPH) { - nibblers.add(npc); + zukShield = event.getNpc(); } - final int id = event.getNpc().getId(); + final InfernoNPC.Type infernoNPCType = InfernoNPC.Type.typeFromId(event.getNpc().getId()); - if (id == NpcID.JALTOKJAD || id == NpcID.JALTOKJAD_7704) + if (infernoNPCType == null) { - jads.add(new InfernoJad(npc)); + return; } - final Actor actor = event.getActor(); - - if (actor != null) + if (infernoNPCType == InfernoNPC.Type.ZUK) { - waveMonsters.add(actor); + log.debug("[INFERNO] Zuk spawn detected, not in final phase"); + finalPhase = false; + zukShieldCornerTicks = -2; + zukShieldLastPosition = null; + zukShieldBase = null; + } + if (infernoNPCType == InfernoNPC.Type.HEALER_ZUK) + { + log.debug("[INFERNO] Final phase detected!"); + finalPhase = true; + } + + // Blobs need to be added to the end of the list because the prayer for their detection tick will be based + // on the upcoming attacks of other NPC's + if (infernoNPCType == InfernoNPC.Type.BLOB) + { + infernoNpcs.add(new InfernoNPC(event.getNpc())); + } + else + { + infernoNpcs.add(0, new InfernoNPC(event.getNpc())); } } private void onNpcDespawned(NpcDespawned event) { - if (client.getMapRegions()[0] != 9043) + if (!isInInferno()) { return; } - NPC npc = event.getNpc(); - if (monsters.containsKey(npc)) + if (event.getNpc().getId() == NpcID.ANCESTRAL_GLYPH) { - monsters.remove(npc); - log.debug(String.valueOf(monsters.size())); + zukShield = null; } - if (npc.getId() == NpcID.JALNIB) + infernoNpcs.removeIf(infernoNPC -> infernoNPC.getNpc() == event.getNpc()); + } + + private void onAnimationChanged(AnimationChanged event) + { + if (!isInInferno()) { - nibblers.remove(npc); + return; } - final ListIterator iter = jads.listIterator(); - while (iter.hasNext()) + if (event.getActor() instanceof NPC) { - final InfernoJad possibleJad = iter.next(); + final NPC npc = (NPC) event.getActor(); - if (possibleJad.getNpc() == npc) + if (ArrayUtils.contains(InfernoNPC.Type.NIBBLER.getNpcIds(), npc.getId()) + && npc.getAnimation() == 7576) { - iter.remove(); + infernoNpcs.removeIf(infernoNPC -> infernoNPC.getNpc() == npc); } } - - final Actor actor = event.getActor(); - if (actor != null) - { - waveMonsters.remove(actor); - } } private void onGameStateChanged(GameStateChanged event) @@ -310,26 +435,29 @@ public class InfernoPlugin extends Plugin if (!isInInferno()) { + infernoNpcs.clear(); + currentWaveNumber = -1; + overlayManager.remove(infernoOverlay); - overlayManager.remove(nibblerOverlay); overlayManager.remove(waveOverlay); overlayManager.remove(jadOverlay); overlayManager.remove(prayerOverlay); } else if (currentWaveNumber == -1) { + infernoNpcs.clear(); + currentWaveNumber = 1; + overlayManager.add(infernoOverlay); - overlayManager.add(nibblerOverlay); + overlayManager.add(jadOverlay); + overlayManager.add(prayerOverlay); if (this.waveDisplay != InfernoWaveDisplayMode.NONE) { overlayManager.add(waveOverlay); } - - overlayManager.add(jadOverlay); - overlayManager.add(prayerOverlay); } } @@ -345,211 +473,639 @@ public class InfernoPlugin extends Plugin if (event.getMessage().contains("Wave:")) { message = message.substring(message.indexOf(": ") + 2); - currentWaveNumber = Integer.parseInt(message.substring(0, message.indexOf("<"))); + currentWaveNumber = Integer.parseInt(message.substring(0, message.indexOf('<'))); } } - private void onGameTick(GameTick event) - { - if (client.getMapRegions()[0] != 9043) - { - return; - } - - clearMapAndPriority(); - - lastTick = System.currentTimeMillis(); - - for (InfernoJad jad : jads) - { - jad.gameTick(); - } - - activeHealers.clear(); - for (NPC npc : client.getNpcs()) - { - if (npc.getId() != NpcID.YTHURKOT_7701 || npc.getInteracting() == client.getLocalPlayer()) - { - continue; - } - - activeHealers.add(npc); - } - - for (InfernoNPC monster : monsters.values()) - { - calculateDistanceToPlayer(monster); - - NPC npc = monster.getNpc(); - - // if they are not attacking but are still attacking - if (monster.isAttacking()) - { - monster.setTicksTillAttack(monster.getTicksTillAttack() - 1); - - // sets the blobs attack style - if (monster.getName().equals("blob") && monster.getTicksTillAttack() == 3 && monster.getDistanceToPlayer() <= 15) - { - if (client.getLocalPlayer().getOverheadIcon() == null) - { - monster.setAttackstyle(InfernoNPC.Attackstyle.RANDOM); - } - else if (client.getLocalPlayer().getOverheadIcon().equals(HeadIcon.MAGIC)) - { - monster.setAttackstyle(InfernoNPC.Attackstyle.RANGE); - } - else if (client.getLocalPlayer().getOverheadIcon().equals(HeadIcon.RANGED)) - { - monster.setAttackstyle(InfernoNPC.Attackstyle.MAGE); - } - } - - // we know the monster is not attacking because it should have attacked and is idling - if (monster.getTicksTillAttack() == 0) - { - if (npc.getAnimation() == -1) - { - monster.setAttacking(false); - } - else - { - // want to reset the monsters attack back to attacking - monster.attacked(); - } - } - } - else - { - // they've just attacked - if (npc.getAnimation() == monster.getAttackAnimation() || npc.getAnimation() == 7581) // special case for blob - { - monster.attacked(); - } - } - - if (monster.getTicksTillAttack() >= 1) - { - monsterCurrentAttackMap.get(monster.getTicksTillAttack()).add(monster); - } - } - - calculatePriorityNPC(); - } - - private void onAnimationChanged(final AnimationChanged event) - { - InfernoJad jad = null; - - for (InfernoJad possibleJad : jads) - { - if (possibleJad.getNpc() == event.getActor()) - { - jad = possibleJad; - } - } - - if (jad != null) - { - if (event.getActor().getAnimation() == InfernoJad.Attack.MAGIC.getAnimation()) - { - jad.updateNextAttack(InfernoJad.Attack.MAGIC); - } - else if (event.getActor().getAnimation() == InfernoJad.Attack.RANGE.getAnimation()) - { - jad.updateNextAttack(InfernoJad.Attack.RANGE); - } - } - } - - private void calculatePriorityNPC() - { - for (int i = 0; i < priorityNPC.length; i++) - { - ArrayList monsters = monsterCurrentAttackMap.get(i + 1); - - if (monsters.size() == 0) - { - continue; - } - - int priority = monsters.get(0).getPriority(); - - InfernoNPC infernoNPC = monsters.get(0); - - for (InfernoNPC npc : monsters) - { - if (npc.getPriority() < priority) - { - priority = npc.getPriority(); - infernoNPC = npc; - } - } - - priorityNPC[i] = infernoNPC; - - log.debug("i: " + i + " " + infernoNPC.getName()); - } - } - - // TODO: blob calculator - private void calculateDistanceToPlayer(InfernoNPC monster) - { - monster.setDistanceToPlayer(client.getLocalPlayer().getWorldLocation().distanceTo(monster.getNpc().getWorldArea())); - } - - private void clearMapAndPriority() - { - for (List l : monsterCurrentAttackMap.values()) - { - l.clear(); - } - - for (int i = 0; i < priorityNPC.length; i++) - { - priorityNPC[i] = null; - } - } - - private boolean isValidInfernoMob(NPC npc) - { - // we only want the bat, blob, melee, ranger and mager - return npc.getId() == NpcID.JALMEJRAH || - npc.getId() == NpcID.JALAK || - npc.getId() == NpcID.JALIMKOT || - npc.getId() == NpcID.JALXIL || - npc.getId() == NpcID.JALZEK; - } - private boolean isInInferno() { return ArrayUtils.contains(client.getMapRegions(), INFERNO_REGION); } - boolean isNotFinalWave() - { - return currentWaveNumber <= 68; - } - - List getWaveMonsters() - { - return waveMonsters; - } - int getNextWaveNumber() { return currentWaveNumber == -1 || currentWaveNumber == 69 ? -1 : currentWaveNumber + 1; } + private void calculateUpcomingAttacks() + { + for (InfernoNPC infernoNPC : infernoNpcs) + { + infernoNPC.gameTick(client, lastLocation, finalPhase); + + if (infernoNPC.getType() == InfernoNPC.Type.ZUK && zukShieldCornerTicks == -1) + { + infernoNPC.updateNextAttack(InfernoNPC.Attack.UNKNOWN, 12); // TODO: Could be 10 or 11. Test! + zukShieldCornerTicks = 0; + } + + // Map all upcoming attacks and their priority + determine which NPC is about to attack next + if (infernoNPC.getTicksTillNextAttack() > 0 && isPrayerHelper(infernoNPC) + && (infernoNPC.getNextAttack() != InfernoNPC.Attack.UNKNOWN + || (indicateBlobDetectionTick && infernoNPC.getType() == InfernoNPC.Type.BLOB + && infernoNPC.getTicksTillNextAttack() >= 4))) + { + upcomingAttacks.computeIfAbsent(infernoNPC.getTicksTillNextAttack(), k -> new HashMap<>()); + + if (indicateBlobDetectionTick && infernoNPC.getType() == InfernoNPC.Type.BLOB + && infernoNPC.getTicksTillNextAttack() >= 4) + { + upcomingAttacks.computeIfAbsent(infernoNPC.getTicksTillNextAttack() - 3, k -> new HashMap<>()); + upcomingAttacks.computeIfAbsent(infernoNPC.getTicksTillNextAttack() - 4, k -> new HashMap<>()); + + // If there's already a magic attack on the detection tick, group them + if (upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).containsKey(InfernoNPC.Attack.MAGIC)) + { + if (upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).get(InfernoNPC.Attack.MAGIC) > InfernoNPC.Type.BLOB.getPriority()) + { + upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).put(InfernoNPC.Attack.MAGIC, InfernoNPC.Type.BLOB.getPriority()); + } + } + // If there's already a ranged attack on the detection tick, group them + else if (upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).containsKey(InfernoNPC.Attack.RANGED)) + { + if (upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).get(InfernoNPC.Attack.RANGED) > InfernoNPC.Type.BLOB.getPriority()) + { + upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).put(InfernoNPC.Attack.RANGED, InfernoNPC.Type.BLOB.getPriority()); + } + } + // If there's going to be a magic attack on the blob attack tick, pray range on the detect tick so magic is prayed on the attack tick + else if (upcomingAttacks.get(infernoNPC.getTicksTillNextAttack()).containsKey(InfernoNPC.Attack.MAGIC) + || upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 4).containsKey(InfernoNPC.Attack.MAGIC)) + { + if (!upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).containsKey(InfernoNPC.Attack.RANGED) + || upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).get(InfernoNPC.Attack.RANGED) > InfernoNPC.Type.BLOB.getPriority()) + { + upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).put(InfernoNPC.Attack.RANGED, InfernoNPC.Type.BLOB.getPriority()); + } + } + // If there's going to be a ranged attack on the blob attack tick, pray magic on the detect tick so range is prayed on the attack tick + else if (upcomingAttacks.get(infernoNPC.getTicksTillNextAttack()).containsKey(InfernoNPC.Attack.RANGED) + || upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 4).containsKey(InfernoNPC.Attack.RANGED)) + { + if (!upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).containsKey(InfernoNPC.Attack.MAGIC) + || upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).get(InfernoNPC.Attack.MAGIC) > InfernoNPC.Type.BLOB.getPriority()) + { + upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).put(InfernoNPC.Attack.MAGIC, InfernoNPC.Type.BLOB.getPriority()); + } + } + // If there's no magic or ranged attack on the detection tick, create a magic pray blob + else + { + upcomingAttacks.get(infernoNPC.getTicksTillNextAttack() - 3).put(InfernoNPC.Attack.MAGIC, InfernoNPC.Type.BLOB.getPriority()); + } + } + else + { + final InfernoNPC.Attack attack = infernoNPC.getNextAttack(); + final int priority = infernoNPC.getType().getPriority(); + + if (!upcomingAttacks.get(infernoNPC.getTicksTillNextAttack()).containsKey(attack) + || upcomingAttacks.get(infernoNPC.getTicksTillNextAttack()).get(attack) > priority) + { + upcomingAttacks.get(infernoNPC.getTicksTillNextAttack()).put(attack, priority); + } + } + } + } + } + + private void calculateClosestAttack() + { + if (prayerDisplayMode == InfernoPrayerDisplayMode.PRAYER_TAB + || prayerDisplayMode == InfernoPrayerDisplayMode.BOTH) + { + int closestTick = 999; + int closestPriority = 999; + + for (Integer tick : upcomingAttacks.keySet()) + { + final Map attackPriority = upcomingAttacks.get(tick); + + for (InfernoNPC.Attack currentAttack : attackPriority.keySet()) + { + final int currentPriority = attackPriority.get(currentAttack); + if (tick < closestTick || (tick == closestTick && currentPriority < closestPriority)) + { + closestAttack = currentAttack; + closestPriority = currentPriority; + closestTick = tick; + } + } + } + } + } + + private void calculateSafespots() + { + if (currentWaveNumber < 69) + { + if (safespotDisplayMode != InfernoSafespotDisplayMode.OFF) + { + int checkSize = (int) Math.floor(safespotsCheckSize / 2.0); + + for (int x = -checkSize; x <= checkSize; x++) + { + for (int y = -checkSize; y <= checkSize; y++) + { + final WorldPoint checkLoc = client.getLocalPlayer().getWorldLocation().dx(x).dy(y); + + if (obstacles.contains(checkLoc)) + { + continue; + } + + for (InfernoNPC infernoNPC : infernoNpcs) + { + if (!isNormalSafespots(infernoNPC)) + { + continue; + } + + if (!safeSpotMap.containsKey(checkLoc)) + { + safeSpotMap.put(checkLoc, 0); + } + + if (infernoNPC.canAttack(client, checkLoc) + || infernoNPC.canMoveToAttack(client, checkLoc, obstacles)) + { + if (infernoNPC.getType().getDefaultAttack() == InfernoNPC.Attack.MELEE) + { + if (safeSpotMap.get(checkLoc) == 0) + { + safeSpotMap.put(checkLoc, 1); + } + else if (safeSpotMap.get(checkLoc) == 2) + { + safeSpotMap.put(checkLoc, 4); + } + else if (safeSpotMap.get(checkLoc) == 3) + { + safeSpotMap.put(checkLoc, 5); + } + else if (safeSpotMap.get(checkLoc) == 6) + { + safeSpotMap.put(checkLoc, 7); + } + } + + if (infernoNPC.getType().getDefaultAttack() == InfernoNPC.Attack.MAGIC + || (infernoNPC.getType() == InfernoNPC.Type.BLOB + && safeSpotMap.get(checkLoc) != 2 && safeSpotMap.get(checkLoc) != 4)) + { + if (safeSpotMap.get(checkLoc) == 0) + { + safeSpotMap.put(checkLoc, 3); + } + else if (safeSpotMap.get(checkLoc) == 1) + { + safeSpotMap.put(checkLoc, 5); + } + else if (safeSpotMap.get(checkLoc) == 2) + { + safeSpotMap.put(checkLoc, 6); + } + else if (safeSpotMap.get(checkLoc) == 5) + { + safeSpotMap.put(checkLoc, 7); + } + } + + if (infernoNPC.getType().getDefaultAttack() == InfernoNPC.Attack.RANGED + || (infernoNPC.getType() == InfernoNPC.Type.BLOB + && safeSpotMap.get(checkLoc) != 3 && safeSpotMap.get(checkLoc) != 5)) + { + if (safeSpotMap.get(checkLoc) == 0) + { + safeSpotMap.put(checkLoc, 2); + } + else if (safeSpotMap.get(checkLoc) == 1) + { + safeSpotMap.put(checkLoc, 4); + } + else if (safeSpotMap.get(checkLoc) == 3) + { + safeSpotMap.put(checkLoc, 6); + } + else if (safeSpotMap.get(checkLoc) == 4) + { + safeSpotMap.put(checkLoc, 7); + } + } + + if (infernoNPC.getType() == InfernoNPC.Type.JAD + && infernoNPC.getNpc().getWorldArea().isInMeleeDistance(checkLoc)) + { + if (safeSpotMap.get(checkLoc) == 0) + { + safeSpotMap.put(checkLoc, 1); + } + else if (safeSpotMap.get(checkLoc) == 2) + { + safeSpotMap.put(checkLoc, 4); + } + else if (safeSpotMap.get(checkLoc) == 3) + { + safeSpotMap.put(checkLoc, 5); + } + else if (safeSpotMap.get(checkLoc) == 6) + { + safeSpotMap.put(checkLoc, 7); + } + } + } + } + } + } + } + } + else if (currentWaveNumber == 69 && zukShield != null) + { + final WorldPoint zukShieldCurrentPosition = zukShield.getWorldLocation(); + + if (zukShieldLastPosition != null && zukShieldLastPosition.getX() != zukShieldCurrentPosition.getX() + && zukShieldCornerTicks == -2) + { + zukShieldBase = zukShieldLastPosition; + zukShieldCornerTicks = -1; + } + + zukShieldLastPosition = zukShield.getWorldLocation(); + + if (safespotDisplayMode != InfernoSafespotDisplayMode.OFF) + { + if ((finalPhase && safespotsZukShieldAfterHealers == InfernoZukShieldDisplayMode.LIVE) + || (!finalPhase && safespotsZukShieldBeforeHealers == InfernoZukShieldDisplayMode.LIVE)) + { + for (int x = zukShield.getWorldLocation().getX() - 1; x <= zukShield.getWorldLocation().getX() + 3; x++) + { + for (int y = zukShield.getWorldLocation().getY() - 4; y <= zukShield.getWorldLocation().getY() - 2; y++) + { + safeSpotMap.put(new WorldPoint(x, y, client.getPlane()), 0); + } + } + } + else if ((finalPhase && safespotsZukShieldAfterHealers == InfernoZukShieldDisplayMode.PREDICT) + || (!finalPhase && safespotsZukShieldBeforeHealers == InfernoZukShieldDisplayMode.PREDICT)) + { + if (zukShieldCornerTicks >= 0) + { + // TODO: Predict zuk shield safespots + // Calculate distance from zukShieldCurrentPosition to zukShieldBase. + // - If shield is not in corner: calculate next position in current direction (use + // difference between current and last position to get direction) + // - If shield is in corner: increment zukShieldCornerTicks and predict next shield + // position based on how many ticks the shield has been in the corner. + } + } + } + } + } + + private void calculateSafespotAreas() + { + if (safespotDisplayMode == InfernoSafespotDisplayMode.AREA) + { + for (WorldPoint worldPoint : safeSpotMap.keySet()) + { + if (!safeSpotAreas.containsKey(safeSpotMap.get(worldPoint))) + { + safeSpotAreas.put(safeSpotMap.get(worldPoint), new ArrayList<>()); + } + + safeSpotAreas.get(safeSpotMap.get(worldPoint)).add(worldPoint); + } + } + + lastLocation = client.getLocalPlayer().getWorldLocation(); + } + + private void calculateObstacles() + { + for (NPC npc : client.getNpcs()) + { + obstacles.addAll(npc.getWorldArea().toWorldPointList()); + } + } + + private void calculateCentralNibbler() + { + InfernoNPC bestNibbler = null; + int bestAmountInArea = 0; + int bestDistanceToPlayer = 999; + + for (InfernoNPC infernoNPC : infernoNpcs) + { + if (infernoNPC.getType() != InfernoNPC.Type.NIBBLER) + { + continue; + } + + int amountInArea = 0; + final int distanceToPlayer = infernoNPC.getNpc().getWorldLocation().distanceTo(client.getLocalPlayer().getWorldLocation()); + + for (InfernoNPC checkNpc : infernoNpcs) + { + if (checkNpc.getType() != InfernoNPC.Type.NIBBLER + || checkNpc.getNpc().getWorldArea().distanceTo(infernoNPC.getNpc().getWorldArea()) > 1) + { + continue; + } + + amountInArea++; + } + + if (amountInArea > bestAmountInArea + || (amountInArea == bestAmountInArea && distanceToPlayer < bestDistanceToPlayer)) + { + bestNibbler = infernoNPC; + } + } + + if (bestNibbler != null) + { + centralNibbler = bestNibbler; + } + } + + private boolean isPrayerHelper(InfernoNPC infernoNPC) + { + switch (infernoNPC.getType()) + { + case BAT: + return prayerBat; + case BLOB: + return prayerBlob; + case MELEE: + return prayerMeleer; + case RANGER: + return prayerRanger; + case MAGE: + return prayerMage; + case HEALER_JAD: + return prayerHealerJad; + case JAD: + return prayerJad; + default: + return false; + } + } + + boolean isTicksOnNpc(InfernoNPC infernoNPC) + { + switch (infernoNPC.getType()) + { + case BAT: + return ticksOnNpcBat; + case BLOB: + return ticksOnNpcBlob; + case MELEE: + return ticksOnNpcMeleer; + case RANGER: + return ticksOnNpcRanger; + case MAGE: + return ticksOnNpcMage; + case HEALER_JAD: + return ticksOnNpcHealerJad; + case JAD: + return ticksOnNpcJad; + case ZUK: + return ticksOnNpcZuk; + default: + return false; + } + } + + boolean isNormalSafespots(InfernoNPC infernoNPC) + { + switch (infernoNPC.getType()) + { + case BAT: + return safespotsBat; + case BLOB: + return safespotsBlob; + case MELEE: + return safespotsMeleer; + case RANGER: + return safespotsRanger; + case MAGE: + return safespotsMage; + case HEALER_JAD: + return safespotsHealerJad; + case JAD: + return safespotsJad; + default: + return false; + } + } + + boolean isIndicateNpcPosition(InfernoNPC infernoNPC) + { + switch (infernoNPC.getType()) + { + case BAT: + return indicateNpcPositionBat; + case BLOB: + return indicateNpcPositionBlob; + case MELEE: + return indicateNpcPositionMeleer; + case RANGER: + return indicateNpcPositionRanger; + case MAGE: + return indicateNpcPositionMage; + default: + return false; + } + } + + private void hideNpcDeaths() + { + + if (this.hideNibblerDeath) + { + client.addHiddenNpcDeath("Jal-Nib"); + } + if (this.hideBatDeath) + { + client.addHiddenNpcDeath("Jal-MejRah"); + } + if (this.hideBlobDeath) + { + client.addHiddenNpcDeath("Jal-Ak"); + } + if (this.hideBlobSmallMeleeDeath) + { + client.addHiddenNpcDeath("Jal-AkRek-Ket"); + } + if (this.hideBlobSmallMagicDeath) + { + client.addHiddenNpcDeath("Jal-AkRek-Mej"); + } + if (this.hideBlobSmallRangedDeath) + { + client.addHiddenNpcDeath("Jal-AkRek-Xil"); + } + if (this.hideMeleerDeath) + { + client.addHiddenNpcDeath("Jal-ImKot"); + } + if (this.hideRangerDeath) + { + client.addHiddenNpcDeath("Jal-Xil"); + } + if (this.hideMagerDeath) + { + client.addHiddenNpcDeath("Jal-Zek"); + } + if (this.hideHealerJadDeath && isInInferno()) + { + client.addHiddenNpcDeath("Yt-HurKot"); + } + if (this.hideJadDeath) + { + client.addHiddenNpcDeath("JalTok-Jad"); + } + if (this.hideHealerZukDeath) + { + client.addHiddenNpcDeath("Jal-MejJak"); + } + if (this.hideZukDeath) + { + client.addHiddenNpcDeath("TzKal-Zuk"); + } + } + + private void showNpcDeaths() + { + + if (!this.hideNibblerDeath) + { + client.removeHiddenNpcDeath("Jal-Nib"); + } + if (!this.hideBatDeath) + { + client.removeHiddenNpcDeath("Jal-MejRah"); + } + if (!this.hideBlobDeath) + { + client.removeHiddenNpcDeath("Jal-Ak"); + } + if (!this.hideBlobSmallMeleeDeath) + { + client.removeHiddenNpcDeath("Jal-AkRek-Ket"); + } + if (!this.hideBlobSmallMagicDeath) + { + client.removeHiddenNpcDeath("Jal-AkRek-Mej"); + } + if (!this.hideBlobSmallRangedDeath) + { + client.removeHiddenNpcDeath("Jal-AkRek-Xil"); + } + if (!this.hideMeleerDeath) + { + client.removeHiddenNpcDeath("Jal-ImKot"); + } + if (!this.hideRangerDeath) + { + client.removeHiddenNpcDeath("Jal-Xil"); + } + if (!this.hideMagerDeath) + { + client.removeHiddenNpcDeath("Jal-Zek"); + } + if (!this.hideHealerJadDeath) + { + client.removeHiddenNpcDeath("Yt-HurKot"); + } + if (!this.hideJadDeath) + { + client.removeHiddenNpcDeath("JalTok-Jad"); + } + if (!this.hideHealerZukDeath) + { + client.removeHiddenNpcDeath("Jal-MejJak"); + } + if (!this.hideZukDeath) + { + client.removeHiddenNpcDeath("TzKal-Zuk"); + } + } + private void updateConfig() { - this.displayNibblerOverlay = config.displayNibblerOverlay(); - this.showPrayerHelp = config.showPrayerHelp(); - this.prayerOverlayMode = config.prayerOverlayMode(); - this.waveDisplay = config.waveDisplay(); - this.getWaveOverlayHeaderColor = config.getWaveOverlayHeaderColor(); - this.getWaveTextColor = config.getWaveTextColor(); + this.prayerDisplayMode = config.prayerDisplayMode(); this.descendingBoxes = config.descendingBoxes(); this.indicateWhenPrayingCorrectly = config.indicateWhenPrayingCorrectly(); - this.indicateActiveHealers = config.indicateActiveHealers(); + this.indicateNonPriorityDescendingBoxes = config.indicateNonPriorityDescendingBoxes(); + this.indicateBlobDetectionTick = config.indicateBlobDetectionTick(); + + this.waveDisplay = config.waveDisplay(); + this.npcNaming = config.npcNaming(); + this.npcLevels = config.npcLevels(); + this.getWaveOverlayHeaderColor = config.getWaveOverlayHeaderColor(); + this.getWaveTextColor = config.getWaveTextColor(); + + this.safespotDisplayMode = config.safespotDisplayMode(); + this.safespotsCheckSize = config.safespotsCheckSize(); + this.indicateNonSafespotted = config.indicateNonSafespotted(); + this.indicateTemporarySafespotted = config.indicateTemporarySafespotted(); + this.indicateSafespotted = config.indicateSafespotted(); + this.indicateObstacles = config.indicateObstacles(); + this.safespotsZukShieldBeforeHealers = config.safespotsZukShieldBeforeHealers(); + + this.indicateNibblers = config.indicateNibblers(); + this.indicateCentralNibbler = config.indicateCentralNibbler(); + + this.indicateActiveHealersJad = config.indicateActiveHealerJad(); + this.indicateActiveHealersZuk = config.indicateActiveHealerZuk(); + + this.indicateNpcPositionBat = config.indicateNpcPositionBat(); + this.indicateNpcPositionBlob = config.indicateNpcPositionBlob(); + this.indicateNpcPositionMeleer = config.indicateNpcPositionMeleer(); + this.indicateNpcPositionRanger = config.indicateNpcPositionRanger(); + this.indicateNpcPositionMage = config.indicateNpcPositionMage(); + + this.ticksOnNpcBat = config.ticksOnNpcBat(); + this.ticksOnNpcBlob = config.ticksOnNpcBlob(); + this.ticksOnNpcMeleer = config.ticksOnNpcMeleer(); + this.ticksOnNpcRanger = config.ticksOnNpcRanger(); + this.ticksOnNpcMage = config.ticksOnNpcMage(); + this.ticksOnNpcHealerJad = config.ticksOnNpcHealerJad(); + this.ticksOnNpcJad = config.ticksOnNpcJad(); + this.ticksOnNpcZuk = config.ticksOnNpcZuk(); + + this.safespotsBat = config.safespotsBat(); + this.safespotsBlob = config.safespotsBlob(); + this.safespotsMeleer = config.safespotsMeleer(); + this.safespotsRanger = config.safespotsRanger(); + this.safespotsMage = config.safespotsMage(); + this.safespotsHealerJad = config.safespotsHealerJad(); + this.safespotsJad = config.safespotsJad(); + this.safespotsZukShieldBeforeHealers = config.safespotsZukShieldBeforeHealers(); + this.safespotsZukShieldAfterHealers = config.safespotsZukShieldAfterHealers(); + + this.prayerBat = config.prayerBat(); + this.prayerBlob = config.prayerBlob(); + this.prayerMeleer = config.prayerMeleer(); + this.prayerRanger = config.prayerRanger(); + this.prayerMage = config.prayerMage(); + this.prayerHealerJad = config.prayerHealerJad(); + this.prayerJad = config.prayerJad(); + + this.hideNibblerDeath = config.hideNibblerDeath(); + this.hideBatDeath = config.hideBatDeath(); + this.hideBlobDeath = config.hideBlobDeath(); + this.hideBlobSmallRangedDeath = config.hideBlobSmallRangedDeath(); + this.hideBlobSmallMagicDeath = config.hideBlobSmallMagicDeath(); + this.hideBlobSmallMeleeDeath = config.hideBlobSmallMeleeDeath(); + this.hideMeleerDeath = config.hideMeleerDeath(); + this.hideRangerDeath = config.hideRangerDeath(); + this.hideMagerDeath = config.hideMagerDeath(); + this.hideHealerJadDeath = config.hideHealerJadDeath(); + this.hideJadDeath = config.hideJadDeath(); + this.hideHealerZukDeath = config.hideHealerZukDeath(); + this.hideZukDeath = config.hideZukDeath(); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPrayerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPrayerOverlay.java deleted file mode 100644 index be91b61495..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPrayerOverlay.java +++ /dev/null @@ -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; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPrayerOverlayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPrayerOverlayMode.java deleted file mode 100644 index 65cadb1789..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoPrayerOverlayMode.java +++ /dev/null @@ -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; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveMappings.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveMappings.java index d8fea0ca5d..4ee86a2823 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveMappings.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveMappings.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2019, Kyleeld - * Copyright (c) 2019, openosrs + * Copyright (c) 2019, RuneLitePlus * * 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 waveMapping; - + private static final Map waveMapping; @Getter(AccessLevel.PACKAGE) - private static final ImmutableMap npcNameMapping; + private static final Map npcNameMappingComplex; + @Getter(AccessLevel.PACKAGE) + private static final Map 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 nameMapBuilder = new ImmutableMap.Builder<>(); + ImmutableMap.Builder 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 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()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveOverlay.java index 03d86509c0..1e09ca3f6e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveOverlay.java @@ -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(), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoNamingDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoNamingDisplayMode.java new file mode 100644 index 0000000000..8818ef210a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoNamingDisplayMode.java @@ -0,0 +1,7 @@ +package net.runelite.client.plugins.inferno.displaymodes; + +public enum InfernoNamingDisplayMode +{ + SIMPLE, + COMPLEX +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoPrayerDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoPrayerDisplayMode.java new file mode 100644 index 0000000000..2d2b489e49 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoPrayerDisplayMode.java @@ -0,0 +1,8 @@ +package net.runelite.client.plugins.inferno.displaymodes; + +public enum InfernoPrayerDisplayMode +{ + PRAYER_TAB, + BOTTOM_RIGHT, + BOTH +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoSafespotDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoSafespotDisplayMode.java new file mode 100644 index 0000000000..fe3a35adde --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoSafespotDisplayMode.java @@ -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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoWaveDisplayMode.java similarity index 96% rename from runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveDisplayMode.java rename to runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoWaveDisplayMode.java index 96c7d12001..dcb1ccb5fb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/InfernoWaveDisplayMode.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoWaveDisplayMode.java @@ -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; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoZukShieldDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoZukShieldDisplayMode.java new file mode 100644 index 0000000000..081fff8f56 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inferno/displaymodes/InfernoZukShieldDisplayMode.java @@ -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; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java index 4ea4c471ee..895f394503 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java @@ -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);