From 57ba5a58580a65f19ad2855a608bbff5ec4dbfb1 Mon Sep 17 00:00:00 2001 From: Yuri Moens Date: Sat, 8 Jan 2022 19:46:18 +0100 Subject: [PATCH] Initial commit --- .gitignore | 24 + LICENSE | 674 ++++++++++++++++++ README.md | 31 + agility/agility.gradle.kts | 52 ++ .../openosrs/agility/AgilityConfig.java | 70 ++ .../openosrs/agility/AgilityPlugin.java | 159 +++++ .../io/reisub/openosrs/agility/Course.java | 71 ++ .../reisub/openosrs/agility/ObstacleArea.java | 32 + .../reisub/openosrs/agility/tasks/Alch.java | 70 ++ .../agility/tasks/HandleObstacle.java | 104 +++ .../openosrs/agility/tasks/PickupMark.java | 62 ++ autobones/autobones.gradle.kts | 52 ++ .../io/reisub/openosrs/autobones/Ashes.java | 17 + .../reisub/openosrs/autobones/Autobones.java | 78 ++ .../io/reisub/openosrs/autobones/Bones.java | 34 + .../io/reisub/openosrs/autobones/Config.java | 48 ++ autodropper/autodropper.gradle.kts | 52 ++ .../openosrs/autodropper/Autodropper.java | 129 ++++ .../reisub/openosrs/autodropper/Config.java | 65 ++ base/base.gradle.kts | 52 ++ .../io/reisub/openosrs/base/BasePlugin.java | 47 ++ .../java/io/reisub/openosrs/base/Config.java | 43 ++ birdhouse/birdhouse.gradle.kts | 52 ++ .../reisub/openosrs/birdhouse/Birdhouse.java | 103 +++ .../openosrs/birdhouse/BirdhouseSpace.java | 21 + .../openosrs/birdhouse/BirdhouseState.java | 25 + .../openosrs/birdhouse/BirdhouseType.java | 34 + .../io/reisub/openosrs/birdhouse/Config.java | 51 ++ .../openosrs/birdhouse/tasks/AddSeeds.java | 29 + .../openosrs/birdhouse/tasks/BankSpores.java | 75 ++ .../birdhouse/tasks/BuildBirdhouse.java | 26 + .../birdhouse/tasks/CraftBirdhouse.java | 28 + .../openosrs/birdhouse/tasks/Deposit.java | 93 +++ .../birdhouse/tasks/EmptyBirdhouse.java | 56 ++ .../openosrs/birdhouse/tasks/GetTools.java | 53 ++ .../birdhouse/tasks/GoToBirdhouse.java | 32 + .../openosrs/birdhouse/tasks/GoToIsland.java | 56 ++ .../birdhouse/tasks/GoToMushroomMeadow.java | 36 + .../birdhouse/tasks/GoToVerdantValley.java | 36 + .../birdhouse/tasks/HarvestSeaweed.java | 42 ++ .../openosrs/birdhouse/tasks/NoteSeaweed.java | 30 + .../openosrs/birdhouse/tasks/PickupSeed.java | 33 + .../birdhouse/tasks/PlantSeaweed.java | 45 ++ blackjack/blackjack.gradle.kts | 52 ++ .../openosrs/blackjack/BlackjackConfig.java | 54 ++ .../openosrs/blackjack/BlackjackPlugin.java | 150 ++++ .../io/reisub/openosrs/blackjack/Target.java | 17 + .../openosrs/blackjack/tasks/Blackjack.java | 43 ++ .../openosrs/blackjack/tasks/Escape.java | 20 + .../openosrs/blackjack/tasks/Pickpocket.java | 46 ++ build.gradle.kts | 128 ++++ buildSrc/build.gradle.kts | 43 ++ buildSrc/src/main/kotlin/BootstrapPlugin.kt | 9 + buildSrc/src/main/kotlin/BootstrapTask.kt | 97 +++ buildSrc/src/main/kotlin/Dependencies.kt | 56 ++ buildSrc/src/main/kotlin/JsonBuilder.kt | 57 ++ buildSrc/src/main/kotlin/Project.kt | 28 + consume/consume.gradle.kts | 52 ++ .../io/reisub/openosrs/consume/Config.java | 387 ++++++++++ .../io/reisub/openosrs/consume/Consume.java | 386 ++++++++++ cooker/cooker.gradle.kts | 52 ++ .../io/reisub/openosrs/cooker/Activity.java | 6 + .../io/reisub/openosrs/cooker/Config.java | 53 ++ .../io/reisub/openosrs/cooker/Cooker.java | 159 +++++ .../io/reisub/openosrs/cooker/tasks/Cook.java | 58 ++ .../openosrs/cooker/tasks/HandleBank.java | 56 ++ .../openosrs/cooker/tasks/SkipLevel.java | 22 + fisher/fisher.gradle.kts | 52 ++ .../reisub/openosrs/fisher/FisherConfig.java | 44 ++ .../reisub/openosrs/fisher/FisherPlugin.java | 90 +++ .../reisub/openosrs/fisher/tasks/DoBank.java | 51 ++ .../io/reisub/openosrs/fisher/tasks/Drop.java | 25 + .../io/reisub/openosrs/fisher/tasks/Fish.java | 36 + .../io/reisub/openosrs/fisher/tasks/Hop.java | 66 ++ glassblower/glassblower.gradle.kts | 52 ++ .../reisub/openosrs/glassblower/Config.java | 51 ++ .../openosrs/glassblower/Glassblower.java | 65 ++ .../reisub/openosrs/glassblower/Product.java | 19 + .../openosrs/glassblower/tasks/Blow.java | 62 ++ .../glassblower/tasks/HandleBank.java | 76 ++ .../glassblower/tasks/PickupSeed.java | 28 + gradle.properties | 7 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 +++++ gradlew.bat | 103 +++ masterthiever/masterthiever.gradle.kts | 52 ++ .../reisub/openosrs/masterthiever/Config.java | 43 ++ .../openosrs/masterthiever/MasterThiever.java | 96 +++ .../masterthiever/tasks/HandleBank.java | 53 ++ .../openosrs/masterthiever/tasks/Steal.java | 31 + miner/miner.gradle.kts | 52 ++ .../java/io/reisub/openosrs/miner/Config.java | 78 ++ .../java/io/reisub/openosrs/miner/Miner.java | 147 ++++ .../reisub/openosrs/miner/RockPosition.java | 10 + .../reisub/openosrs/miner/tasks/Deposit.java | 50 ++ .../io/reisub/openosrs/miner/tasks/Drop.java | 37 + .../io/reisub/openosrs/miner/tasks/Mine.java | 175 +++++ .../miner/tasks/PrepareBirdhouseRun.java | 187 +++++ .../openosrs/miner/tasks/PrepareMining.java | 41 ++ .../reisub/openosrs/miner/tasks/TpAway.java | 38 + .../openosrs/miner/tasks/TpToQuarry.java | 22 + .../io/reisub/openosrs/miner/tasks/Wait.java | 21 + .../miner/tasks/WalkToStartPosition.java | 43 ++ plugins.json | 1 + prayerflick/prayerflick.gradle.kts | 52 ++ .../reisub/openosrs/prayerflick/Config.java | 93 +++ .../openosrs/prayerflick/Prayerflick.java | 170 +++++ release/agility-0.0.1.jar | Bin 0 -> 13052 bytes release/autobones-1.0.0.jar | Bin 0 -> 6016 bytes release/autodropper-1.0.0.jar | Bin 0 -> 4474 bytes release/birdhouse-1.0.0.jar | Bin 0 -> 32708 bytes release/blackjack-0.0.1.jar | Bin 0 -> 8818 bytes release/consume-0.0.1.jar | Bin 0 -> 9613 bytes release/consume-1.0.0.jar | Bin 0 -> 10444 bytes release/cooker-0.0.1.jar | Bin 0 -> 9881 bytes release/fisher-0.0.1.jar | Bin 0 -> 10262 bytes release/glassblower-1.0.0.jar | Bin 0 -> 9902 bytes release/masterthiever-1.0.0.jar | Bin 0 -> 7252 bytes release/miner-1.0.0.jar | Bin 0 -> 24728 bytes release/prayerflick-1.0.0.jar | Bin 0 -> 5134 bytes release/prayerflick-1.1.0.jar | Bin 0 -> 5186 bytes release/shopper-1.0.0.jar | Bin 0 -> 6151 bytes release/smelter-1.0.0.jar | Bin 0 -> 10891 bytes release/tempoross-1.0.0.jar | Bin 0 -> 22939 bytes release/test-0.0.1.jar | Bin 0 -> 4617 bytes release/util-1.0.0.jar | Bin 0 -> 21818 bytes release/wintertodt-0.0.1.jar | Bin 0 -> 46415 bytes release/woodcutter-0.0.1.jar | Bin 0 -> 6136 bytes settings.gradle.kts | 56 ++ shopper/shopper.gradle.kts | 52 ++ .../io/reisub/openosrs/shopper/Config.java | 298 ++++++++ .../java/io/reisub/openosrs/shopper/Item.java | 16 + .../io/reisub/openosrs/shopper/Shopper.java | 109 +++ .../io/reisub/openosrs/shopper/tasks/Buy.java | 31 + smelter/smelter.gradle.kts | 52 ++ .../io/reisub/openosrs/smelter/Activity.java | 6 + .../io/reisub/openosrs/smelter/Config.java | 51 ++ .../reisub/openosrs/smelter/Ingredient.java | 11 + .../io/reisub/openosrs/smelter/Product.java | 18 + .../io/reisub/openosrs/smelter/Smelter.java | 161 +++++ .../openosrs/smelter/tasks/HandleBank.java | 67 ++ .../reisub/openosrs/smelter/tasks/Smelt.java | 58 ++ .../reisub/openosrs/tempoross/Activity.java | 20 + .../io/reisub/openosrs/tempoross/Config.java | 43 ++ .../reisub/openosrs/tempoross/Tempoross.java | 368 ++++++++++ .../openosrs/tempoross/TemporossObject.java | 15 + .../openosrs/tempoross/tasks/Attack.java | 33 + .../reisub/openosrs/tempoross/tasks/Cook.java | 41 ++ .../openosrs/tempoross/tasks/DouseFire.java | 35 + .../openosrs/tempoross/tasks/EnterBoat.java | 30 + .../openosrs/tempoross/tasks/FillBuckets.java | 38 + .../reisub/openosrs/tempoross/tasks/Fish.java | 69 ++ .../openosrs/tempoross/tasks/LeaveBoat.java | 30 + .../openosrs/tempoross/tasks/Repair.java | 41 ++ .../openosrs/tempoross/tasks/Stock.java | 64 ++ .../openosrs/tempoross/tasks/Tether.java | 29 + tempoross/tempoross.gradle.kts | 52 ++ .../io/reisub/openosrs/test/TestConfig.java | 44 ++ .../io/reisub/openosrs/test/TestPlugin.java | 107 +++ .../io/reisub/openosrs/test/tasks/Test.java | 36 + test/test.gradle.kts | 52 ++ .../java/io/reisub/openosrs/util/CScript.java | 146 ++++ .../io/reisub/openosrs/util/Calculations.java | 95 +++ .../java/io/reisub/openosrs/util/Config.java | 8 + .../io/reisub/openosrs/util/HopHelper.java | 67 ++ .../io/reisub/openosrs/util/ParentTask.java | 40 ++ .../java/io/reisub/openosrs/util/Task.java | 70 ++ .../java/io/reisub/openosrs/util/Util.java | 38 + .../reisub/openosrs/util/enums/Activity.java | 6 + .../io/reisub/openosrs/util/enums/Log.java | 21 + .../openosrs/util/tasks/CutFoodForKitten.java | 33 + .../io/reisub/openosrs/util/tasks/Eat.java | 64 ++ .../openosrs/util/tasks/HandleKitten.java | 92 +++ .../openosrs/util/tasks/KittenTask.java | 28 + .../reisub/openosrs/util/tasks/PickupCat.java | 31 + .../io/reisub/openosrs/util/tasks/Run.java | 50 ++ util/util.gradle.kts | 47 ++ .../reisub/openosrs/wintertodt/Activity.java | 17 + .../reisub/openosrs/wintertodt/BossData.java | 22 + .../io/reisub/openosrs/wintertodt/Config.java | 132 ++++ .../openosrs/wintertodt/InterruptType.java | 21 + .../reisub/openosrs/wintertodt/Scouter.java | 91 +++ .../io/reisub/openosrs/wintertodt/Side.java | 15 + .../openosrs/wintertodt/Wintertodt.java | 472 ++++++++++++ .../wintertodt/WintertodtProjectile.java | 30 + .../openosrs/wintertodt/tasks/Burn.java | 49 ++ .../openosrs/wintertodt/tasks/ChangeSide.java | 57 ++ .../openosrs/wintertodt/tasks/Chop.java | 52 ++ .../wintertodt/tasks/DodgeProjectile.java | 89 +++ .../wintertodt/tasks/EatWhileWaiting.java | 41 ++ .../reisub/openosrs/wintertodt/tasks/Fix.java | 43 ++ .../openosrs/wintertodt/tasks/Fletch.java | 55 ++ .../openosrs/wintertodt/tasks/GoToBank.java | 56 ++ .../wintertodt/tasks/GoToWintertodt.java | 60 ++ .../openosrs/wintertodt/tasks/HandleBank.java | 71 ++ .../reisub/openosrs/wintertodt/tasks/Hop.java | 122 ++++ .../openosrs/wintertodt/tasks/Light.java | 47 ++ .../wintertodt/tasks/MoveToBrazier.java | 38 + .../openosrs/wintertodt/tasks/OpenCrates.java | 48 ++ .../wintertodt/tasks/OpenInventory.java | 32 + wintertodt/wintertodt.gradle.kts | 52 ++ .../openosrs/woodcutter/WoodcutterConfig.java | 64 ++ .../openosrs/woodcutter/WoodcutterPlugin.java | 75 ++ .../openosrs/woodcutter/tasks/Chop.java | 39 + .../openosrs/woodcutter/tasks/Drop.java | 31 + woodcutter/woodcutter.gradle.kts | 52 ++ 207 files changed, 12402 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 agility/agility.gradle.kts create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/AgilityConfig.java create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/AgilityPlugin.java create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/Course.java create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/ObstacleArea.java create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/tasks/Alch.java create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/tasks/HandleObstacle.java create mode 100644 agility/src/main/java/io/reisub/openosrs/agility/tasks/PickupMark.java create mode 100644 autobones/autobones.gradle.kts create mode 100644 autobones/src/main/java/io/reisub/openosrs/autobones/Ashes.java create mode 100644 autobones/src/main/java/io/reisub/openosrs/autobones/Autobones.java create mode 100644 autobones/src/main/java/io/reisub/openosrs/autobones/Bones.java create mode 100644 autobones/src/main/java/io/reisub/openosrs/autobones/Config.java create mode 100644 autodropper/autodropper.gradle.kts create mode 100644 autodropper/src/main/java/io/reisub/openosrs/autodropper/Autodropper.java create mode 100644 autodropper/src/main/java/io/reisub/openosrs/autodropper/Config.java create mode 100644 base/base.gradle.kts create mode 100644 base/src/main/java/io/reisub/openosrs/base/BasePlugin.java create mode 100644 base/src/main/java/io/reisub/openosrs/base/Config.java create mode 100644 birdhouse/birdhouse.gradle.kts create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Birdhouse.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseSpace.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseState.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseType.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Config.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/AddSeeds.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BankSpores.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BuildBirdhouse.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/CraftBirdhouse.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/Deposit.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/EmptyBirdhouse.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GetTools.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToBirdhouse.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToIsland.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToMushroomMeadow.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToVerdantValley.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/HarvestSeaweed.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/NoteSeaweed.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PickupSeed.java create mode 100644 birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PlantSeaweed.java create mode 100644 blackjack/blackjack.gradle.kts create mode 100644 blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackConfig.java create mode 100644 blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackPlugin.java create mode 100644 blackjack/src/main/java/io/reisub/openosrs/blackjack/Target.java create mode 100644 blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Blackjack.java create mode 100644 blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Escape.java create mode 100644 blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Pickpocket.java create mode 100644 build.gradle.kts create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/BootstrapPlugin.kt create mode 100644 buildSrc/src/main/kotlin/BootstrapTask.kt create mode 100644 buildSrc/src/main/kotlin/Dependencies.kt create mode 100644 buildSrc/src/main/kotlin/JsonBuilder.kt create mode 100644 buildSrc/src/main/kotlin/Project.kt create mode 100644 consume/consume.gradle.kts create mode 100644 consume/src/main/java/io/reisub/openosrs/consume/Config.java create mode 100644 consume/src/main/java/io/reisub/openosrs/consume/Consume.java create mode 100644 cooker/cooker.gradle.kts create mode 100644 cooker/src/main/java/io/reisub/openosrs/cooker/Activity.java create mode 100644 cooker/src/main/java/io/reisub/openosrs/cooker/Config.java create mode 100644 cooker/src/main/java/io/reisub/openosrs/cooker/Cooker.java create mode 100644 cooker/src/main/java/io/reisub/openosrs/cooker/tasks/Cook.java create mode 100644 cooker/src/main/java/io/reisub/openosrs/cooker/tasks/HandleBank.java create mode 100644 cooker/src/main/java/io/reisub/openosrs/cooker/tasks/SkipLevel.java create mode 100644 fisher/fisher.gradle.kts create mode 100644 fisher/src/main/java/io/reisub/openosrs/fisher/FisherConfig.java create mode 100644 fisher/src/main/java/io/reisub/openosrs/fisher/FisherPlugin.java create mode 100644 fisher/src/main/java/io/reisub/openosrs/fisher/tasks/DoBank.java create mode 100644 fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Drop.java create mode 100644 fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Fish.java create mode 100644 fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Hop.java create mode 100644 glassblower/glassblower.gradle.kts create mode 100644 glassblower/src/main/java/io/reisub/openosrs/glassblower/Config.java create mode 100644 glassblower/src/main/java/io/reisub/openosrs/glassblower/Glassblower.java create mode 100644 glassblower/src/main/java/io/reisub/openosrs/glassblower/Product.java create mode 100644 glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/Blow.java create mode 100644 glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/HandleBank.java create mode 100644 glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/PickupSeed.java create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 masterthiever/masterthiever.gradle.kts create mode 100644 masterthiever/src/main/java/io/reisub/openosrs/masterthiever/Config.java create mode 100644 masterthiever/src/main/java/io/reisub/openosrs/masterthiever/MasterThiever.java create mode 100644 masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/HandleBank.java create mode 100644 masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/Steal.java create mode 100644 miner/miner.gradle.kts create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/Config.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/Miner.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/RockPosition.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/Deposit.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/Drop.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/Mine.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareBirdhouseRun.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareMining.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/TpAway.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/TpToQuarry.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/Wait.java create mode 100644 miner/src/main/java/io/reisub/openosrs/miner/tasks/WalkToStartPosition.java create mode 100644 plugins.json create mode 100644 prayerflick/prayerflick.gradle.kts create mode 100644 prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Config.java create mode 100644 prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Prayerflick.java create mode 100644 release/agility-0.0.1.jar create mode 100644 release/autobones-1.0.0.jar create mode 100644 release/autodropper-1.0.0.jar create mode 100644 release/birdhouse-1.0.0.jar create mode 100644 release/blackjack-0.0.1.jar create mode 100644 release/consume-0.0.1.jar create mode 100644 release/consume-1.0.0.jar create mode 100644 release/cooker-0.0.1.jar create mode 100644 release/fisher-0.0.1.jar create mode 100644 release/glassblower-1.0.0.jar create mode 100644 release/masterthiever-1.0.0.jar create mode 100644 release/miner-1.0.0.jar create mode 100644 release/prayerflick-1.0.0.jar create mode 100644 release/prayerflick-1.1.0.jar create mode 100644 release/shopper-1.0.0.jar create mode 100644 release/smelter-1.0.0.jar create mode 100644 release/tempoross-1.0.0.jar create mode 100644 release/test-0.0.1.jar create mode 100644 release/util-1.0.0.jar create mode 100644 release/wintertodt-0.0.1.jar create mode 100644 release/woodcutter-0.0.1.jar create mode 100644 settings.gradle.kts create mode 100644 shopper/shopper.gradle.kts create mode 100644 shopper/src/main/java/io/reisub/openosrs/shopper/Config.java create mode 100644 shopper/src/main/java/io/reisub/openosrs/shopper/Item.java create mode 100644 shopper/src/main/java/io/reisub/openosrs/shopper/Shopper.java create mode 100644 shopper/src/main/java/io/reisub/openosrs/shopper/tasks/Buy.java create mode 100644 smelter/smelter.gradle.kts create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/Activity.java create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/Config.java create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/Ingredient.java create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/Product.java create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/Smelter.java create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/tasks/HandleBank.java create mode 100644 smelter/src/main/java/io/reisub/openosrs/smelter/tasks/Smelt.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/Activity.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/Config.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/Tempoross.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/TemporossObject.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Attack.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Cook.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/DouseFire.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/EnterBoat.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/FillBuckets.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Fish.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/LeaveBoat.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Repair.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Stock.java create mode 100644 tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Tether.java create mode 100644 tempoross/tempoross.gradle.kts create mode 100644 test/src/main/java/io/reisub/openosrs/test/TestConfig.java create mode 100644 test/src/main/java/io/reisub/openosrs/test/TestPlugin.java create mode 100644 test/src/main/java/io/reisub/openosrs/test/tasks/Test.java create mode 100644 test/test.gradle.kts create mode 100644 util/src/main/java/io/reisub/openosrs/util/CScript.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/Calculations.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/Config.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/HopHelper.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/ParentTask.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/Task.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/Util.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/enums/Activity.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/enums/Log.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/tasks/CutFoodForKitten.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/tasks/Eat.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/tasks/HandleKitten.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/tasks/KittenTask.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/tasks/PickupCat.java create mode 100644 util/src/main/java/io/reisub/openosrs/util/tasks/Run.java create mode 100644 util/util.gradle.kts create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Activity.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/BossData.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Config.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/InterruptType.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Scouter.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Side.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Wintertodt.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/WintertodtProjectile.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Burn.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/ChangeSide.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Chop.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/DodgeProjectile.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/EatWhileWaiting.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fix.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fletch.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToBank.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToWintertodt.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/HandleBank.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Hop.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Light.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/MoveToBrazier.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenCrates.java create mode 100644 wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenInventory.java create mode 100644 wintertodt/wintertodt.gradle.kts create mode 100644 woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterConfig.java create mode 100644 woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterPlugin.java create mode 100644 woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Chop.java create mode 100644 woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Drop.java create mode 100644 woodcutter/woodcutter.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f42d9b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +target +nbactions.xml +nb-configuration.xml +/nbproject/ +project.properties +*.iml +.idea/ +.project +.settings/ +.classpath +runelite-client/src/main/resources/META-INF/MANIFEST.MF +git +classes/artifacts/client_jar/run.bat +classes/artifacts/client_jar/client.jar +!gradle-wrapper.jar +.live/ +**/build/* +*/out/ +.gradle/ +runelite-client/src/main/resources/runelite/* +runelite-client/dependencies.txt +.staging/ +hs_err* +replay_pid* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6eec8f2 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# OpenOSRS-external-example + +You can use this repository as base for your external plugins, and host it on GitHub to make your external plugins available for everybody through the external manager plugin panel in the OpenOSRS client. + +This has been updated for the new OPRS client version 4.0+. + +First of all you need to build the client (refer to the steps in this [guide][1]) +After building you need to upload all the artifacts to your local maven repository. +You can do this within intellij by going to the gradle panel at the right hand side and click on OpenOSRS -> Tasks -> publishing -> publishToMavenLocal + +In this repository you'll find two examples one is written in kotlin and the other one is written in java. +Before you start you need to make a couple changes: + +1. Go to the file "build.gradle.kts" in the main folder +2. Change the value of "project.extra["GithubUrl"]" to your github name and repository (only needed if you want to upload the plugins) +3. Change the value of "project.extra["PluginProvider"]" to your name or your alias. +4. Change the value of "project.extra["ProjectSupportUrl"]" to your discord channel or leave it empty. + +The file "{project}/{projectname}.gradle.kts" (for example "javaexample/javaexample.gradle.kts") has two values you'll need to change "project.extra["PluginName"]" and "project.extra["PluginDescription"]" + +After building your project you can find the plugin jar in the build/libs folder of that project. +This jar can be used directly by copying it into the "plugins" folder in the ".openosrs" directory: `USER/.openosrs/plugins`. + +If you want to bootstrap your plugins to make them available on GitHub you can just easily run the following task from the gradle panel in intellij Tasks -> other -> bootstrapPlugin +This will copy your plugin to the release folder in the main directory and fill the plugins.json file with the needed information. + +You should always run the clean task before running the bootstrapPlugins task! +Before bootstrapping make sure you've changed the version number of your project, duplicate version numbers are not allowed and the bootstrap task will fail. + + +[1]: https://github.com/open-osrs/runelite/wiki/Building-with-IntelliJ-IDEA \ No newline at end of file diff --git a/agility/agility.gradle.kts b/agility/agility.gradle.kts new file mode 100644 index 0000000..c37830f --- /dev/null +++ b/agility/agility.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Agility" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Hippity hoppity, jumps on your property" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/agility/src/main/java/io/reisub/openosrs/agility/AgilityConfig.java b/agility/src/main/java/io/reisub/openosrs/agility/AgilityConfig.java new file mode 100644 index 0000000..58da25f --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/AgilityConfig.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.agility; +import net.runelite.client.config.Button; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosAgilityConfig") + +public interface AgilityConfig extends Config { + @ConfigItem( + position = 0, + keyName = "courseSelection", + name = "Select course", + description = "Select a course to run." + ) + default Course courseSelection() + { + return Course.SEERS_VILLAGE; + } + + @ConfigItem( + position = 10, + keyName = "highAlch", + name = "High alch", + description = "Enable to high alch between obstacles." + ) + default boolean highAlch() { return false; } + + @ConfigItem( + position = 11, + keyName = "alchItems", + name = "High alch items", + description = "Names of items to alch, multiple items separated with a comma" + ) + default String alchItems() { return ""; } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/agility/src/main/java/io/reisub/openosrs/agility/AgilityPlugin.java b/agility/src/main/java/io/reisub/openosrs/agility/AgilityPlugin.java new file mode 100644 index 0000000..410933f --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/AgilityPlugin.java @@ -0,0 +1,159 @@ +package io.reisub.openosrs.agility; + +import com.google.inject.Provides; +import io.reisub.openosrs.agility.tasks.Alch; +import io.reisub.openosrs.agility.tasks.HandleObstacle; +import io.reisub.openosrs.agility.tasks.PickupMark; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Eat; +import io.reisub.openosrs.util.tasks.KittenTask; +import io.reisub.openosrs.util.tasks.Run; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Agility", + description = "Hippity hoppity, jumps on your property", + enabledByDefault = false +) +@Slf4j +public class AgilityPlugin extends iScript { + @Inject + private AgilityConfig config; + + private List tasks; + + private KittenTask kittenTask; + private Alch alchTask; + private HandleObstacle handleObstacleTask; + private PickupMark pickupMarkTask; + + @Provides + AgilityConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(AgilityConfig.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Agility"); + + Eat eatTask = injector.getInstance(Eat.class); + eatTask.setInterval(14, 24); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + kittenTask = KittenTask.getInstance(injector); + handleObstacleTask = injector.getInstance(HandleObstacle.class); + pickupMarkTask = injector.getInstance(PickupMark.class); + + if (config.highAlch()) { + alchTask = injector.getInstance(Alch.class); + } + + tasks = new ArrayList<>(); + tasks.add(eatTask); + tasks.add(runTask); + tasks.add(kittenTask); + tasks.add(pickupMarkTask); + if (config.highAlch()) { + tasks.add(alchTask); + } + tasks.add(handleObstacleTask); + + handleObstacleTask.setReady(true); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Agility"); + if (tasks != null) { + tasks.clear(); + } + + KittenTask.handleKitten = false; + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @Subscribe + private void onHitsplatApplied(HitsplatApplied event) { + if (handleObstacleTask != null) { + handleObstacleTask.onHitsplatApplied(event); + } + + if (pickupMarkTask != null) { + pickupMarkTask.onHitsplatApplied(event); + } + } + + @Subscribe + private void onStatChanged(StatChanged event) { + if (handleObstacleTask != null) { + handleObstacleTask.onStatChanged(event); + } + + if (pickupMarkTask != null) { + pickupMarkTask.onStatChanged(event); + } + + if (alchTask != null) { + alchTask.onStatChanged(event); + } + } + + @Subscribe + private void onGameTick(GameTick event) { + if (handleObstacleTask != null) { + handleObstacleTask.onGameTick(event); + } + + if (alchTask != null) { + alchTask.onGameTick(event); + } + } + + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (kittenTask != null) { + kittenTask.onChatMessage(chatMessage); + } + + if (handleObstacleTask != null) { + handleObstacleTask.onChatMessage(chatMessage); + } + } +} \ No newline at end of file diff --git a/agility/src/main/java/io/reisub/openosrs/agility/Course.java b/agility/src/main/java/io/reisub/openosrs/agility/Course.java new file mode 100644 index 0000000..10332de --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/Course.java @@ -0,0 +1,71 @@ +package io.reisub.openosrs.agility; + +import net.runelite.client.plugins.iutils.scene.Position; + +public enum Course { + SEERS_VILLAGE( + new ObstacleArea(2704,2733,3456,3495,0,14927), + new ObstacleArea(2721,2730,3490,3497,3,14928), + new ObstacleArea(2704,2714,3487,3497,2,14932), + new ObstacleArea(2709,2716,3476,3482,2,14929), + new ObstacleArea(2700,2715,3470,3475,3,14930), + new ObstacleArea(2690,2703,3459,3466,2,14931) + ), + POLLNIVNEACH( + new ObstacleArea(3345,3375,2957,3003,0,14935), + new ObstacleArea(3346,3351,2963,2968,1,14936), + new ObstacleArea(3352,3355,2973,2976,1,14937), + new ObstacleArea(3360,3362,2977,2979,1,14938), + new ObstacleArea(3366,3370,2974,2976,1,14939), + new ObstacleArea(3365,3369,2982,2986,1,14940), + new ObstacleArea(3355,3365,2980,2985,2,14941), + new ObstacleArea(3357,3370,2990,2995,2,14944), + new ObstacleArea(3356,3364,3000,3004,2,14945) + ), + RELEKKA( + new ObstacleArea(2622,2653,3656,3682,0,14946), + new ObstacleArea(2622,2626,3672,3676,3,14947), + new ObstacleArea(2615,2622,3658,3668,3,14987), + new ObstacleArea(2626,2630,3651,3655,3,14990), + new ObstacleArea(2639,2644,3649,3653,3,14991), + new ObstacleArea(2643,2650,3657,3662,3,14992), + new ObstacleArea(2655,2666,3665,3685,3,14994) + ), + ARDOUGNE( + new ObstacleArea(2650,2674,3293,3319,0,15608), + new ObstacleArea(2671,2671,3299,3309,3,15609), + new ObstacleArea(2662,2665,3318,3318,3,26635), + new ObstacleArea(2654,2657,3318,3318,3,15610), + new ObstacleArea(2653,2653,3310,3314,3,15611), + new ObstacleArea(2651,2653,3300,3309,3,28912), + new ObstacleArea(2656,2656,3297,3297,3,15612) + ); + + private final ObstacleArea[] areas; + + Course(ObstacleArea... area) { + areas = area; + } + + public int getNextObstacleID(Position current) { + for (ObstacleArea area : areas) { + if (area.contains(current)) { + return area.getId(); + } + } + + return 0; + } + + public boolean markIsReachable(Position player, Position mark) { + if (player == null || mark == null) return false; + + for (ObstacleArea area : areas) { + if (area.contains(player) && area.contains(mark)) { + return true; + } + } + + return false; + } +} diff --git a/agility/src/main/java/io/reisub/openosrs/agility/ObstacleArea.java b/agility/src/main/java/io/reisub/openosrs/agility/ObstacleArea.java new file mode 100644 index 0000000..a4e515d --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/ObstacleArea.java @@ -0,0 +1,32 @@ +package io.reisub.openosrs.agility; + +import lombok.Getter; +import net.runelite.client.plugins.iutils.scene.Position; + +public class ObstacleArea { + private final int minX; + private final int maxX; + private final int minY; + private final int maxY; + private final int z; + + @Getter + private final int id; + + public ObstacleArea(int minX, int maxX, int minY, int maxY, int z, int id) { + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + this.z = z; + this.id = id; + } + + public boolean contains(Position position) { + return position.x >= minX + && position.x <= maxX + && position.y >= minY + && position.y <= maxY + && position.z == z; + } +} diff --git a/agility/src/main/java/io/reisub/openosrs/agility/tasks/Alch.java b/agility/src/main/java/io/reisub/openosrs/agility/tasks/Alch.java new file mode 100644 index 0000000..5339ebd --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/tasks/Alch.java @@ -0,0 +1,70 @@ +package io.reisub.openosrs.agility.tasks; + +import io.reisub.openosrs.agility.AgilityConfig; +import io.reisub.openosrs.util.Task; +import net.runelite.api.Skill; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.StatChanged; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.iutils.api.Spells; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iWidget; + +import javax.inject.Inject; + +public class Alch extends Task { + @Inject + private AgilityConfig config; + + private boolean ready; + private long lastAlchTick; + private String[] items; + + @Override + public String getStatus() { + return "High alching"; + } + + @Override + public boolean validate() { + if (items == null) { + items = config.alchItems().split(","); + for (int i = 0; i < items.length; i++) { + items[i] = items[i].trim(); + } + } + + return config.highAlch() + && game.inventory().withName(items).exists() + && game.inventory().withName("Nature rune").exists() + && ready; + } + + @Override + public void execute() { + ready = false; + lastAlchTick = game.ticks(); + + iWidget magicTab = game.widget(WidgetInfo.RESIZABLE_VIEWPORT_MAGIC_TAB); + if (magicTab == null || magicTab.hidden()) { + game.openInterface(3); + game.tick(); + } + + InventoryItem item = game.inventory().withName(items).first(); + game.widget(Spells.HIGH_LEVEL_ALCHEMY.getInfo()).useOn(item); + game.sleepDelay(); + } + + public void onStatChanged(StatChanged event) { + if (event.getSkill().equals(Skill.AGILITY)) { + ready = true; + } + } + + public void onGameTick(GameTick event) { + if (game.ticks() - lastAlchTick >= 5 && game.localPlayer().position().z == 0) { + ready = true; + } + } +} diff --git a/agility/src/main/java/io/reisub/openosrs/agility/tasks/HandleObstacle.java b/agility/src/main/java/io/reisub/openosrs/agility/tasks/HandleObstacle.java new file mode 100644 index 0000000..d59a04b --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/tasks/HandleObstacle.java @@ -0,0 +1,104 @@ +package io.reisub.openosrs.agility.tasks; + +import io.reisub.openosrs.agility.AgilityConfig; +import io.reisub.openosrs.util.Task; +import net.runelite.api.Skill; +import net.runelite.api.events.*; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +public class HandleObstacle extends Task { + @Inject + private AgilityConfig config; + + private boolean ready; + private int timeout = 10; + private int idleTicks; + + @Override + public String getStatus() { + return "Handling obstacle"; + } + + @Override + public boolean validate() { + return ready; + } + + @Override + public void execute() { + int id = config.courseSelection().getNextObstacleID(game.localPlayer().position()); + if (id == 0) return; + + iObject object = game.objects().withId(id).first(); + if (object == null) return; + + game.sleepDelay(); + object.interact(object.actions().get(0)); + game.tick(); + + ready = false; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + + public void onHitsplatApplied(HitsplatApplied event) { + if (event.getHitsplat() != null && event.getHitsplat().isMine()) { + timeout = calc.random(2, 3); + } + } + + public void onStatChanged(StatChanged event) { + if (event.getSkill().equals(Skill.AGILITY)) { + ready = true; + } + + if (event.getSkill().equals(Skill.MAGIC)) { + if (game.localPlayer().position().z == 0) { + if (!game.localPlayer().isMoving()) { + ready = true; + } + + int id = config.courseSelection().getNextObstacleID(game.localPlayer().position()); + iObject object = game.objects().withId(id).first(); + + if (object != null && game.localPlayer().position().distanceTo(object.position()) < 4) { + ready = true; + } + } else { + ready = true; + } + } + } + + public void onGameTick(GameTick event) { + if (game.localPlayer().isIdle()) { + idleTicks++; + if (idleTicks >= timeout) { + ready = true; + timeout = calc.random(7, 13); + } + } else { + idleTicks = 0; + } + } + + public void onChatMessage(ChatMessage chatMessage) { + if (chatMessage.getMessage().contains("Can't reach that")) { + timeout = calc.random(1, 3); + } + + if (chatMessage.getMessage().contains("You softly stroke your cat.")) { + ready = true; + } + } + + public void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + ready = true; + } + } +} diff --git a/agility/src/main/java/io/reisub/openosrs/agility/tasks/PickupMark.java b/agility/src/main/java/io/reisub/openosrs/agility/tasks/PickupMark.java new file mode 100644 index 0000000..d11419a --- /dev/null +++ b/agility/src/main/java/io/reisub/openosrs/agility/tasks/PickupMark.java @@ -0,0 +1,62 @@ +package io.reisub.openosrs.agility.tasks; + +import io.reisub.openosrs.agility.AgilityConfig; +import io.reisub.openosrs.util.Task; +import net.runelite.api.Skill; +import net.runelite.api.events.HitsplatApplied; +import net.runelite.api.events.StatChanged; +import net.runelite.client.plugins.iutils.game.iGroundItem; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +public class PickupMark extends Task { + @Inject + private AgilityConfig config; + + private boolean failed; + + @Override + public String getStatus() { + return "Picking up mark"; + } + + @Override + public boolean validate() { + if (failed) return false; + + iGroundItem mark = game.groundItems().withName("Mark of grace").nearest(); + + Position markPos = null; + try { + markPos = mark.position(); + } catch (NullPointerException ignored) {} + + return mark != null + && config.courseSelection().markIsReachable(game.localPlayer().position(), markPos); + } + + @Override + public void execute() { + game.waitUntil(() -> game.localPlayer().isIdle(), 10); + + game.groundItems().withName("Mark of grace").findFirst().ifPresent(mark -> { + mark.interact(mark.actions().get(0)); + game.tick(); + }); + + game.waitUntil(() -> !game.groundItems().withName("Mark of grace").exists(), 10); + } + + public void onHitsplatApplied(HitsplatApplied event) { + if (event.getHitsplat() != null && event.getHitsplat().isMine()) { + failed = true; + } + } + + public void onStatChanged(StatChanged event) { + if (event.getSkill().equals(Skill.AGILITY)) { + failed = false; + } + } +} diff --git a/autobones/autobones.gradle.kts b/autobones/autobones.gradle.kts new file mode 100644 index 0000000..74f1676 --- /dev/null +++ b/autobones/autobones.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Autobones" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Buries ashes and scatters bones, or something like that." // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/autobones/src/main/java/io/reisub/openosrs/autobones/Ashes.java b/autobones/src/main/java/io/reisub/openosrs/autobones/Ashes.java new file mode 100644 index 0000000..09694c1 --- /dev/null +++ b/autobones/src/main/java/io/reisub/openosrs/autobones/Ashes.java @@ -0,0 +1,17 @@ +package io.reisub.openosrs.autobones; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +@AllArgsConstructor +@Getter +public enum Ashes { + FIENDISH_ASHES(ItemID.FIENDISH_ASHES), + VILE_ASHES(ItemID.VILE_ASHES), + MALICIOUS_ASHES(ItemID.MALICIOUS_ASHES), + ABYSSAL_ASHES(ItemID.ABYSSAL_ASHES), + INFERNAL_ASHES(ItemID.INFERNAL_ASHES); + + private final int id; +} diff --git a/autobones/src/main/java/io/reisub/openosrs/autobones/Autobones.java b/autobones/src/main/java/io/reisub/openosrs/autobones/Autobones.java new file mode 100644 index 0000000..688b1b0 --- /dev/null +++ b/autobones/src/main/java/io/reisub/openosrs/autobones/Autobones.java @@ -0,0 +1,78 @@ +package io.reisub.openosrs.autobones; + +import com.google.inject.Provides; +import io.reisub.openosrs.util.Util; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameState; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.game.Game; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Autobones", + description = "Buries ashes and scatters bones, or something like that.", + enabledByDefault = false +) +@Slf4j +public class Autobones extends Plugin { + @SuppressWarnings("unused") + @Inject + private Game game; + + @SuppressWarnings("unused") + @Inject + private Config config; + + @SuppressWarnings("unused") + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + private ScheduledExecutorService executor; + + @Override + protected void startUp() { + log.info("Starting Chaos Autobones"); + + executor = Executors.newSingleThreadScheduledExecutor(); + } + + @Override + protected void shutDown() { + log.info("Stopping Chaos Autobones"); + + executor.shutdownNow(); + } + + @SuppressWarnings("unused") + @Subscribe + private void onItemContainerChanged(ItemContainerChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + if (config.bones().getId() == -1 && config.ashes().getId() == -1) return; + + executor.schedule(() -> { + game.inventory().withId(config.bones().getId(), config.ashes().getId()).first().interact(0); + }, 0, TimeUnit.MILLISECONDS); + } +} \ No newline at end of file diff --git a/autobones/src/main/java/io/reisub/openosrs/autobones/Bones.java b/autobones/src/main/java/io/reisub/openosrs/autobones/Bones.java new file mode 100644 index 0000000..9ace50a --- /dev/null +++ b/autobones/src/main/java/io/reisub/openosrs/autobones/Bones.java @@ -0,0 +1,34 @@ +package io.reisub.openosrs.autobones; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +@AllArgsConstructor +@Getter +public enum Bones { + NONE(-1), + BONES(ItemID.BONES), + WOLF_BONES(ItemID.WOLF_BONES), + BURNT_BONES(ItemID.BURNT_BONES), + MONKEY_BONES(ItemID.MONKEY_BONES), + BAT_BONES(ItemID.BAT_BONES), + BIG_BONES(ItemID.BIG_BONES), + JOGRE_BONES(ItemID.JOGRE_BONES), + ZOGRE_BONES(ItemID.ZOGRE_BONES), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES), + WYRM_BONES(ItemID.WYRM_BONES), + WYVERN_BONES(ItemID.WYVERN_BONES), + DRAGON_BONES(ItemID.DRAGON_BONES), + DRAKE_BONES(ItemID.DRAKE_BONES), + FAYRG_BONES(ItemID.FAYRG_BONES), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES), + RAURG_BONES(ItemID.RAURG_BONES), + HYDRA_BONES(ItemID.HYDRA), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES), + OURG_BONES(ItemID.OURG_BONES), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES); + + private final int id; +} diff --git a/autobones/src/main/java/io/reisub/openosrs/autobones/Config.java b/autobones/src/main/java/io/reisub/openosrs/autobones/Config.java new file mode 100644 index 0000000..004db72 --- /dev/null +++ b/autobones/src/main/java/io/reisub/openosrs/autobones/Config.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.autobones; + +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chaosautobones") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "bones", + name = "Bones", + description = "Choose which bones to auto-bury.", + position = 0 + ) + default Bones bones() { return Bones.BIG_BONES; } + + @ConfigItem( + keyName = "ashes", + name = "Ashes", + description = "Choose which ashes to auto-bury.", + position = 1 + ) + default Ashes ashes() { return Ashes.VILE_ASHES; } +} \ No newline at end of file diff --git a/autodropper/autodropper.gradle.kts b/autodropper/autodropper.gradle.kts new file mode 100644 index 0000000..42279c6 --- /dev/null +++ b/autodropper/autodropper.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Autodropper" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Automatically drops stuff" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/autodropper/src/main/java/io/reisub/openosrs/autodropper/Autodropper.java b/autodropper/src/main/java/io/reisub/openosrs/autodropper/Autodropper.java new file mode 100644 index 0000000..d7ffa13 --- /dev/null +++ b/autodropper/src/main/java/io/reisub/openosrs/autodropper/Autodropper.java @@ -0,0 +1,129 @@ +package io.reisub.openosrs.autodropper; + +import com.google.inject.Provides; +import io.reisub.openosrs.util.Util; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameState; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.game.Game; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Autodropper", + description = "Automatically drops stuff", + enabledByDefault = false +) +@Slf4j +public class Autodropper extends Plugin { + @SuppressWarnings("unused") + @Inject + private Game game; + + @SuppressWarnings("unused") + @Inject + private Config config; + + @SuppressWarnings("unused") + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + private ScheduledExecutorService executor; + private String[] itemNames; + private int[] itemIds; + + @Override + protected void startUp() { + log.info("Starting Chaos Autodropper"); + + executor = Executors.newSingleThreadScheduledExecutor(); + } + + @Override + protected void shutDown() { + log.info("Stopping Chaos Autodropper"); + + executor.shutdownNow(); + } + + @SuppressWarnings("unused") + @Subscribe + private void onItemContainerChanged(ItemContainerChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + if (!config.dropWhileItemSelected() && game.client().isItemSelected() == 1) return; + + if (itemNames == null) itemNames = parseNames(); + if (itemIds == null) itemIds = parseIds(); + + executor.schedule(() -> { + game.inventory().withName(itemNames).drop(); + game.inventory().withId(itemIds).drop(); + }, 0, TimeUnit.MILLISECONDS); + } + + @Subscribe + @SuppressWarnings("unused") + private void onConfigChanged(ConfigChanged event) { + String name = this.getName().replaceAll(" ", "").toLowerCase(Locale.ROOT); + + if (event.getGroup().equals(name) && event.getKey().startsWith("item")) { + itemNames = parseNames(); + itemIds = parseIds(); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked event) { + String name = this.getName().replaceAll(" ", "").toLowerCase(Locale.ROOT); + + if (event.getGroup().equals(name) && event.getKey().equals("reloadButton")) { + itemNames = parseNames(); + itemIds = parseIds(); + } + } + + private String[] parseNames() { + String[] names = config.itemNames().split(";"); + + for (int i = 0; i < names.length; i++) { + names[i] = names[i].trim(); + } + + return names; + } + + private int[] parseIds() { + if (config.itemIds().equals("")) return new int[]{}; + + String[] idsStr = config.itemIds().split(";"); + int[] ids = new int[idsStr.length]; + + for (int i = 0; i < idsStr.length; i++) { + ids[i] = Integer.parseInt(idsStr[i].trim()); + } + + return ids; + } +} \ No newline at end of file diff --git a/autodropper/src/main/java/io/reisub/openosrs/autodropper/Config.java b/autodropper/src/main/java/io/reisub/openosrs/autodropper/Config.java new file mode 100644 index 0000000..87bb121 --- /dev/null +++ b/autodropper/src/main/java/io/reisub/openosrs/autodropper/Config.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.autodropper; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chaosautodropper") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "itemNames", + name = "Item names", + description = "List of item names, separated with a semicolon.", + position = 0 + ) + default String itemNames() { return "Empty plant pot"; } + + @ConfigItem( + keyName = "itemIds", + name = "Item IDs", + description = "List of item IDs, separated with a semicolon. Useful if you want to differentiate between 2 items with the same name.", + position = 1 + ) + default String itemIds() { return ""; } + + @ConfigItem( + keyName = "dropWhileItemSelected", + name = "Drop while item selected", + description = "If enabled, this will drop items even if an item is selected, resulting in deselecting the item.", + position = 2 + ) + default boolean dropWhileItemSelected() { return false; } + + @ConfigItem( + keyName = "reloadButton", + name = "Reload configuration", + description = "Force reload the configuration.", + position = 100 + ) + default Button reloadButton() { return new Button(); } +} \ No newline at end of file diff --git a/base/base.gradle.kts b/base/base.gradle.kts new file mode 100644 index 0000000..debacb1 --- /dev/null +++ b/base/base.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Base" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/base/src/main/java/io/reisub/openosrs/base/BasePlugin.java b/base/src/main/java/io/reisub/openosrs/base/BasePlugin.java new file mode 100644 index 0000000..2a29bf8 --- /dev/null +++ b/base/src/main/java/io/reisub/openosrs/base/BasePlugin.java @@ -0,0 +1,47 @@ +package io.reisub.openosrs.smelter; + +import com.google.inject.Provides; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Eat; +import io.reisub.openosrs.util.tasks.KittenTask; +import io.reisub.openosrs.util.tasks.Run; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Base", + description = "", + enabledByDefault = false +) +@Slf4j +public class BasePlugin extends iScript { + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(BaseConfig.class); + } + + @Override + protected void onStart() { + super.onStart(); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + tasks.add(runTask); + } +} \ No newline at end of file diff --git a/base/src/main/java/io/reisub/openosrs/base/Config.java b/base/src/main/java/io/reisub/openosrs/base/Config.java new file mode 100644 index 0000000..f5b3a53 --- /dev/null +++ b/base/src/main/java/io/reisub/openosrs/base/Config.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.smelter; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chaosbase") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/birdhouse/birdhouse.gradle.kts b/birdhouse/birdhouse.gradle.kts new file mode 100644 index 0000000..c60890d --- /dev/null +++ b/birdhouse/birdhouse.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Birdhouse" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Automatic bird massacre" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Birdhouse.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Birdhouse.java new file mode 100644 index 0000000..fc39248 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Birdhouse.java @@ -0,0 +1,103 @@ +package io.reisub.openosrs.birdhouse; + +import com.google.inject.Provides; +import io.reisub.openosrs.birdhouse.tasks.*; +import io.reisub.openosrs.util.CScript; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Run; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scene.Position; +import org.pf4j.Extension; + +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Birdhouse", + description = "Automatic bird massacre", + enabledByDefault = false +) +@Slf4j +public class Birdhouse extends CScript { + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Getter + private Map birdhouseTimers; + + @Getter + private final Position islandPosition = new Position(3769, 3898, 0); + + private final Position hillHousePosition = new Position(3764, 3869, 1); + + private boolean active; + + @Override + protected void onStart() { + super.onStart(); + active = true; + + birdhouseTimers = new HashMap<>(); + birdhouseTimers.put(BirdhouseSpace.MEADOW_NORTH, Instant.EPOCH); + birdhouseTimers.put(BirdhouseSpace.MEADOW_SOUTH, Instant.EPOCH); + birdhouseTimers.put(BirdhouseSpace.VALLEY_NORTH, Instant.EPOCH); + birdhouseTimers.put(BirdhouseSpace.VALLEY_SOUTH, Instant.EPOCH); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + tasks.add(runTask); + addTask(AddSeeds.class); + addTask(BankSpores.class); + addTask(BuildBirdhouse.class); + addTask(CraftBirdhouse.class); + addTask(EmptyBirdhouse.class); + addTask(GetTools.class); + addTask(GoToBirdhouse.class); + addTask(GoToIsland.class); + addTask(GoToMushroomMeadow.class); + addTask(GoToVerdantValley.class); + addTask(PickupSeed.class); + addTask(PlantSeaweed.class); + addTask(NoteSeaweed.class); + addTask(HarvestSeaweed.class); + addTask(Deposit.class); + } + + @Override + protected void onStop() { + super.onStop(); + active = false; + } + + @SuppressWarnings("unused") + @Subscribe + private void onGameTick(GameTick event) { + if (isLoggedIn() + && game.localPlayer().position().regionID() == 14908) { + if (!active && game.localPlayer().position().distanceTo(hillHousePosition) < 10 && game.inventory().withNamePart("logs").count() == 4) { + execute(); + } else if (active && game.inventory().all().isEmpty() && !bank.isOpen() && game.localPlayer().position().distanceTo(islandPosition) < 10) { + execute(); + } + } + } + + public boolean hasRecentlyBeenEmptied(BirdhouseSpace space) { + return Duration.between(getBirdhouseTimers().get(space), Instant.now()).getSeconds() < 2000; + } +} \ No newline at end of file diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseSpace.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseSpace.java new file mode 100644 index 0000000..63ee544 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseSpace.java @@ -0,0 +1,21 @@ +package io.reisub.openosrs.birdhouse; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.VarPlayer; +import net.runelite.client.plugins.iutils.scene.Position; + +@AllArgsConstructor +@Getter +public enum BirdhouseSpace { + // declaration order should not be changed, this is the order we do the birdhouses in + VALLEY_SOUTH(VarPlayer.BIRD_HOUSE_VALLEY_SOUTH, new Position(3763, 3755, 0)), + VALLEY_NORTH(VarPlayer.BIRD_HOUSE_VALLEY_NORTH, new Position(3768, 3761, 0)), + MEADOW_NORTH(VarPlayer.BIRD_HOUSE_MEADOW_NORTH, new Position(3677, 3882, 0)), + MEADOW_SOUTH(VarPlayer.BIRD_HOUSE_MEADOW_SOUTH, new Position(3679, 3815, 0)); + + // 3681 3819 == near south meadow + + private final VarPlayer varp; + private final Position position; +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseState.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseState.java new file mode 100644 index 0000000..e12e00f --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseState.java @@ -0,0 +1,25 @@ +package io.reisub.openosrs.birdhouse; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum BirdhouseState { + SEEDED(), + BUILT(), + EMPTY(), + UNKNOWN(); + + public static BirdhouseState fromVarpValue(int varp) { + if (varp < 0 || varp > BirdhouseType.values().length * 3) { + return UNKNOWN; + } else if (varp == 0) { + return EMPTY; + } else if (varp % 3 == 0) { + return SEEDED; + } else { + return BUILT; + } + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseType.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseType.java new file mode 100644 index 0000000..85d79db --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/BirdhouseType.java @@ -0,0 +1,34 @@ +package io.reisub.openosrs.birdhouse; + +import javax.annotation.Nullable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +@AllArgsConstructor +@Getter +public enum BirdhouseType { + NORMAL("Bird House", ItemID.BIRD_HOUSE), + OAK("Oak Bird House", ItemID.OAK_BIRD_HOUSE), + WILLOW("Willow Bird House", ItemID.WILLOW_BIRD_HOUSE), + TEAK("Teak Bird House", ItemID.TEAK_BIRD_HOUSE), + MAPLE("Maple Bird House", ItemID.MAPLE_BIRD_HOUSE), + MAHOGANY("Mahogany Bird House", ItemID.MAHOGANY_BIRD_HOUSE), + YEW("Yew Bird House", ItemID.YEW_BIRD_HOUSE), + MAGIC("Magic Bird House", ItemID.MAGIC_BIRD_HOUSE), + REDWOOD("Redwood Bird House", ItemID.REDWOOD_BIRD_HOUSE); + + private final String name; + private final int itemID; + + @Nullable + static BirdhouseType fromVarpValue(int varp) { + int index = (varp - 1) / 3; + + if (varp <= 0 || index >= values().length) { + return null; + } + + return values()[index]; + } +} \ No newline at end of file diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Config.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Config.java new file mode 100644 index 0000000..dd2a675 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/Config.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.birdhouse; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chaosbirdhouse") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "farmSeaweed", + name = "Farm seaweed", + description = "Harvest and plant seaweed after a birdhouse run.", + position = 0 + ) + default boolean farmSeaweed() { return true; } + + @ConfigItem( + keyName = "startButton", + name = "Force Start/Stop", + description = "The script should automatically start and stop. Use this button for manual overrides.", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/AddSeeds.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/AddSeeds.java new file mode 100644 index 0000000..21fd0c4 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/AddSeeds.java @@ -0,0 +1,29 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iObject; + +public class AddSeeds extends Task { + @Override + public String getStatus() { + return "Adding seeds to birdhouse"; + } + + @Override + public boolean validate() { + return game.objects().filter(iObject -> iObject.name().endsWith("(empty)")).exists(); + } + + @Override + public void execute() { + iObject birdhouse = game.objects().filter(iObject -> iObject.name().endsWith("(empty)")).nearest(); + InventoryItem seeds = game.inventory().withNamePart("seed").first(); + if (birdhouse == null || seeds == null) return; + + int quantity = seeds.quantity(); + seeds.useOn(birdhouse); + + game.waitUntil(() -> game.inventory().withNamePart("seed").first() == null || game.inventory().withNamePart("seed").first().quantity() < quantity, 5); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BankSpores.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BankSpores.java new file mode 100644 index 0000000..c318c35 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BankSpores.java @@ -0,0 +1,75 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.birdhouse.Birdhouse; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import javax.inject.Inject; + +public class BankSpores extends Task { + @Inject + private Birdhouse plugin; + + @Override + public String getStatus() { + return "Depositing everything and withdrawing spores"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().distanceTo(plugin.getIslandPosition()) < 10 + && game.inventory().withNamePart("nest").exists(); + } + + @Override + public void execute() { + for (InventoryItem nest : game.inventory().withNamePart("nest").withAction("Search").all()) { + nest.interact("Search"); + game.tick(); + } + + if (!bank.isOpen()) { + iObject bankObj = game.objects().withName("Bank chest", "Bank booth", "Bank Chest-wreck").nearest(); + if (bankObj == null) return; + + bankObj.interact(0); + game.waitUntil(() -> bank.isOpen(), 15); + } + + bank.depositInventory(); + game.tick(); + + bank.withdraw(ItemID.SEAWEED_SPORE, 2, false); + game.tick(); + + bank.withdraw(ItemID.FISHBOWL_HELMET, 1, false); + game.tick(); + + bank.withdraw(ItemID.DIVING_APPARATUS, 1, false); + game.tick(); + + bank.close(); + game.waitUntil(() -> !bank.isOpen(), 5); + + game.inventory().withId(ItemID.FISHBOWL_HELMET).findFirst().ifPresent((helmet) -> helmet.interact("Wear")); + game.tick(); + + game.inventory().withId(ItemID.DIVING_APPARATUS).findFirst().ifPresent((apparatus) -> apparatus.interact("Wear")); + game.tick(); + iObject rowboat = game.objects().withName("Rowboat").nearest(); + if (rowboat == null) return; + + rowboat.interact("Dive"); + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT + || game.localPlayer().position().regionID() == 15008, 10); + + if (chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT) { + chatbox.chooseOption(1); + game.waitUntil(() -> game.localPlayer().position().regionID() == 15008, 10); + } + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BuildBirdhouse.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BuildBirdhouse.java new file mode 100644 index 0000000..4ac9b51 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/BuildBirdhouse.java @@ -0,0 +1,26 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iObject; + +public class BuildBirdhouse extends Task { + @Override + public String getStatus() { + return "Building birdhouse"; + } + + @Override + public boolean validate() { + return game.inventory().withNamePart("bird house").exists(); + } + + @Override + public void execute() { + iObject space = game.objects().withName("Space").withAction("Build").nearest(); + if (space == null) return; + + space.interact("Build"); + game.waitUntil(() -> !game.inventory().withNamePart("bird house").exists(), 5); + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/CraftBirdhouse.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/CraftBirdhouse.java new file mode 100644 index 0000000..60168e4 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/CraftBirdhouse.java @@ -0,0 +1,28 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.InventoryItem; + +public class CraftBirdhouse extends Task { + @Override + public String getStatus() { + return "Crafting birdhouse"; + } + + @Override + public boolean validate() { + return game.inventory().withId(ItemID.CLOCKWORK).exists(); + } + + @Override + public void execute() { + InventoryItem chisel = game.inventory().withId(ItemID.CHISEL).first(); + InventoryItem logs = game.inventory().withNamePart("logs").first(); + if (chisel == null || logs == null) return; + + chisel.useOn(logs); + game.waitUntil(() -> !game.inventory().withId(ItemID.CLOCKWORK).exists(), 5); + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/Deposit.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/Deposit.java new file mode 100644 index 0000000..b41074d --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/Deposit.java @@ -0,0 +1,93 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.birdhouse.Birdhouse; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.game.iWidget; + +import javax.inject.Inject; + +public class Deposit extends Task { + @Inject + private Birdhouse plugin; + + @Override + public String getStatus() { + return "Depositing everything"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 15008 + && game.objects().withName("Seaweed").count() == 2; + } + + @Override + public void execute() { + iNPC leprechaun = game.npcs().withName("Tool Leprechaun").first(); + if (leprechaun == null) return; + + leprechaun.interact("Exchange"); + game.waitUntil(() -> game.widget(125, 0) != null, 30); + game.tick(); + + iWidget dibber = game.widget(126, 2); + if (dibber == null) return; + dibber.interact("Store-1"); + game.tick(); + + iWidget spade = game.widget(126, 3); + if (spade == null) return; + spade.interact("Store-1"); + game.tick(); + + for (int i = 0; i < 2; i++) { + iWidget bucket = game.widget(126, 9); + if (bucket == null) return; + bucket.interact("Store-1"); + game.sleepDelay(); + } + game.tick(); + + iWidget close = game.widget(125, 1, 11); + if (close == null) return; + close.interact("Close"); + + game.waitUntil(() -> game.widget(125, 0) == null, 5); + + iObject rope = game.objects().withName("Anchor rope").first(); + if (rope == null) return; + + rope.interact("Climb"); + game.tick(); + + if (game.inventory().withAction("Wear").exists()) { + for (InventoryItem gear : game.inventory().withAction("Wear").all()) { + gear.interact("Wear"); + game.tick(3); + } + + game.tick(); + rope.interact("Climb"); + } + + game.waitUntil(() -> game.localPlayer().position().regionID() == 14908, 30); + game.tick(); + + if (!bank.isOpen()) { + iObject bankObj = game.objects().withName("Bank chest", "Bank booth", "Bank Chest-wreck").nearest(); + if (bankObj == null) return; + + bankObj.interact(0); + game.waitUntil(() -> bank.isOpen(), 15); + } + + bank.depositInventory(); + game.sleepDelay(); + + bank.close(); + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/EmptyBirdhouse.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/EmptyBirdhouse.java new file mode 100644 index 0000000..64b7abf --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/EmptyBirdhouse.java @@ -0,0 +1,56 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.birdhouse.Birdhouse; +import io.reisub.openosrs.birdhouse.BirdhouseSpace; +import io.reisub.openosrs.birdhouse.BirdhouseState; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; + +public class EmptyBirdhouse extends Task { + @Inject + private Birdhouse plugin; + + private BirdhouseSpace space = null; + + @Override + public String getStatus() { + return "Emptying birdhouse"; + } + + @Override + public boolean validate() { + space = getNext(); + if (space == null) return false; + + return game.objects().withPosition(space.getPosition()).withAction("Empty").exists(); + } + + @Override + public void execute() { + iObject birdhouse = game.objects().withPosition(space.getPosition()).withAction("Empty").first(); + if (birdhouse == null) return; + + birdhouse.interact("Empty"); + if (!game.waitUntil(() -> !game.objects().withPosition(space.getPosition()).withAction("Empty").exists(), 15)) return; + + plugin.getBirdhouseTimers().put(space, Instant.now()); + space = null; + } + + private BirdhouseSpace getNext() { + for (BirdhouseSpace space : BirdhouseSpace.values()) { + int varp = game.client().getVar(space.getVarp()); + + if (BirdhouseState.fromVarpValue(varp) == BirdhouseState.SEEDED + && !plugin.hasRecentlyBeenEmptied(space)) { + return space; + } + } + + return null; + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GetTools.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GetTools.java new file mode 100644 index 0000000..3c9e30d --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GetTools.java @@ -0,0 +1,53 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.game.iWidget; + +public class GetTools extends Task { + @Override + public String getStatus() { + return "Getting farming tools"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 15008 + && !game.inventory().withId(ItemID.SEED_DIBBER).exists(); + } + + @Override + public void execute() { + iNPC leprechaun = game.npcs().withName("Tool Leprechaun").nearest(); + if (leprechaun == null) return; + + leprechaun.interact("Exchange"); + game.waitUntil(() -> game.widget(125, 0) != null, 30); + game.tick(); + + iWidget dibber = game.widget(125, 9); + if (dibber == null) return; + dibber.interact("Remove-1"); + game.tick(); + + iWidget spade = game.widget(125, 10); + if (spade == null) return; + spade.interact("Remove-1"); + game.tick(); + + for (int i = 0; i < 2; i++) { + iWidget compost = game.widget(125, 19); + if (compost == null) return; + compost.interact("Remove-1"); + game.sleepDelay(); + } + game.tick(); + + iWidget close = game.widget(125, 1, 11); + if (close == null) return; + close.interact("Close"); + + game.waitUntil(() -> game.widget(125, 0) == null, 5); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToBirdhouse.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToBirdhouse.java new file mode 100644 index 0000000..70e0780 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToBirdhouse.java @@ -0,0 +1,32 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.birdhouse.Birdhouse; +import io.reisub.openosrs.birdhouse.BirdhouseSpace; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +public class GoToBirdhouse extends Task { + @Inject + private Birdhouse plugin; + + private final Position target = new Position(3681, 3819, 0); + + @Override + public String getStatus() { + return "Going to southern birdhouse"; + } + + @Override + public boolean validate() { + return plugin.hasRecentlyBeenEmptied(BirdhouseSpace.MEADOW_NORTH) + && !plugin.hasRecentlyBeenEmptied(BirdhouseSpace.MEADOW_SOUTH) + && game.localPlayer().position().distanceTo(target) > 15; + } + + @Override + public void execute() { + walking.walkTo(target.areaWithin(1)); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToIsland.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToIsland.java new file mode 100644 index 0000000..a4f907b --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToIsland.java @@ -0,0 +1,56 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.birdhouse.Birdhouse; +import io.reisub.openosrs.birdhouse.BirdhouseSpace; +import io.reisub.openosrs.birdhouse.Config; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.scene.Position; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import javax.inject.Inject; + +public class GoToIsland extends Task { + @Inject + private Birdhouse plugin; + + @Inject + private Config config; + + private final Position target = new Position(3731, 3892, 0); + + @Override + public String getStatus() { + return "Going to northern island"; + } + + @Override + public boolean validate() { + for (BirdhouseSpace space : BirdhouseSpace.values()) { + if (!plugin.hasRecentlyBeenEmptied(space)) return false; + } + + if (!config.farmSeaweed()) return false; + + int region = game.localPlayer().position().regionID(); + + return region == 14651 || region == 14652; + } + + @Override + public void execute() { + walking.walkTo(target.areaWithin(1)); + + iObject rowboat = game.objects().withName("Rowboat").withAction("Travel").nearest(); + if (rowboat == null) return; + + while (game.localPlayer().position().distanceTo(plugin.getIslandPosition()) > 10) { + rowboat.interact("Travel"); + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT, 15); + + chatbox.chooseOption(3); + game.waitUntil(() -> game.localPlayer().position().distanceTo(plugin.getIslandPosition()) < 10, 5); + } + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToMushroomMeadow.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToMushroomMeadow.java new file mode 100644 index 0000000..d2f6490 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToMushroomMeadow.java @@ -0,0 +1,36 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.game.iWidget; + +public class GoToMushroomMeadow extends Task { + @Override + public String getStatus() { + return "Going to Mushroom Meadow"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 14906 + && game.inventory().withNamePart("logs").count() == 2; + } + + @Override + public void execute() { + iObject tree = game.objects().withName("Magic Mushtree").nearest(); + if (tree == null) return; + + tree.interact(0); + game.waitUntil(() -> game.widget(WidgetInfo.FOSSIL_MUSHROOM_TELEPORT) != null, 15); + game.tick(); + + iWidget teleportWidget = game.widget(WidgetInfo.FOSSIL_MUSHROOM_MEADOW); + if (teleportWidget == null) return; + teleportWidget.select(); + + game.waitUntil(() -> game.localPlayer().position().regionID() == 14652, 5); + game.tick(2); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToVerdantValley.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToVerdantValley.java new file mode 100644 index 0000000..54d16ba --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/GoToVerdantValley.java @@ -0,0 +1,36 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.game.iWidget; + +public class GoToVerdantValley extends Task { + @Override + public String getStatus() { + return "Going to Verdant Valley"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 14908 + && game.inventory().withNamePart("logs").count() == 4; + } + + @Override + public void execute() { + iObject tree = game.objects().withName("Magic Mushtree").nearest(); + if (tree == null) return; + + tree.interact(0); + game.waitUntil(() -> game.widget(WidgetInfo.FOSSIL_MUSHROOM_TELEPORT) != null, 15); + game.tick(); + + iWidget teleportWidget = game.widget(WidgetInfo.FOSSIL_MUSHROOM_VALLEY); + if (teleportWidget == null) return; + teleportWidget.select(); + + game.waitUntil(() -> game.localPlayer().position().regionID() == 14906, 5); + game.tick(2); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/HarvestSeaweed.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/HarvestSeaweed.java new file mode 100644 index 0000000..de45e43 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/HarvestSeaweed.java @@ -0,0 +1,42 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iObject; + +public class HarvestSeaweed extends Task { + @Override + public String getStatus() { + return "Harvesting seaweed"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 15008 + && game.inventory().withId(ItemID.SEED_DIBBER).exists() + && !game.inventory().full() + && (game.objects().withName("Dead seaweed").exists() || game.objects().withAction("Pick").withName("Seaweed").exists()); + } + + @Override + public void execute() { + iObject deadSeaweed = game.objects().withName("Dead seaweed").first(); + if (deadSeaweed != null) { + int count = (int) game.objects().withName("Dead seaweed").count(); + deadSeaweed.interact("Clear"); + + game.waitUntil(() -> game.objects().withName("Dead seaweed").count() < count, 30); + return; + } + + iObject seaweed = game.objects().withName("Seaweed").withAction("Pick").nearest(); + if (seaweed == null) return; + + int count = (int) game.objects().withName("Seaweed").withAction("Pick").count(); + seaweed.interact("Pick"); + game.waitUntil(() -> game.objects().withName("Seaweed").withAction("Pick").count() < count + || game.groundItems().withId(ItemID.SEAWEED_SPORE).exists() + || game.inventory().full(), 120); + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/NoteSeaweed.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/NoteSeaweed.java new file mode 100644 index 0000000..3d743fd --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/NoteSeaweed.java @@ -0,0 +1,30 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iNPC; + +public class NoteSeaweed extends Task { + @Override + public String getStatus() { + return "Noting seaweed"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 15008 + && game.inventory().withId(ItemID.GIANT_SEAWEED).exists(); + } + + @Override + public void execute() { + InventoryItem seaweed = game.inventory().withId(ItemID.GIANT_SEAWEED).first(); + iNPC leprechaun = game.npcs().withName("Tool Leprechaun").first(); + if (seaweed == null || leprechaun == null) return; + + seaweed.useOn(leprechaun); + game.waitUntil(() -> !game.inventory().withId(ItemID.GIANT_SEAWEED).exists(), 30); + game.tick(); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PickupSeed.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PickupSeed.java new file mode 100644 index 0000000..7ab707b --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PickupSeed.java @@ -0,0 +1,33 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iGroundItem; + +public class PickupSeed extends Task { + @Override + public String getStatus() { + return "Picking up seaweed spore"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 15008 + && game.groundItems().withId(ItemID.SEAWEED_SPORE).exists(); + } + + @Override + public void execute() { + iGroundItem item = game.groundItems().withId(ItemID.SEAWEED_SPORE).nearest(); + if (item == null) return; + + int quantity = game.inventory().withId(ItemID.SEAWEED_SPORE).quantity(); + + do { + item.interact("Take"); + game.sleepDelay(); + } while (!game.localPlayer().isMoving()); + + game.waitUntil(() -> game.inventory().withId(ItemID.SEAWEED_SPORE).quantity() > quantity, 30); + } +} diff --git a/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PlantSeaweed.java b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PlantSeaweed.java new file mode 100644 index 0000000..4c1b344 --- /dev/null +++ b/birdhouse/src/main/java/io/reisub/openosrs/birdhouse/tasks/PlantSeaweed.java @@ -0,0 +1,45 @@ +package io.reisub.openosrs.birdhouse.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iObject; + +public class PlantSeaweed extends Task { + @Override + public String getStatus() { + return "Planting seaweed"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 15008 + && game.objects().withName("Seaweed patch").exists(); + } + + @Override + public void execute() { + iObject patch = game.objects().withName("Seaweed patch").nearest(); + if (patch == null) return; + + InventoryItem seed = game.inventory().withId(ItemID.SEAWEED_SPORE).first(); + if (seed == null) return; + + int quantity = seed.quantity(); + + seed.useOn(patch); + game.waitUntil(() -> game.inventory().withId(ItemID.SEAWEED_SPORE).first() == null + || game.inventory().withId(ItemID.SEAWEED_SPORE).first().quantity() < quantity, 10); + game.tick(3); + + + InventoryItem ultraCompost = game.inventory().withId(ItemID.ULTRACOMPOST).first(); + if (ultraCompost == null) return; + + patch = game.objects().withName("Seaweed").nearest(); + if (patch == null) return; + + ultraCompost.useOn(patch); + game.tick(3); + } +} diff --git a/blackjack/blackjack.gradle.kts b/blackjack/blackjack.gradle.kts new file mode 100644 index 0000000..540dd59 --- /dev/null +++ b/blackjack/blackjack.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Blackjack" +project.extra["PluginDescription"] = "Blackjacks like a black Jack" + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackConfig.java b/blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackConfig.java new file mode 100644 index 0000000..f1789b9 --- /dev/null +++ b/blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackConfig.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.blackjack; + +import net.runelite.client.config.Button; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosBlackjackConfig") + +public interface BlackjackConfig extends Config { + @ConfigItem( + keyName = "target", + name = "Target", + description = "Select blackjack target", + position = 10 + ) + default Target target() { + return Target.THUG; + } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackPlugin.java b/blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackPlugin.java new file mode 100644 index 0000000..ad36cf3 --- /dev/null +++ b/blackjack/src/main/java/io/reisub/openosrs/blackjack/BlackjackPlugin.java @@ -0,0 +1,150 @@ +package io.reisub.openosrs.blackjack; + +import com.google.inject.Provides; +import io.reisub.openosrs.blackjack.tasks.Blackjack; +import io.reisub.openosrs.blackjack.tasks.Escape; +import io.reisub.openosrs.blackjack.tasks.Pickpocket; +import io.reisub.openosrs.util.Calculations; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Eat; +import io.reisub.openosrs.util.tasks.KittenTask; +import io.reisub.openosrs.util.tasks.Run; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Blackjack", + description = "", + enabledByDefault = false +) +@Slf4j +public class BlackjackPlugin extends iScript { + private final String BLACKJACK_SUCCESS = "You smack the bandit over the head and render them unconscious."; + private final String BLACKJACK_FAIL = "Your blow only glances off the bandit's head."; + private final String PICKPOCKET = "You pick the bandit's pocket."; + + private List tasks; + private KittenTask kittenTask; + private Blackjack blackjackTask; + private Pickpocket pickpocketTask; + private Escape escapeTask; + private long lastAction = 0; + + @Provides + BlackjackConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(BlackjackConfig.class); + } + + @Inject + private Calculations calc; + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + lastAction = System.currentTimeMillis(); + log.info(t.getStatus()); + t.execute(); + break; + } + } + } + + @Override + protected void onStart() { + log.info("Starting Chaos Blackjack"); + + Eat eatTask = injector.getInstance(Eat.class); + eatTask.setWait(false); + eatTask.setInterval(14, 24); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + kittenTask = KittenTask.getInstance(injector); + pickpocketTask = injector.getInstance(Pickpocket.class); + blackjackTask = injector.getInstance(Blackjack.class); + escapeTask = injector.getInstance(Escape.class); + + tasks = new ArrayList<>(); + tasks.add(kittenTask); + tasks.add(escapeTask); + tasks.add(pickpocketTask); + tasks.add(blackjackTask); + tasks.add(eatTask); + tasks.add(runTask); + + blackjackTask.setReady(true); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Blackjack"); + if (tasks != null) { + tasks.clear(); + } + + KittenTask.handleKitten = false; + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (kittenTask != null) { + kittenTask.onChatMessage(chatMessage); + } + + if (pickpocketTask != null && blackjackTask != null) { + String msg = chatMessage.getMessage(); + if (msg.contains(BLACKJACK_SUCCESS) || msg.contains(BLACKJACK_FAIL)) { + blackjackTask.setNextKnockoutTick(game.client().getTickCount() + calc.random(3, 5)); + pickpocketTask.setReady(true); + } + + if (msg.contains(BLACKJACK_FAIL)) { + blackjackTask.setReady(true); + } + + if (msg.contains(PICKPOCKET)) { + if (pickpocketTask.getSuccessivePickpockets() < 2) { + pickpocketTask.setReady(true); + } else { + pickpocketTask.setSuccessivePickpockets(0); + blackjackTask.setReady(true); + } + } + } + } + + @Subscribe + private void onGameTick(GameTick event) { + if (System.currentTimeMillis() > lastAction + calc.random(5000, 6000)) { + log.info("idle, blackjacking"); + pickpocketTask.setSuccessivePickpockets(0); + blackjackTask.setReady(true); + } + } +} \ No newline at end of file diff --git a/blackjack/src/main/java/io/reisub/openosrs/blackjack/Target.java b/blackjack/src/main/java/io/reisub/openosrs/blackjack/Target.java new file mode 100644 index 0000000..e8b4b87 --- /dev/null +++ b/blackjack/src/main/java/io/reisub/openosrs/blackjack/Target.java @@ -0,0 +1,17 @@ +package io.reisub.openosrs.blackjack; + +import lombok.Getter; +import net.runelite.api.NpcID; + +public enum Target { + BANDIT_41(NpcID.BANDIT_737), + BANDIT_56(NpcID.BANDIT_735), + THUG(NpcID.MENAPHITE_THUG); + + @Getter + private final int id; + + Target(int id) { + this.id = id; + } +} diff --git a/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Blackjack.java b/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Blackjack.java new file mode 100644 index 0000000..6da97e1 --- /dev/null +++ b/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Blackjack.java @@ -0,0 +1,43 @@ +package io.reisub.openosrs.blackjack.tasks; + +import io.reisub.openosrs.blackjack.BlackjackConfig; +import io.reisub.openosrs.util.Task; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.plugins.iutils.game.iNPC; + +import javax.inject.Inject; + +public class Blackjack extends Task { + @Inject + private BlackjackConfig config; + + @Setter @Getter + private volatile boolean ready; + + @Setter @Getter + private volatile long nextKnockoutTick = 0; + + @Override + public String getStatus() { + return "Blackjacking"; + } + + @Override + public boolean validate() { + return isReady() + && game.client().getTickCount() >= nextKnockoutTick + && game.client().getLocalPlayer().getModelHeight() != 1000 + && game.inventory().withAction("Eat", "Drink").exists(); + } + + @Override + public void execute() { + iNPC target = game.npcs().withId(config.target().getId()).nearest(); + if (target == null) return; + + target.interact("Knock-Out"); + ready = false; + game.tick(); + } +} diff --git a/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Escape.java b/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Escape.java new file mode 100644 index 0000000..8b327e4 --- /dev/null +++ b/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Escape.java @@ -0,0 +1,20 @@ +package io.reisub.openosrs.blackjack.tasks; + +import io.reisub.openosrs.util.Task; + +public class Escape extends Task { + @Override + public String getStatus() { + return "Escaping from combat"; + } + + @Override + public boolean validate() { + return false; + } + + @Override + public void execute() { + + } +} diff --git a/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Pickpocket.java b/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Pickpocket.java new file mode 100644 index 0000000..8ed27c5 --- /dev/null +++ b/blackjack/src/main/java/io/reisub/openosrs/blackjack/tasks/Pickpocket.java @@ -0,0 +1,46 @@ +package io.reisub.openosrs.blackjack.tasks; + +import io.reisub.openosrs.blackjack.BlackjackConfig; +import io.reisub.openosrs.util.Task; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.plugins.iutils.game.iNPC; + +import javax.inject.Inject; + +public class Pickpocket extends Task { + @Inject + private BlackjackConfig config; + + @Setter + private volatile boolean ready; + + @Getter + @Setter + private volatile int successivePickpockets = 0; + + @Override + public String getStatus() { + return "Pickpocketing"; + } + + @Override + public boolean validate() { + return ready && game.inventory().withAction("Eat", "Drink").exists(); + } + + @Override + public void execute() { + iNPC target = game.npcs().withId(config.target().getId()).nearest(); + if (target == null) return; + + if (getSuccessivePickpockets() == 0) { + game.sleepDelay(); + } + + target.interact("Pickpocket"); + setSuccessivePickpockets(getSuccessivePickpockets() + 1); + ready = false; + game.tick(); + } +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..3c7c3c6 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,128 @@ +import ProjectVersions.openosrsVersion + +buildscript { + repositories { + gradlePluginPortal() + } +} + +plugins { + java + checkstyle +} + +project.extra["GithubUrl"] = "https://github.com/yuri-moens/chaos-plugins" + +apply() + +allprojects { + group = "io.reisub" + apply() +} + +allprojects { + apply() + + repositories { + mavenLocal() + } +} + +subprojects { + group = "io.reisub.openosrs" + + project.extra["PluginProvider"] = "ChaosEnergy" + project.extra["ProjectSupportUrl"] = "https://github.com/yuri-moens/chaos-plugins/issues" + project.extra["PluginLicense"] = "3-Clause BSD License" + + repositories { + mavenCentral { + content { + excludeGroupByRegex("com\\.openosrs.*") + } + } + + jcenter { + content { + excludeGroupByRegex("com\\.openosrs.*") + } + } + + exclusiveContent { + forRepository { + mavenLocal() + } + filter { + includeGroupByRegex("com\\.openosrs.*") + } + } + } + + apply() + + dependencies { + annotationProcessor(Libraries.lombok) + annotationProcessor(Libraries.pf4j) + + compileOnly("com.openosrs:http-api:$openosrsVersion+") + compileOnly("com.openosrs:runelite-api:$openosrsVersion+") + compileOnly("com.openosrs:runelite-client:$openosrsVersion+") + compileOnly("com.openosrs.rs:runescape-api:$openosrsVersion+") + compileOnly("com.openosrs.externals:iutils:4+") + + compileOnly(Libraries.findbugs) + compileOnly(Libraries.apacheCommonsText) + compileOnly(Libraries.gson) + compileOnly(Libraries.guice) + compileOnly(Libraries.javax) + compileOnly(Libraries.lombok) + compileOnly(Libraries.okhttp3) + compileOnly(Libraries.pf4j) + compileOnly(Libraries.rxjava) + } + + configure { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + configure { + repositories { + maven { + url = uri("$buildDir/repo") + } + } + publications { + register("mavenJava", MavenPublication::class) { + from(components["java"]) + } + } + } + + tasks { + withType { + options.encoding = "UTF-8" + } + + withType { + doLast { + copy { + from("./build/libs/") + into("../release/") + } + } + } + + withType { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + dirMode = 493 + fileMode = 420 + } + + register("copyDeps") { + into("./build/deps/") + from(configurations["runtimeClasspath"]) + } + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..8024c53 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +plugins { + `kotlin-dsl` +} + +repositories { + jcenter() +} + +dependencies { + implementation(gradleApi()) + implementation(group = "org.json", name = "json", version = "20190722") + implementation(group = "com.savvasdalkitsis", name = "json-merge", version = "0.0.4") + implementation(group = "com.squareup.okhttp3", name = "okhttp", version = "4.2.2") +} + +kotlinDslPluginOptions { + experimentalWarning.set(false) +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/BootstrapPlugin.kt b/buildSrc/src/main/kotlin/BootstrapPlugin.kt new file mode 100644 index 0000000..c65baac --- /dev/null +++ b/buildSrc/src/main/kotlin/BootstrapPlugin.kt @@ -0,0 +1,9 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.register + +class BootstrapPlugin : Plugin { + override fun apply(project: Project) { + project.tasks.register("bootstrapPlugins") {} + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/BootstrapTask.kt b/buildSrc/src/main/kotlin/BootstrapTask.kt new file mode 100644 index 0000000..9700f90 --- /dev/null +++ b/buildSrc/src/main/kotlin/BootstrapTask.kt @@ -0,0 +1,97 @@ +import com.savvasdalkitsis.jsonmerger.JsonMerger +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.extra +import org.gradle.kotlin.dsl.get +import org.json.JSONArray +import org.json.JSONObject +import java.io.File +import java.nio.file.Paths +import java.security.MessageDigest +import java.text.SimpleDateFormat +import java.util.* + +open class BootstrapTask : DefaultTask() { + + private fun formatDate(date: Date?) = with(date ?: Date()) { + SimpleDateFormat("yyyy-MM-dd").format(this) + } + + private fun hash(file: ByteArray): String { + return MessageDigest.getInstance("SHA-512").digest(file).fold("", { str, it -> str + "%02x".format(it) }).toUpperCase() + } + + private fun getBootstrap(filename: String): JSONArray? { + val bootstrapFile = File(filename).readLines() + + return JSONObject("{\"plugins\":$bootstrapFile}").getJSONArray("plugins") + } + + @TaskAction + fun boostrap() { + if (project == project.rootProject) { + val bootstrapDir = File("${project.projectDir}") + val bootstrapReleaseDir = File("${project.projectDir}/release") + + bootstrapReleaseDir.mkdirs() + + val plugins = ArrayList() + val baseBootstrap = getBootstrap("$bootstrapDir/plugins.json") ?: throw RuntimeException("Base bootstrap is null!") + + project.subprojects.forEach { + if (it.project.properties.containsKey("PluginName") && it.project.properties.containsKey("PluginDescription")) { + var pluginAdded = false + val plugin = it.project.tasks["jar"].outputs.files.singleFile + + val releases = ArrayList() + + releases.add(JsonBuilder( + "version" to it.project.version, + "requires" to ProjectVersions.apiVersion, + "date" to formatDate(Date()), + "url" to "${project.rootProject.extra.get("GithubUrl")}/blob/master/release/${it.project.name}-${it.project.version}.jar?raw=true", + "sha512sum" to hash(plugin.readBytes()) + )) + + val pluginObject = JsonBuilder( + "name" to it.project.extra.get("PluginName"), + "id" to nameToId(it.project.extra.get("PluginName") as String), + "description" to it.project.extra.get("PluginDescription"), + "provider" to it.project.extra.get("PluginProvider"), + "projectUrl" to it.project.extra.get("ProjectSupportUrl"), + "releases" to releases.toTypedArray() + ).jsonObject() + + for (i in 0 until baseBootstrap.length()) { + val item = baseBootstrap.getJSONObject(i) + + if (item.get("id") != nameToId(it.project.extra.get("PluginName") as String)) { + continue + } + + if (it.project.version.toString() in item.getJSONArray("releases").toString()) { + pluginAdded = true + plugins.add(item) + break + } + + plugins.add(JsonMerger(arrayMergeMode = JsonMerger.ArrayMergeMode.MERGE_ARRAY).merge(item, pluginObject)) + pluginAdded = true + } + + if (!pluginAdded) + { + plugins.add(pluginObject) + } + + plugin.copyTo(Paths.get(bootstrapReleaseDir.toString(), "${it.project.name}-${it.project.version}.jar").toFile()) + } + } + + File(bootstrapDir, "plugins.json").printWriter().use { out -> + out.println(plugins.toString()) + } + } + + } +} diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt new file mode 100644 index 0000000..0ab4a8e --- /dev/null +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +object ProjectVersions { + const val openosrsVersion = "4.17.0" + const val apiVersion = "^1.0.0" +} + +object Libraries { + private object Versions { + const val apacheCommonsText = "1.9" + const val gson = "2.8.8" + const val guice = "5.0.1" + const val javax = "1.3.2" + const val lombok = "1.18.22" + const val okhttp3 = "4.9.0" + const val pf4j = "3.6.0" + const val findbugs = "3.0.1" + const val rxjava = "3.1.3" + const val slf4j = "2.0.0-alpha5" + } + + const val apacheCommonsText = "org.apache.commons:commons-text:${Versions.apacheCommonsText}" + const val gson = "com.google.code.gson:gson:${Versions.gson}" + const val guice = "com.google.inject:guice:${Versions.guice}" + const val javax = "javax.annotation:javax.annotation-api:${Versions.javax}" + const val lombok = "org.projectlombok:lombok:${Versions.lombok}" + const val okhttp3 = "com.squareup.okhttp3:okhttp:${Versions.okhttp3}" + const val pf4j = "org.pf4j:pf4j:${Versions.pf4j}" + const val findbugs = "com.google.code.findbugs:jsr305:${Versions.findbugs}" + const val rxjava = "io.reactivex.rxjava3:rxjava:${Versions.rxjava}" + const val slf4j = "org.slf4j:slf4j-api:${Versions.slf4j}" + +} diff --git a/buildSrc/src/main/kotlin/JsonBuilder.kt b/buildSrc/src/main/kotlin/JsonBuilder.kt new file mode 100644 index 0000000..3e704c3 --- /dev/null +++ b/buildSrc/src/main/kotlin/JsonBuilder.kt @@ -0,0 +1,57 @@ +import org.json.JSONArray +import org.json.JSONObject + +class JsonBuilder() { + private var json = JSONObject() + + constructor(vararg pairs: Pair) : this() { + add(*pairs) + } + + fun add(vararg pairs: Pair) { + for ((key, value) in pairs) { + when (value) { + is Boolean -> json.put(key, value) + is Number -> add(key, value) + is String -> json.put(key, value) + is JsonBuilder -> json.put(key, value.json) + is Array<*> -> add(key, value) + is JSONObject -> json.put(key, value) + is JSONArray -> json.put(key, value) + } + } + } + + fun add(key: String, value: Number): JsonBuilder { + when (value) { + is Int -> json.put(key, value) + is Long -> json.put(key, value) + is Float -> json.put(key, value) + is Double -> json.put(key, value) + else -> {} + } + + return this + } + + fun add(key: String, items: Array): JsonBuilder { + val jsonArray = JSONArray() + items.forEach { + when (it) { + is String,is Long,is Int, is Boolean -> jsonArray.put(it) + is JsonBuilder -> jsonArray.put(it.json) + else -> try {jsonArray.put(it)} catch (ignored:Exception) { + + } + } + } + + json.put(key, jsonArray) + + return this + } + + fun jsonObject() = json + + override fun toString() = json.toString() +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Project.kt b/buildSrc/src/main/kotlin/Project.kt new file mode 100644 index 0000000..0a7ed01 --- /dev/null +++ b/buildSrc/src/main/kotlin/Project.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +fun nameToId(name: String): String { + return name.replace("[^A-Za-z]".toRegex(), "").toLowerCase() + "-plugin" +} diff --git a/consume/consume.gradle.kts b/consume/consume.gradle.kts new file mode 100644 index 0000000..e659754 --- /dev/null +++ b/consume/consume.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Consume" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Eats, drinks, activates items/specials" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/consume/src/main/java/io/reisub/openosrs/consume/Config.java b/consume/src/main/java/io/reisub/openosrs/consume/Config.java new file mode 100644 index 0000000..539e8a4 --- /dev/null +++ b/consume/src/main/java/io/reisub/openosrs/consume/Config.java @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.consume; + +import net.runelite.client.config.*; + +@ConfigGroup("ChaosConsumeConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigSection( + keyName = "eatConfig", + name = "Eating Configuration", + description = "Configure when to eat", + position = 1 + ) + String eatConfig = "eatConfig"; + + @ConfigItem( + keyName = "enableEating", + name = "Enable eating", + description = "Enable automatic eating based on the minimum and maximum values set below.", + section = "eatConfig", + position = 2 + ) + default boolean enableEating() { + return true; + } + + @ConfigItem( + keyName = "minEatHP", + name = "Minimum Eat HP", + description = "Minimum HP to eat at.", + section = "eatConfig", + position = 3 + ) + default int minEatHP() { + return 10; + } + + @ConfigItem( + keyName = "maxEatHP", + name = "Maximum Eat HP", + description = "Highest HP to potentially eat at.", + section = "eatConfig", + position = 4 + ) + default int maxEatHP() { + return 20; + } + + @ConfigItem( + keyName = "comboEat", + name = "Combo eat", + description = "Combo karambwan with pot and/or other food.", + section = "eatConfig", + position = 6 + ) + default boolean comboEat() { + return true; + } + + @ConfigItem( + keyName = "eatWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any food.", + section = "eatConfig", + hidden = true, + unhide = "enableEating", + position = 7 + ) + default boolean eatWarnings() { + return true; + } + + @ConfigSection( + keyName = "potsConfig", + name = "Potions Configuration", + description = "Configure when to pot", + position = 10 + ) + String potsConfig = "potsConfig"; + + @ConfigItem( + keyName = "drinkAntiPoison", + name = "Drink anti-poison", + description = "Drink an anti-poison potion when poisoned.", + section = "potsConfig", + position = 11 + ) + default boolean drinkAntiPoison() { + return true; + } + + @ConfigItem( + keyName = "antiPoisonWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any anti-poison.", + section = "potsConfig", + hidden = true, + unhide = "drinkAntiPoison", + position = 12 + ) + default boolean antiPoisonWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkAntiFire", + name = "Drink anti-fire", + description = "Drink an anti-fire potion when not under its effect or its effect is running out.", + section = "potsConfig", + position = 13 + ) + default boolean drinkAntiFire() { + return false; + } + + @ConfigItem( + keyName = "antiFireWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any anti-fire potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkAntiFire", + position = 14 + ) + default boolean antiFireWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkPrayer", + name = "Drink prayer potions", + description = "Drink a prayer potion when prayer points fall below a threshold.", + section = "potsConfig", + position = 15 + ) + default boolean drinkPrayer() { + return false; + } + + @ConfigItem( + keyName = "minPrayerPoints", + name = "Minimum prayer points", + description = "Minimum prayer points to drink at.", + section = "potsConfig", + hidden = true, + unhide = "drinkPrayer", + position = 16 + ) + default int minPrayerPoints() { + return 10; + } + + @ConfigItem( + keyName = "maxPrayerPoints", + name = "Maximum prayer points", + description = "Highest prayer points to potentially drink at", + section = "potsConfig", + hidden = true, + unhide = "drinkPrayer", + position = 17 + ) + default int maxPrayerPoints() { + return 20; + } + + @ConfigItem( + keyName = "prayerWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any prayer potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkPrayer", + position = 18 + ) + default boolean prayerWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkStrength", + name = "Drink strength pots", + description = "Enable to drink pots to boost strength.", + section = "potsConfig", + position = 19 + ) + default boolean drinkStrength() { + return false; + } + + @ConfigItem( + keyName = "strengthLevel", + name = "Strength level", + description = "Drink strength boosting pot below this level.", + section = "potsConfig", + position = 20, + hidden = true, + unhide = "drinkStrength" + ) + default int strengthLevel() { + return 100; + } + + @ConfigItem( + keyName = "strengthWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any strength potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkStrength", + position = 21 + ) + default boolean strengthWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkAttack", + name = "Drink attack pots", + description = "Enable to drink pots to boost attack.", + section = "potsConfig", + position = 22 + ) + default boolean drinkAttack() { + return false; + } + + @ConfigItem( + keyName = "attackLevel", + name = "Attack level", + description = "Drink attack boosting pot below this level.", + section = "potsConfig", + position = 23, + hidden = true, + unhide = "drinkAttack" + ) + default int attackLevel() { + return 100; + } + + @ConfigItem( + keyName = "attackWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any attack potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkAttack", + position = 24 + ) + default boolean attackWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkDefence", + name = "Drink defence pots", + description = "Enable to drink pots to boost defence.", + section = "potsConfig", + position = 25 + ) + default boolean drinkDefence() { + return false; + } + + @ConfigItem( + keyName = "defenceLevel", + name = "Defence level", + description = "Drink defence boosting pot below this level.", + section = "potsConfig", + position = 26, + hidden = true, + unhide = "drinkDefence" + ) + default int defenceLevel() { + return 100; + } + + @ConfigItem( + keyName = "defenceWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any defence potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkDefence", + position = 27 + ) + default boolean defenceWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkRanged", + name = "Drink ranged pots", + description = "Enable to drink pots to boost ranged.", + section = "potsConfig", + position = 28 + ) + default boolean drinkRanged() { + return false; + } + + @ConfigItem( + keyName = "rangedLevel", + name = "Ranged level", + description = "Drink ranged boosting pot below this level.", + section = "potsConfig", + position = 29, + hidden = true, + unhide = "drinkRanged" + ) + default int rangedLevel() { + return 100; + } + + @ConfigItem( + keyName = "rangedWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any ranged potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkRanged", + position = 30 + ) + default boolean rangedWarnings() { + return true; + } + + @ConfigItem( + keyName = "drinkMagic", + name = "Drink magic pots", + description = "Enable to drink pots to boost magic.", + section = "potsConfig", + position = 31 + ) + default boolean drinkMagic() { + return false; + } + + @ConfigItem( + keyName = "magicLevel", + name = "Magic level", + description = "Drink magic boosting pot below this level.", + section = "potsConfig", + position = 32, + hidden = true, + unhide = "drinkMagic" + ) + default int magicLevel() { + return 100; + } + + @ConfigItem( + keyName = "magicWarnings", + name = "Warnings", + description = "Enable warning messages in chat if you don't have any magic potions.", + section = "potsConfig", + hidden = true, + unhide = "drinkMagic", + position = 33 + ) + default boolean magicWarnings() { + return true; + } +} \ No newline at end of file diff --git a/consume/src/main/java/io/reisub/openosrs/consume/Consume.java b/consume/src/main/java/io/reisub/openosrs/consume/Consume.java new file mode 100644 index 0000000..7bd6342 --- /dev/null +++ b/consume/src/main/java/io/reisub/openosrs/consume/Consume.java @@ -0,0 +1,386 @@ +package io.reisub.openosrs.consume; + +import com.google.inject.Provides; +import io.reisub.openosrs.util.Calculations; +import io.reisub.openosrs.util.Util; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameState; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.api.VarPlayer; +import net.runelite.api.events.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.game.Game; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.Set; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Consume", + description = "Eats, drinks, activates items/specials", + enabledByDefault = false +) +@Slf4j +public class Consume extends Plugin { + @Inject + @SuppressWarnings("unused") + private Game game; + + @Inject + @SuppressWarnings("unused") + private Config config; + + @Inject + @SuppressWarnings("unused") + private Calculations calc; + + private final Set IGNORE_FOOD = Set.of(ItemID.DWARVEN_ROCK_CAKE, ItemID.DWARVEN_ROCK_CAKE_7510); + private final Set DRINK_SET = Set.of(ItemID.JUG_OF_WINE, ItemID.SARADOMIN_BREW1, ItemID.SARADOMIN_BREW2, ItemID.SARADOMIN_BREW3, ItemID.SARADOMIN_BREW4, ItemID.XERICS_AID_1, ItemID.XERICS_AID_2, ItemID.XERICS_AID_3, ItemID.XERICS_AID_4, ItemID.XERICS_AID_1_20977, ItemID.XERICS_AID_2_20978, ItemID.XERICS_AID_3_20979, ItemID.XERICS_AID_4_20980, ItemID.XERICS_AID_1_20981, ItemID.XERICS_AID_2_20982, ItemID.XERICS_AID_3_20983, ItemID.XERICS_AID_4_20984, ItemID.BANDAGES); + private final Set ANTI_POISON_IDS = Set.of(ItemID.ANTIPOISON1, ItemID.ANTIPOISON2, ItemID.ANTIPOISON3, ItemID.ANTIPOISON4, ItemID.SUPERANTIPOISON1, ItemID.SUPERANTIPOISON2, ItemID.SUPERANTIPOISON3, ItemID.SUPERANTIPOISON4, + ItemID.ANTIDOTE1, ItemID.ANTIDOTE2, ItemID.ANTIDOTE3, ItemID.ANTIDOTE4, ItemID.ANTIDOTE1_5958, ItemID.ANTIDOTE2_5956, ItemID.ANTIDOTE3_5954, ItemID.ANTIDOTE4_5952, + ItemID.ANTIVENOM1, ItemID.ANTIVENOM2, ItemID.ANTIVENOM3, ItemID.ANTIVENOM4, ItemID.ANTIVENOM4_12913, ItemID.ANTIVENOM3_12915, ItemID.ANTIVENOM2_12917, ItemID.ANTIVENOM1_12919); + private final Set ANTI_FIRE_IDS = Set.of(ItemID.ANTIFIRE_POTION1, ItemID.ANTIFIRE_POTION2, ItemID.ANTIFIRE_POTION3, ItemID.ANTIFIRE_POTION4, ItemID.SUPER_ANTIFIRE_POTION1, ItemID.SUPER_ANTIFIRE_POTION2, ItemID.SUPER_ANTIFIRE_POTION3, ItemID.SUPER_ANTIFIRE_POTION4, + ItemID.EXTENDED_ANTIFIRE1, ItemID.EXTENDED_ANTIFIRE2, ItemID.EXTENDED_ANTIFIRE3, ItemID.EXTENDED_ANTIFIRE4, ItemID.EXTENDED_SUPER_ANTIFIRE1, ItemID.EXTENDED_SUPER_ANTIFIRE2, ItemID.EXTENDED_SUPER_ANTIFIRE3, ItemID.EXTENDED_SUPER_ANTIFIRE4); + private final Set PRAYER_IDS = Set.of(ItemID.PRAYER_POTION1, ItemID.PRAYER_POTION2, ItemID.PRAYER_POTION3, ItemID.PRAYER_POTION4, + ItemID.SUPER_RESTORE1, ItemID.SUPER_RESTORE2, ItemID.SUPER_RESTORE3, ItemID.SUPER_RESTORE4, ItemID.BLIGHTED_SUPER_RESTORE1, + ItemID.BLIGHTED_SUPER_RESTORE2, ItemID.BLIGHTED_SUPER_RESTORE3, ItemID.BLIGHTED_SUPER_RESTORE4, ItemID.EGNIOL_POTION_1, + ItemID.EGNIOL_POTION_2, ItemID.EGNIOL_POTION_3, ItemID.EGNIOL_POTION_4); + private final Set STRENGTH_IDS = Set.of(ItemID.STRENGTH_POTION1, ItemID.STRENGTH_POTION2, ItemID.STRENGTH_POTION3, ItemID.STRENGTH_POTION4, + ItemID.SUPER_STRENGTH1, ItemID.SUPER_STRENGTH2, ItemID.SUPER_STRENGTH3, ItemID.SUPER_STRENGTH4, + ItemID.DIVINE_SUPER_STRENGTH_POTION1, ItemID.DIVINE_SUPER_STRENGTH_POTION2, ItemID.DIVINE_SUPER_STRENGTH_POTION3, ItemID.DIVINE_SUPER_STRENGTH_POTION4, + ItemID.DIVINE_SUPER_COMBAT_POTION1, ItemID.DIVINE_SUPER_COMBAT_POTION2, ItemID.DIVINE_SUPER_COMBAT_POTION3, ItemID.DIVINE_SUPER_COMBAT_POTION4, + ItemID.COMBAT_POTION1, ItemID.COMBAT_POTION2, ItemID.COMBAT_POTION3, ItemID.COMBAT_POTION4, + ItemID.SUPER_COMBAT_POTION1, ItemID.SUPER_COMBAT_POTION2, ItemID.SUPER_COMBAT_POTION3, ItemID.SUPER_COMBAT_POTION4); + private final Set ATTACK_IDS = Set.of(ItemID.ATTACK_POTION1, ItemID.ATTACK_POTION2, ItemID.ATTACK_POTION3, ItemID.ATTACK_POTION4, + ItemID.SUPER_ATTACK1, ItemID.SUPER_ATTACK2, ItemID.SUPER_ATTACK3, ItemID.SUPER_ATTACK4, + ItemID.DIVINE_SUPER_ATTACK_POTION1, ItemID.DIVINE_SUPER_ATTACK_POTION2, ItemID.DIVINE_SUPER_ATTACK_POTION3, ItemID.DIVINE_SUPER_ATTACK_POTION4, + ItemID.DIVINE_SUPER_COMBAT_POTION1, ItemID.DIVINE_SUPER_COMBAT_POTION2, ItemID.DIVINE_SUPER_COMBAT_POTION3, ItemID.DIVINE_SUPER_COMBAT_POTION4, + ItemID.COMBAT_POTION1, ItemID.COMBAT_POTION2, ItemID.COMBAT_POTION3, ItemID.COMBAT_POTION4, + ItemID.SUPER_COMBAT_POTION1, ItemID.SUPER_COMBAT_POTION2, ItemID.SUPER_COMBAT_POTION3, ItemID.SUPER_COMBAT_POTION4); + private final Set DEFENCE_IDS = Set.of(ItemID.DEFENCE_POTION1, ItemID.DEFENCE_POTION2, ItemID.DEFENCE_POTION3, ItemID.DEFENCE_POTION4, + ItemID.SUPER_DEFENCE1, ItemID.SUPER_DEFENCE2, ItemID.SUPER_DEFENCE3, ItemID.SUPER_DEFENCE4, + ItemID.DIVINE_SUPER_DEFENCE_POTION1, ItemID.DIVINE_SUPER_DEFENCE_POTION2, ItemID.DIVINE_SUPER_DEFENCE_POTION3, ItemID.DIVINE_SUPER_DEFENCE_POTION4, + ItemID.DIVINE_SUPER_COMBAT_POTION1, ItemID.DIVINE_SUPER_COMBAT_POTION2, ItemID.DIVINE_SUPER_COMBAT_POTION3, ItemID.DIVINE_SUPER_COMBAT_POTION4, + ItemID.SUPER_COMBAT_POTION1, ItemID.SUPER_COMBAT_POTION2, ItemID.SUPER_COMBAT_POTION3, ItemID.SUPER_COMBAT_POTION4); + private final Set RANGED_IDS = Set.of(ItemID.RANGING_POTION1, ItemID.RANGING_POTION2, ItemID.RANGING_POTION3, ItemID.RANGING_POTION4, + ItemID.BASTION_POTION1, ItemID.BASTION_POTION2, ItemID.BASTION_POTION3, ItemID.BASTION_POTION4, + ItemID.DIVINE_RANGING_POTION1, ItemID.DIVINE_RANGING_POTION2, ItemID.DIVINE_RANGING_POTION3, ItemID.DIVINE_RANGING_POTION4, + ItemID.DIVINE_BASTION_POTION1, ItemID.DIVINE_BASTION_POTION2, ItemID.DIVINE_BASTION_POTION3, ItemID.DIVINE_BASTION_POTION4, + ItemID.SUPER_RANGING_1, ItemID.SUPER_RANGING_2, ItemID.SUPER_RANGING_3, ItemID.SUPER_RANGING_4); + private final Set MAGIC_IDS = Set.of(ItemID.MAGIC_POTION1, ItemID.MAGIC_POTION2, ItemID.MAGIC_POTION3, ItemID.MAGIC_POTION4, + ItemID.BATTLEMAGE_POTION1, ItemID.BATTLEMAGE_POTION2, ItemID.BATTLEMAGE_POTION3, ItemID.BATTLEMAGE_POTION4, + ItemID.DIVINE_MAGIC_POTION1, ItemID.DIVINE_MAGIC_POTION2, ItemID.DIVINE_MAGIC_POTION3, ItemID.DIVINE_MAGIC_POTION4, + ItemID.DIVINE_BATTLEMAGE_POTION1, ItemID.DIVINE_BATTLEMAGE_POTION2, ItemID.DIVINE_BATTLEMAGE_POTION3, ItemID.DIVINE_BATTLEMAGE_POTION4); + + private long lastAte; + private long lastPot; + private int timeout; + private int eatThreshold; + private int prayerThreshold; + private boolean shouldDrinkAntiPoison; + private long lastAntiPoison; + private boolean shouldDrinkAntiFire; + private long lastAntiFire; + private boolean shouldDrinkPrayer; + private long lastPrayer; + private boolean shouldDrinkStrength; + private long lastStrength; + private boolean shouldDrinkAttack; + private long lastAttack; + private boolean shouldDrinkDefence; + private long lastDefence; + private boolean shouldDrinkRanged; + private long lastRanged; + private boolean shouldDrinkMagic; + private long lastMagic; + + @Provides + @SuppressWarnings("unused") + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void startUp() { + log.info("Starting Chaos Consume"); + } + + @Override + protected void shutDown() { + log.info("Stopping Chaos Consume"); + } + + @Subscribe + @SuppressWarnings("unused") + private void onGameTick(GameTick event) { + if (game.client() == null || game.localPlayer() == null || game.client().getGameState() != GameState.LOGGED_IN) return; + + if (timeout > 0) { + timeout--; + return; + } + + if (eatThreshold == 0) generateNewEatThreshold(); + if (prayerThreshold == 0) generateNewPrayerThreshold(); + + int hp = game.modifiedLevel(Skill.HITPOINTS); + + if (config.enableEating() && hp <= eatThreshold && canEat()) { + boolean[] ate = new boolean[1]; + + game.inventory().withAction("Eat").withoutId(IGNORE_FOOD).findFirst().ifPresent((food) -> { + food.interact(0); + lastAte = game.ticks(); + ate[0] = true; + }); + + if (!ate[0]) { + game.inventory().withId(DRINK_SET).findFirst().ifPresent((drink) -> { + drink.interact(0); + lastAte = game.ticks(); + ate[0] = true; + }); + } + + if (!ate[0] && config.eatWarnings()) { + game.utils.sendGameMessage("HP below threshold but you don't have any food!"); + } else { + generateNewEatThreshold(); + } + } + + if (shouldDrinkAntiPoison && canPot()) { + if (!drinkPotion(ANTI_POISON_IDS) && config.antiPoisonWarnings()) { + game.utils.sendGameMessage("Poisoned but you don't have anti-poison!"); + } + + shouldDrinkAntiPoison = false; + lastAntiPoison = game.ticks(); + } + + if (shouldDrinkAntiFire && canPot()) { + if (!drinkPotion(ANTI_FIRE_IDS) && config.antiFireWarnings()) { + game.utils.sendGameMessage("Sustaining dragon fire damage but you don't have an anti-dragon fire potion!"); + } + + shouldDrinkAntiFire = false; + lastAntiFire = game.ticks(); + } + + if (shouldDrinkPrayer && canPot()) { + if (!drinkPotion(PRAYER_IDS) && config.prayerWarnings()) { + game.utils.sendGameMessage("Prayer points below threshold but you don't have any prayer potions!"); + } + + shouldDrinkPrayer = false; + lastPrayer = game.ticks(); + } + + if (shouldDrinkStrength && canPot()) { + if (!drinkPotion(STRENGTH_IDS) && config.strengthWarnings()) { + game.utils.sendGameMessage("Strength below threshold but you don't have any strength potions!"); + } + + shouldDrinkStrength = false; + lastStrength = game.ticks(); + } + + if (shouldDrinkAttack && canPot()) { + if (!drinkPotion(ATTACK_IDS) && config.attackWarnings()) { + game.utils.sendGameMessage("Attack below threshold but you don't have any attack potions!"); + } + + shouldDrinkAttack = false; + lastAttack = game.ticks(); + } + + if (shouldDrinkDefence && canPot()) { + if (!drinkPotion(DEFENCE_IDS) && config.defenceWarnings()) { + game.utils.sendGameMessage("Defence below threshold but you don't have any defence potions!"); + } + + shouldDrinkDefence = false; + lastDefence = game.ticks(); + } + + if (shouldDrinkRanged && canPot()) { + if (!drinkPotion(RANGED_IDS) && config.rangedWarnings()) { + game.utils.sendGameMessage("Ranged below threshold but you don't have any ranged potions!"); + } + + shouldDrinkRanged = false; + lastRanged = game.ticks(); + } + + if (shouldDrinkMagic && canPot()) { + if (!drinkPotion(MAGIC_IDS) && config.magicWarnings()) { + game.utils.sendGameMessage("Magic below threshold but you don't have any magic potions!"); + } + + shouldDrinkMagic = false; + lastMagic = game.ticks(); + } + + if (config.enableEating() && hp < eatThreshold && config.comboEat()) { + game.inventory().withId(ItemID.COOKED_KARAMBWAN, ItemID.COOKED_KARAMBWAN_3147, ItemID.COOKED_KARAMBWAN_23533).findFirst().ifPresent((food) -> { + food.interact(0); + lastAte = game.ticks(); + }); + } + } + + @Subscribe + @SuppressWarnings("unused") + private void onVarbitChanged(VarbitChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + if (config.drinkAntiPoison() + && event.getIndex() == VarPlayer.POISON.getId() + && game.client().getVarpValue(VarPlayer.POISON.getId()) > 0) { + shouldDrinkAntiPoison = true; + } + } + + @Subscribe + @SuppressWarnings("unused") + private void onChatMessage(ChatMessage event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + String BURN_MESSAGE = ("You're horribly burnt by the dragon fire!"); + String BURN_EXPIRE = ("antifire potion is about to expire."); + + String message = event.getMessage(); + + if (config.drinkAntiFire() && (message.contains(BURN_MESSAGE) || message.contains(BURN_EXPIRE))) { + shouldDrinkAntiFire = true; + } + } + + @Subscribe + @SuppressWarnings("unused") + private void onStatChanged(StatChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN || timeout > 0) return; + + Skill skill = event.getSkill(); + int level = event.getBoostedLevel(); + + checkSkill(skill, level); + } + + @Subscribe + @SuppressWarnings("unused") + private void onGameStateChanged(GameStateChanged event) { + if (event.getGameState() == GameState.LOGGED_IN) { + timeout = 5; + } + } + + @Subscribe + @SuppressWarnings("unused") + private void onConfigChanged(ConfigChanged event) { + switch (event.getKey()) { + case "minEatHP": + case "maxEatHP": + generateNewEatThreshold(); + break; + case "minPrayerPoints": + case "maxPrayerPoints": + generateNewPrayerThreshold(); + case "drinkPrayer": + checkSkill(Skill.PRAYER); + break; + case "strengthLevel": + checkSkill(Skill.STRENGTH); + break; + case "attackLevel": + checkSkill(Skill.ATTACK); + break; + case "defenceLevel": + checkSkill(Skill.DEFENCE); + break; + case "rangedLevel": + checkSkill(Skill.RANGED); + break; + case "magicLevel": + checkSkill(Skill.MAGIC); + break; + } + } + + private void generateNewEatThreshold() { + eatThreshold = calc.random(config.minEatHP(), config.maxEatHP() + 1); + } + + private void generateNewPrayerThreshold() { + prayerThreshold = calc.random(config.minPrayerPoints(), config.maxPrayerPoints() + 1); + } + + private boolean canPot() { + return game.ticks() > lastPot + 3; + } + + private boolean canEat() { + return game.ticks() > lastAte + 3; + } + + private void checkSkill(Skill skill) { + checkSkill(skill, game.modifiedLevel(skill)); + } + + private void checkSkill(Skill skill, int level) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + switch (skill) { + case PRAYER: + if (config.drinkPrayer() && level <= prayerThreshold && game.ticks() > lastPrayer + 5) { + shouldDrinkPrayer = true; + } + break; + case STRENGTH: + if (config.drinkStrength() && level <= config.strengthLevel() && game.ticks() > lastStrength + 5) { + shouldDrinkStrength = true; + } + break; + case ATTACK: + if (config.drinkAttack() && level <= config.attackLevel() && game.ticks() > lastAttack + 5) { + shouldDrinkAttack = true; + } + break; + case DEFENCE: + if (config.drinkDefence() && level <= config.defenceLevel() && game.ticks() > lastDefence + 5) { + shouldDrinkDefence = true; + } + break; + case RANGED: + if (config.drinkRanged() && level <= config.rangedLevel() && game.ticks() > lastRanged + 5) { + shouldDrinkRanged = true; + } + break; + case MAGIC: + if (config.drinkMagic() && level <= config.magicLevel() && game.ticks() > lastMagic + 5) { + shouldDrinkMagic = true; + } + break; + } + } + + private boolean drinkPotion(Set ids) { + InventoryItem potion = game.inventory().withId(ids).first(); + if (potion == null) return false; + + potion.interact(0); + lastPot = game.ticks(); + return true; + } +} \ No newline at end of file diff --git a/cooker/cooker.gradle.kts b/cooker/cooker.gradle.kts new file mode 100644 index 0000000..28450a1 --- /dev/null +++ b/cooker/cooker.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Cooker" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Cooks better than Gordon Ramsay" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/cooker/src/main/java/io/reisub/openosrs/cooker/Activity.java b/cooker/src/main/java/io/reisub/openosrs/cooker/Activity.java new file mode 100644 index 0000000..9614e5b --- /dev/null +++ b/cooker/src/main/java/io/reisub/openosrs/cooker/Activity.java @@ -0,0 +1,6 @@ +package io.reisub.openosrs.cooker; + +public enum Activity { + IDLE, + COOKING; +} diff --git a/cooker/src/main/java/io/reisub/openosrs/cooker/Config.java b/cooker/src/main/java/io/reisub/openosrs/cooker/Config.java new file mode 100644 index 0000000..de3c508 --- /dev/null +++ b/cooker/src/main/java/io/reisub/openosrs/cooker/Config.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.cooker; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosCookerConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "rawFood", + name = "Food ID", + description = "ID of the raw food to cook.", + position = 1 + ) + default int rawFood() { + return 361; + } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/cooker/src/main/java/io/reisub/openosrs/cooker/Cooker.java b/cooker/src/main/java/io/reisub/openosrs/cooker/Cooker.java new file mode 100644 index 0000000..cf6aecd --- /dev/null +++ b/cooker/src/main/java/io/reisub/openosrs/cooker/Cooker.java @@ -0,0 +1,159 @@ +package io.reisub.openosrs.cooker; + +import com.google.inject.Provides; +import io.reisub.openosrs.cooker.tasks.Cook; +import io.reisub.openosrs.cooker.tasks.HandleBank; +import io.reisub.openosrs.cooker.tasks.SkipLevel; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Run; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.ChatMessageType; +import net.runelite.api.GameState; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import static net.runelite.api.AnimationID.IDLE; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Cooker", + description = "Cooks better than Gordon Ramsay", + enabledByDefault = false +) +@Slf4j +public class Cooker extends iScript { + private List tasks; + + @Getter + private Activity currentActivity; + + private Instant lastActionTime; + + @Inject + private Config config; + + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + checkActionTimeout(); + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Cooker"); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + tasks = new ArrayList<>(); + tasks.add(runTask); + tasks.add(injector.getInstance(SkipLevel.class)); + tasks.add(injector.getInstance(HandleBank.class)); + tasks.add(injector.getInstance(Cook.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Cooker"); + if (tasks != null) { + tasks.clear(); + } + + setActivity(Activity.IDLE); + } + + @SuppressWarnings("unused") + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onAnimationChanged(AnimationChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + if (event.getActor() != game.client().getLocalPlayer()) return; + + int animId = game.localPlayer().animation(); + switch (animId) { + case AnimationID.COOKING_FIRE: + case AnimationID.COOKING_RANGE: + setActivity(Activity.COOKING); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onItemContainerChanged(ItemContainerChanged event) { + if (!game.inventory().withId(config.rawFood()).exists()) { + setActivity(Activity.IDLE); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (chatMessage.getType() == ChatMessageType.GAMEMESSAGE) { + if (chatMessage.getMessage().startsWith("Congratulations, you've just advanced your")) { + setActivity(Activity.IDLE); + } + } + } + + private void setActivity(Activity action) { + currentActivity = action; + + if (action != Activity.IDLE) { + lastActionTime = Instant.now(); + } + } + + private void checkActionTimeout() { + if (currentActivity == Activity.IDLE) return; + + int animId = game.localPlayer().animation(); + if (animId != IDLE || lastActionTime == null) return; + + Duration timeout = Duration.ofSeconds(3); + Duration sinceAction = Duration.between(lastActionTime, Instant.now()); + + if (sinceAction.compareTo(timeout) >= 0) { + setActivity(Activity.IDLE); + } + } +} \ No newline at end of file diff --git a/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/Cook.java b/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/Cook.java new file mode 100644 index 0000000..ceee69c --- /dev/null +++ b/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/Cook.java @@ -0,0 +1,58 @@ +package io.reisub.openosrs.cooker.tasks; + +import io.reisub.openosrs.cooker.Activity; +import io.reisub.openosrs.cooker.Config; +import io.reisub.openosrs.cooker.Cooker; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import javax.inject.Inject; + +public class Cook extends Task { + @Inject + private Cooker plugin; + + @Inject + private Config config; + + @Override + public String getStatus() { + return "Cooking"; + } + + @Override + public boolean validate() { + return plugin.getCurrentActivity() == Activity.IDLE + && game.inventory().withId(config.rawFood()).exists(); + } + + @Override + public void execute() { + iObject oven = game.objects().withName("Clay oven").nearest(); + iObject fire = game.objects().withName("Fire").nearest(); + if (oven == null && fire == null) return; + + if (oven != null) { + oven.interact(0); + } else { + InventoryItem rawFood = game.inventory().withId(config.rawFood()).first(); + if (rawFood == null) return; + + rawFood.useOn(fire); + } + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.MAKE, 20); + + if (chatbox.chatState() == Chatbox.ChatState.MAKE) { + if (calc.random(0, 4) == 1) { + game.sleepDelay(); + } + + int count = (int) game.inventory().withId(config.rawFood()).count(); + + chatbox.make(0, count); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.COOKING, 5); + } + } +} diff --git a/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/HandleBank.java b/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/HandleBank.java new file mode 100644 index 0000000..caccb4a --- /dev/null +++ b/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/HandleBank.java @@ -0,0 +1,56 @@ +package io.reisub.openosrs.cooker.tasks; + +import io.reisub.openosrs.cooker.Config; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +public class HandleBank extends Task { + @Inject + private Config config; + + @Override + public String getStatus() { + return "Banking"; + } + + @Override + public boolean validate() { + return !game.inventory().withId(config.rawFood()).exists(); + } + + @Override + public void execute() { + if (!bank.isOpen()) { + iNPC banker = game.npcs().withName("Emerald Benedict").first(); + iObject bankObj = game.objects().withName("Bank chest", "Bank booth").nearest();; + if (banker == null && bankObj == null) return; + + if (banker != null) { + banker.interact("Bank"); + } else { + bankObj.interact(0); + } + game.waitUntil(() -> bank.isOpen(), 20); + } + + if (game.inventory().findAny().isPresent()) { + bank.depositInventory(); + game.tick(); + game.sleepDelay(); + } + + if (config.rawFood() == ItemID.GIANT_SEAWEED) { + bank.withdraw(config.rawFood(), 4, false); + } else { + bank.withdraw(config.rawFood(), 28, false); + } + + game.sleepDelay(); + bank.close(); + game.sleepDelay(); + } +} diff --git a/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/SkipLevel.java b/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/SkipLevel.java new file mode 100644 index 0000000..667e9a2 --- /dev/null +++ b/cooker/src/main/java/io/reisub/openosrs/cooker/tasks/SkipLevel.java @@ -0,0 +1,22 @@ +package io.reisub.openosrs.cooker.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +public class SkipLevel extends Task { + @Override + public String getStatus() { + return "Skipping level chatbox"; + } + + @Override + public boolean validate() { + return chatbox.chatState() == Chatbox.ChatState.LEVEL_UP; + } + + @Override + public void execute() { + chatbox.continueChats(); + game.tick(); + } +} diff --git a/fisher/fisher.gradle.kts b/fisher/fisher.gradle.kts new file mode 100644 index 0000000..2d4fa6a --- /dev/null +++ b/fisher/fisher.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Fisher" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Frantically fishes fish" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/fisher/src/main/java/io/reisub/openosrs/fisher/FisherConfig.java b/fisher/src/main/java/io/reisub/openosrs/fisher/FisherConfig.java new file mode 100644 index 0000000..94f7fc3 --- /dev/null +++ b/fisher/src/main/java/io/reisub/openosrs/fisher/FisherConfig.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.fisher; + +import net.runelite.client.config.Button; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosFisherConfig") + +public interface FisherConfig extends Config { + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/fisher/src/main/java/io/reisub/openosrs/fisher/FisherPlugin.java b/fisher/src/main/java/io/reisub/openosrs/fisher/FisherPlugin.java new file mode 100644 index 0000000..adf1b3d --- /dev/null +++ b/fisher/src/main/java/io/reisub/openosrs/fisher/FisherPlugin.java @@ -0,0 +1,90 @@ +package io.reisub.openosrs.fisher; + +import com.google.inject.Provides; +import io.reisub.openosrs.fisher.tasks.Drop; +import io.reisub.openosrs.fisher.tasks.Fish; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.KittenTask; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Fisher", + description = "Frantically fishes fish", + enabledByDefault = false +) +@Slf4j +public class FisherPlugin extends iScript { + private List tasks; + + private KittenTask kittenTask; + + @Provides + FisherConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(FisherConfig.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Fisher"); + + kittenTask = KittenTask.getInstance(injector); + + tasks = new ArrayList<>(); + tasks.add(kittenTask); + tasks.add(injector.getInstance(Drop.class)); + tasks.add(injector.getInstance(Fish.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Fisher"); + if (tasks != null) { + tasks.clear(); + } + + KittenTask.handleKitten = false; + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (kittenTask != null) { + kittenTask.onChatMessage(chatMessage); + } + } +} \ No newline at end of file diff --git a/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/DoBank.java b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/DoBank.java new file mode 100644 index 0000000..8a08032 --- /dev/null +++ b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/DoBank.java @@ -0,0 +1,51 @@ +package io.reisub.openosrs.fisher.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.scene.Position; +import net.runelite.client.plugins.iutils.ui.Bank; + +import javax.inject.Inject; + +public class DoBank extends Task { + @Inject + public Bank bank; + + @Override + public String getStatus() { + return "Banking"; + } + + @Override + public boolean validate() { + return game.inventory().full(); + } + + @Override + public void execute() { + iNPC banker = game.npcs().withName("Banker").nearest(); + + if (banker == null) { + Position pos = new Position(2852, 2957, 0); + game.walkUtils.sceneWalk(pos, 1, calc.random(50, 250)); + game.tick(); + + game.waitUntil(() -> pos.distanceTo(game.localPlayer().position()) < calc.random(6, 9)); + } else if (banker.position().distanceTo(game.localPlayer().position()) > 8) { + Position pos = banker.position(); + game.walkUtils.sceneWalk(pos,1, calc.random(50, 250)); + game.tick(); + + game.waitUntil(() -> pos.distanceTo(game.localPlayer().position()) < calc.random(6, 9)); + } + + banker = game.npcs().withName("Banker").nearest(); + + banker.interact("Bank"); + game.tick(); + + game.waitUntil(() -> bank.isOpen(), 10); + bank.depositAll(true, 335, 331); + game.tick(); + } +} diff --git a/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Drop.java b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Drop.java new file mode 100644 index 0000000..8e3dd8d --- /dev/null +++ b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Drop.java @@ -0,0 +1,25 @@ +package io.reisub.openosrs.fisher.tasks; + +import io.reisub.openosrs.util.Task; + +public class Drop extends Task { + @Override + public String getStatus() { + return "Dropping"; + } + + @Override + public boolean validate() { + return game.inventory().full(); + } + + @Override + public void execute() { + game.inventory().withNamePart("Leaping").forEach((item) -> { + item.interact("Drop"); + game.sleepDelay(); + }); + + game.tick(); + } +} diff --git a/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Fish.java b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Fish.java new file mode 100644 index 0000000..34ffb20 --- /dev/null +++ b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Fish.java @@ -0,0 +1,36 @@ +package io.reisub.openosrs.fisher.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iActor; +import net.runelite.client.plugins.iutils.game.iNPC; + +public class Fish extends Task { + private iNPC spot; + + @Override + public String getStatus() { + return "Fishing"; + } + + @Override + public boolean validate() { + spot = game.npcs().withName("Fishing spot") + .filter(iNPC -> iNPC.position().y < 3513 && iNPC.position().y > 3504).nearest(); + + iActor target = game.localPlayer().target(); + if (target != null && (target.position().y >= 3513 || target.position().y <= 3504)) return true; + + return !game.inventory().full() + && spot != null + && (game.localPlayer().isIdle() || game.localPlayer().target() == null); + } + + @Override + public void execute() { + spot.interact("Use-rod"); + game.tick(calc.random(2, 4)); + + game.waitUntil(() -> game.localPlayer().animation() != -1, 10); + spot = null; + } +} diff --git a/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Hop.java b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Hop.java new file mode 100644 index 0000000..a39f1a3 --- /dev/null +++ b/fisher/src/main/java/io/reisub/openosrs/fisher/tasks/Hop.java @@ -0,0 +1,66 @@ +package io.reisub.openosrs.fisher.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.GameState; +import net.runelite.api.World; +import net.runelite.api.WorldType; +import net.runelite.client.plugins.iutils.game.iNPC; + +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.Queue; + +public class Hop extends Task { + private Queue worlds; + + @Override + public String getStatus() { + return "Hopping"; + } + + @Override + public boolean validate() { + iNPC spot = game.npcs().filter(iNPC -> iNPC.name().equals("Rod Fishing spot") && iNPC.position().x > 2850 && iNPC.position().y <= 2974).nearest(); + + return spot == null && !game.inventory().full(); + } + + @Override + public void execute() { + game.client().hopToWorld(getNextWorld()); + game.sleepApproximately(3000); + + game.waitUntil(() -> game.client().getGameState() == GameState.LOGGED_IN); + } + + private World getNextWorld() { + if (worlds == null) { + generateWorldsQueue(); + } + + World w = worlds.poll(); + worlds.add(w); + + return w; + } + + private void generateWorldsQueue() { + worlds = new LinkedList<>(); + + for (World w : game.client().getWorldList()) { + EnumSet types = w.getTypes(); + + if (types.contains(WorldType.MEMBERS) + && !types.contains(WorldType.BOUNTY) + && !types.contains(WorldType.TOURNAMENT_WORLD) + && !types.contains(WorldType.DEADMAN) + && !types.contains(WorldType.HIGH_RISK) + && !types.contains(WorldType.LAST_MAN_STANDING) + && !types.contains(WorldType.NOSAVE_MODE) + && !types.contains(WorldType.SEASONAL) + && !types.contains(WorldType.PVP)) { + worlds.add(w); + } + } + } +} diff --git a/glassblower/glassblower.gradle.kts b/glassblower/glassblower.gradle.kts new file mode 100644 index 0000000..39821b0 --- /dev/null +++ b/glassblower/glassblower.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Glassblower" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Huffs and puffs" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/glassblower/src/main/java/io/reisub/openosrs/glassblower/Config.java b/glassblower/src/main/java/io/reisub/openosrs/glassblower/Config.java new file mode 100644 index 0000000..e3f8928 --- /dev/null +++ b/glassblower/src/main/java/io/reisub/openosrs/glassblower/Config.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.glassblower; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chaosglassblower") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "targetProduct", + name = "Blow", + description = "Choose what to blow.", + position = 0 + ) + default Product targetProduct() { return Product.LANTERN_LENS; } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/glassblower/src/main/java/io/reisub/openosrs/glassblower/Glassblower.java b/glassblower/src/main/java/io/reisub/openosrs/glassblower/Glassblower.java new file mode 100644 index 0000000..59092dd --- /dev/null +++ b/glassblower/src/main/java/io/reisub/openosrs/glassblower/Glassblower.java @@ -0,0 +1,65 @@ +package io.reisub.openosrs.glassblower; + +import com.google.inject.Provides; +import io.reisub.openosrs.glassblower.tasks.Blow; +import io.reisub.openosrs.glassblower.tasks.HandleBank; +import io.reisub.openosrs.glassblower.tasks.PickupSeed; +import io.reisub.openosrs.util.enums.Activity; +import io.reisub.openosrs.util.CScript; +import io.reisub.openosrs.util.Util; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.GameState; +import net.runelite.api.Skill; +import net.runelite.api.events.AnimationChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Glassblower", + description = "Huffs and puffs", + enabledByDefault = false +) +@Slf4j +public class Glassblower extends CScript { + public static final int FOSSIL_ISLAND_SMALL_ISLAND_REGION = 14908; + public static final int FOSSIL_ISLAND_SEAWEED_REGION = 15008; + + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void onStart() { + super.onStart(); + + idleCheckSkills.put(Skill.CRAFTING, Activity.GLASSBLOWING); + + addTask(PickupSeed.class); + addTask(HandleBank.class); + addTask(Blow.class); + } + + @SuppressWarnings("unused") + @Subscribe + private void onAnimationChanged(AnimationChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + if (event.getActor() != game.client().getLocalPlayer()) return; + + + int animId = game.localPlayer().animation(); + switch (animId) { + case AnimationID.CRAFTING_GLASSBLOWING: + setActivity(Activity.GLASSBLOWING); + break; + } + } +} \ No newline at end of file diff --git a/glassblower/src/main/java/io/reisub/openosrs/glassblower/Product.java b/glassblower/src/main/java/io/reisub/openosrs/glassblower/Product.java new file mode 100644 index 0000000..fba406a --- /dev/null +++ b/glassblower/src/main/java/io/reisub/openosrs/glassblower/Product.java @@ -0,0 +1,19 @@ +package io.reisub.openosrs.glassblower; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum Product { + BEER_GLASS(0), + EMPTY_CANDLE_LANTERN(1), + EMPTY_OIL_LAMP(2), + VIAL(3), + FISHBOWL(4), + UNPOWERED_ORB(5), + LANTERN_LENS(6), + LIGHT_ORB(7); + + private final int makeIndex; +} diff --git a/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/Blow.java b/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/Blow.java new file mode 100644 index 0000000..edf066f --- /dev/null +++ b/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/Blow.java @@ -0,0 +1,62 @@ +package io.reisub.openosrs.glassblower.tasks; + +import io.reisub.openosrs.glassblower.Config; +import io.reisub.openosrs.glassblower.Glassblower; +import io.reisub.openosrs.util.enums.Activity; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import javax.inject.Inject; + +public class Blow extends Task { + @Inject + private Glassblower plugin; + + @Inject + private Config config; + + @Override + public String getStatus() { + return "Glassblowing"; + } + + @Override + public boolean validate() { + return game.inventory().withId(ItemID.GLASSBLOWING_PIPE).exists() + && game.inventory().withId(ItemID.MOLTEN_GLASS).exists() + && plugin.getCurrentActivity() == Activity.IDLE; + } + + @Override + public void execute() { + if (game.localPlayer().position().regionID() == Glassblower.FOSSIL_ISLAND_SMALL_ISLAND_REGION) { + iObject rowboat = game.objects().withName("Rowboat").nearest(); + if (rowboat == null) return; + + rowboat.interact("Dive"); + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT + || game.localPlayer().position().regionID() == Glassblower.FOSSIL_ISLAND_SEAWEED_REGION, 10); + + if (chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT) { + chatbox.chooseOption(1); + game.waitUntil(() -> game.localPlayer().position().regionID() == Glassblower.FOSSIL_ISLAND_SEAWEED_REGION, 10); + } + game.tick(); + } + + InventoryItem pipe = game.inventory().withId(ItemID.GLASSBLOWING_PIPE).first(); + InventoryItem moltenGlass = game.inventory().withId(ItemID.MOLTEN_GLASS).first(); + if (pipe == null || moltenGlass == null) return; + + pipe.useOn(moltenGlass); + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.MAKE, 5); + + int quantity = (int) game.inventory().withId(ItemID.MOLTEN_GLASS).count(); + + chatbox.make(config.targetProduct().getMakeIndex(), quantity); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.GLASSBLOWING, 5); + } +} diff --git a/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/HandleBank.java b/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/HandleBank.java new file mode 100644 index 0000000..1ccb9ee --- /dev/null +++ b/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/HandleBank.java @@ -0,0 +1,76 @@ +package io.reisub.openosrs.glassblower.tasks; + +import io.reisub.openosrs.glassblower.Glassblower; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iObject; + +import java.time.Duration; +import java.time.Instant; + +public class HandleBank extends Task { + private Instant lastBanking = Instant.EPOCH; + + @Override + public String getStatus() { + return "Banking"; + } + + @Override + public boolean validate() { + return !game.inventory().withId(ItemID.MOLTEN_GLASS).exists() + && Duration.between(lastBanking, Instant.now()).getSeconds() > 5; + } + + @Override + public void execute() { + if (game.localPlayer().position().regionID() == Glassblower.FOSSIL_ISLAND_SEAWEED_REGION) { + iObject rope = game.objects().withName("Anchor rope").first(); + if (rope == null) return; + + rope.interact("Climb"); + if (!game.waitUntil(() -> game.localPlayer().position().regionID() == Glassblower.FOSSIL_ISLAND_SMALL_ISLAND_REGION + || game.groundItems().withId(ItemID.SEAWEED_SPORE).exists(), 30)) { + return; + } + + if (game.groundItems().withId(ItemID.SEAWEED_SPORE).exists()) return; + + game.tick(); + } + + if (!bank.isOpen()) { + iObject bankObj = game.objects().withName("Bank chest", "Bank booth", "Bank Chest-wreck").nearest(); + if (bankObj == null) return; + + bankObj.interact(0); + game.waitUntil(() -> bank.isOpen(), 15); + } + + bank.depositExcept(false, ItemID.GLASSBLOWING_PIPE, ItemID.SEAWEED_SPORE); +// bank.depositAll(false, ItemID.BEER_GLASS, +// ItemID.EMPTY_CANDLE_LANTERN, +// ItemID.EMPTY_OIL_LAMP, +// ItemID.VIAL, +// ItemID.FISHBOWL, +// ItemID.UNPOWERED_ORB, +// ItemID.LANTERN_LENS, +// ItemID.LIGHT_ORB); + game.tick(); + game.sleepDelay(); + + if (!game.inventory().withId(ItemID.GLASSBLOWING_PIPE).exists()) { + bank.withdraw(ItemID.GLASSBLOWING_PIPE, 1, false); + game.sleepDelay(); + } + + int quantity = game.inventory().withId(ItemID.SEAWEED_SPORE).exists() ? 26 : 27; + + bank.withdraw(ItemID.MOLTEN_GLASS, quantity, false); + game.sleepDelay(); + + bank.close(); + game.sleepDelay(); + lastBanking = Instant.now(); + } +} diff --git a/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/PickupSeed.java b/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/PickupSeed.java new file mode 100644 index 0000000..b320d01 --- /dev/null +++ b/glassblower/src/main/java/io/reisub/openosrs/glassblower/tasks/PickupSeed.java @@ -0,0 +1,28 @@ +package io.reisub.openosrs.glassblower.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iGroundItem; + +public class PickupSeed extends Task { + @Override + public String getStatus() { + return "Picking up seed"; + } + + @Override + public boolean validate() { + return game.groundItems().withId(ItemID.SEAWEED_SPORE).exists(); + } + + @Override + public void execute() { + iGroundItem item = game.groundItems().withId(ItemID.SEAWEED_SPORE).nearest(); + if (item == null) return; + + int quantity = game.inventory().withId(ItemID.SEAWEED_SPORE).quantity(); + + item.interact("Take"); + game.waitUntil(() -> game.inventory().withId(ItemID.SEAWEED_SPORE).quantity() > quantity, 30); + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..b639f73 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,7 @@ +org.gradle.caching=false +org.gradle.warning.mode=all +org.gradle.parallel=true +org.gradle.console=rich +org.gradle.configureondemand=true +org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +kapt.incremental.apt=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..6254d2d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/masterthiever/masterthiever.gradle.kts b/masterthiever/masterthiever.gradle.kts new file mode 100644 index 0000000..83099f6 --- /dev/null +++ b/masterthiever/masterthiever.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Master Thiever" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Steals seeds from master farmers" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/Config.java b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/Config.java new file mode 100644 index 0000000..49bdccb --- /dev/null +++ b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/Config.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.masterthiever; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosMasterThieverConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/MasterThiever.java b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/MasterThiever.java new file mode 100644 index 0000000..fc81d9e --- /dev/null +++ b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/MasterThiever.java @@ -0,0 +1,96 @@ +package io.reisub.openosrs.masterthiever; + +import com.google.inject.Provides; + +import io.reisub.openosrs.masterthiever.tasks.HandleBank; +import io.reisub.openosrs.masterthiever.tasks.Steal; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Eat; +import io.reisub.openosrs.util.tasks.KittenTask; +import io.reisub.openosrs.util.tasks.Run; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Master Thiever", + description = "Steals seeds from master farmers.", + enabledByDefault = false +) +@Slf4j +public class MasterThiever extends iScript { + private List tasks; + private KittenTask kittenTask; + + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Master Thiever"); + + Eat eatTask = injector.getInstance(Eat.class); + eatTask.setInterval(14, 24); + + kittenTask = KittenTask.getInstance(injector); + + tasks = new ArrayList<>(); + tasks.add(eatTask); + tasks.add(kittenTask); + tasks.add(injector.getInstance(HandleBank.class)); + tasks.add(injector.getInstance(Steal.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Master Thiever"); + if (tasks != null) { + tasks.clear(); + } + + KittenTask.handleKitten = false; + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (kittenTask != null) { + kittenTask.onChatMessage(chatMessage); + } + } +} \ No newline at end of file diff --git a/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/HandleBank.java b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/HandleBank.java new file mode 100644 index 0000000..75c51d5 --- /dev/null +++ b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/HandleBank.java @@ -0,0 +1,53 @@ +package io.reisub.openosrs.masterthiever.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.client.plugins.iutils.api.Interactable; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.scene.Position; + +import java.util.function.Predicate; + +public class HandleBank extends Task { + @Override + public String getStatus() { + return "Banking"; + } + + @Override + public boolean validate() { + return game.client().getLocalPlayer().getModelHeight() != 1000 + && (game.inventory().full() + || (!game.inventory().withAction("Eat").exists() && game.modifiedLevel(Skill.HITPOINTS) <= 35)); + } + + @Override + public void execute() { + game.tick(); + if (!bank.isOpen()) { + Interactable bankObj; + if (calc.random(0, 1) == 0) { + bankObj = game.objects().filter(obj -> obj.name().equals("Bank booth") && obj.position().equals(new Position(3091, 3245, 0))).first(); + } else { + bankObj = game.npcs().filter(npc -> npc.name().equals("Banker") && npc.position().equals(new Position(3090, 3245, 0))).first(); + } + + if (bankObj == null) return; + + bankObj.interact("Bank"); + game.waitUntil(() -> bank.isOpen(), 15); + } + + bank.depositInventory(); + game.tick(); + + int quantity = 5 + (game.baseLevel(Skill.HITPOINTS) - game.modifiedLevel(Skill.HITPOINTS)) / 9; + + bank.withdraw(ItemID.SALMON, quantity, false); + game.waitUntil(() -> game.inventory().withAction("Eat", "Drink").count() > 1, 6); + + bank.close(); + game.waitUntil(() -> !bank.isOpen(), 3); + } +} diff --git a/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/Steal.java b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/Steal.java new file mode 100644 index 0000000..f74475e --- /dev/null +++ b/masterthiever/src/main/java/io/reisub/openosrs/masterthiever/tasks/Steal.java @@ -0,0 +1,31 @@ +package io.reisub.openosrs.masterthiever.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.NpcID; +import net.runelite.api.Skill; +import net.runelite.client.plugins.iutils.game.iNPC; + +public class Steal extends Task { + @Override + public String getStatus() { + return "Pickpocketing"; + } + + @Override + public boolean validate() { + return !game.inventory().full() + && game.client().getLocalPlayer().getModelHeight() != 1000 + && (game.inventory().withAction("Eat").exists() || game.modifiedLevel(Skill.HITPOINTS) > 35); + } + + @Override + public void execute() { + iNPC farmer = game.npcs().withId(NpcID.MASTER_FARMER, NpcID.MASTER_FARMER_5731).nearest(); + + if (farmer == null) return; + + farmer.interact("Pickpocket"); + game.tick(calc.random(0, 2)); + game.sleepDelay(); + } +} diff --git a/miner/miner.gradle.kts b/miner/miner.gradle.kts new file mode 100644 index 0000000..6316cca --- /dev/null +++ b/miner/miner.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Miner" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Mines stuff" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/miner/src/main/java/io/reisub/openosrs/miner/Config.java b/miner/src/main/java/io/reisub/openosrs/miner/Config.java new file mode 100644 index 0000000..8a74918 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/Config.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.miner; + +import io.reisub.openosrs.util.enums.Log; +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("chaosminer") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "3t4g", + name = "3t4g mining", + description = "Enable three tick, four granite mining.", + position = 0 + ) + default boolean threeTickFourGranite() { return false; } + + @ConfigItem( + keyName = "3t4s", + name = "3t4s mining", + description = "Enable three tick, four sandstone mining.", + position = 1 + ) + default boolean threeTickFourSandstone() { return false; } + + @ConfigItem( + keyName = "prepareBirdhouseRun", + name = "Prepare for birdhouse run", + description = "When enabled, will teleport away when out of water and withdraw materials needed for a birdhouse run. Either do the run manually or use the Chaos Birdhouse plugin.", + position = 10 + ) + default boolean prepareBirdhouseRun() { return false; } + + @ConfigItem( + keyName = "birdhouseLogs", + name = "Logs", + description = "What log type to take out of the bank for birdhouse runs.", + hidden = true, + unhide = "prepareBirdhouseRun", + position = 11 + ) + default Log birdhouseLogs() { return Log.YEW; } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/miner/src/main/java/io/reisub/openosrs/miner/Miner.java b/miner/src/main/java/io/reisub/openosrs/miner/Miner.java new file mode 100644 index 0000000..7316094 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/Miner.java @@ -0,0 +1,147 @@ +package io.reisub.openosrs.miner; + +import com.google.inject.Provides; +import io.reisub.openosrs.miner.tasks.*; +import io.reisub.openosrs.util.CScript; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Run; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameState; +import net.runelite.api.ItemID; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Miner", + description = "Mines stuff", + enabledByDefault = false +) +@Slf4j +public class Miner extends CScript { + @Inject + private Config config; + + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Getter + @Setter + private boolean startedCycle; + + @Getter + @Setter + private boolean interrupted; + + private int ticks; + + @Getter + private int ticksWithoutSuccess; + + private Mine mineTask; + private ScheduledExecutorService executor; + + @Override + protected void onStart() { + super.onStart(); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + ticks = 0; + + tasks.add(runTask); + addTask(TpAway.class); + addTask(PrepareMining.class); + addTask(PrepareBirdhouseRun.class); + //addTask(Drop.class); + if (!config.threeTickFourSandstone() && !config.threeTickFourGranite()) { + addTask(Mine.class); + } else { + mineTask = injector.getInstance(Mine.class); + executor = Executors.newSingleThreadScheduledExecutor(); + addTask(WalkToStartPosition.class); + } + addTask(Deposit.class); + } + + @Override + protected void onStop() { + super.onStop(); + mineTask = null; + executor = null; + } + + public boolean hasWaterSkins() { + return game.inventory().withId( + ItemID.WATERSKIN1, + ItemID.WATERSKIN2, + ItemID.WATERSKIN3, + ItemID.WATERSKIN4 + ).exists(); + } + + @SuppressWarnings("unused") + @Subscribe + private void onGameTick(GameTick event) { + if (mineTask != null && executor != null) { + if (ticksWithoutSuccess++ > 15) { + ticks = -6; + ticksWithoutSuccess = 0; + return; + } + + executor.schedule(() -> { + ticks++; + if (ticks == 3) ticks = 0; + + if (ticks == 0 && mineTask.validate()) { + mineTask.execute(); + } + }, calc.random(245, 250), TimeUnit.MILLISECONDS); + //}, calc.random(245, 250), TimeUnit.MILLISECONDS); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onItemContainerChanged(ItemContainerChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + game.inventory().withNamePart("Granite").drop(); + if (game.inventory().withId(ItemID.WATERSKIN0).exists()) { + game.inventory().withId(ItemID.WATERSKIN0).drop(); + interrupted = true; + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onChatMessage(ChatMessage event) { + if (event.getMessage().equals("You take a drink of water.")) { + interrupted = true; + } + + if (event.getMessage().startsWith("You manage to quarry")) { + ticksWithoutSuccess = 0; + } + } +} \ No newline at end of file diff --git a/miner/src/main/java/io/reisub/openosrs/miner/RockPosition.java b/miner/src/main/java/io/reisub/openosrs/miner/RockPosition.java new file mode 100644 index 0000000..15b646e --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/RockPosition.java @@ -0,0 +1,10 @@ +package io.reisub.openosrs.miner; + +import lombok.Value; +import net.runelite.client.plugins.iutils.scene.Position; + +@Value +public class RockPosition { + Position rock; + Position interactFrom; +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/Deposit.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Deposit.java new file mode 100644 index 0000000..e9e5a47 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Deposit.java @@ -0,0 +1,50 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.miner.Config; +import io.reisub.openosrs.miner.Miner; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +public class Deposit extends Task { + @Inject + private Miner plugin; + + @Inject + private Config config; + + @Override + public String getStatus() { + return "Depositing grinder"; + } + + @Override + public boolean validate() { + if (!plugin.hasWaterSkins() && game.inventory().withNamePart("Sandstone").exists()) return true; + + return game.inventory().full() + && !game.inventory().withNamePart("Granite").exists() + && game.localPlayer().position().regionID() == 12589 + && (game.localPlayer().isIdle() || plugin.isInterrupted()); + } + + @Override + public void execute() { + if (plugin.isInterrupted()) { + plugin.setInterrupted(false); + } + + if (config.threeTickFourGranite() || config.threeTickFourSandstone()) { + walking.setRun(false); + game.sleepDelay(); + } + + iObject grinder = game.objects().withName("Grinder").nearest(); + if (grinder == null) return; + + grinder.interact(0); + plugin.setStartedCycle(false); + game.waitUntil(() -> !game.inventory().withNamePart("Sandstone").exists() || plugin.isInterrupted(), 10); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/Drop.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Drop.java new file mode 100644 index 0000000..c855c0a --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Drop.java @@ -0,0 +1,37 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; + +import java.time.Duration; +import java.time.Instant; + +public class Drop extends Task { + private Instant lastDrop = Instant.EPOCH; + + @Override + public String getStatus() { + return "Dropping"; + } + + @Override + public boolean validate() { + return Duration.between(lastDrop, Instant.now()).getSeconds() > 1 + && (game.inventory().withNamePart("Granite").exists() + || game.inventory().withId(ItemID.WATERSKIN0).exists()); + } + + @Override + public void execute() { + boolean inventoryIsFull = game.inventory().full(); + + game.inventory().withNamePart("Granite").drop(); + game.inventory().withId(ItemID.WATERSKIN0).drop(); + + if (inventoryIsFull) { + game.waitUntil(() -> !game.inventory().full(), 5); + } + + lastDrop = Instant.now(); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/Mine.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Mine.java new file mode 100644 index 0000000..b58faa8 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Mine.java @@ -0,0 +1,175 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.miner.Config; +import io.reisub.openosrs.miner.Miner; +import io.reisub.openosrs.miner.RockPosition; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; +import java.util.*; + +public class Mine extends Task { + @Inject + private Miner plugin; + + @Inject + private Config config; + + private final List positions = new ArrayList<>() { + { + add(new Position(3167, 2913, 0)); + add(new Position(3166, 2913, 0)); + add(new Position(3167, 2915, 0)); + add(new Position(3166, 2915, 0)); + } + }; + + private final Queue threeTickGranitePositions = new LinkedList<>() { + { + add(new Position(3165, 2908, 0)); + add(new Position(3165, 2909, 0)); + add(new Position(3165, 2910, 0)); + add(new Position(3167, 2911, 0)); + } + }; + + private final Position sandstoneStartPosition = new Position(3165, 2914, 0); + private final Queue threeTickSandstonePositions = new LinkedList<>() { + { + add(new RockPosition(new Position(3166, 2913, 0), new Position(3166, 2914, 0))); + add(new RockPosition(new Position(3164, 2915, 0), new Position(3165, 2915, 0))); + add(new RockPosition(new Position(3164, 2914, 0), new Position(3165, 2914, 0))); + add(new RockPosition(new Position(3167, 2913, 0), new Position(3166, 2914, 0))); + } + }; + + private RockPosition currentRockPosition; + + @Override + public String getStatus() { + return "Mining"; + } + + @Override + public boolean validate() { + if (isThreeTick() + && !plugin.isStartedCycle() + && !game.localPlayer().position().equals(sandstoneStartPosition)) { + return false; + } + + return !game.inventory().full() + && plugin.hasWaterSkins() + && game.localPlayer().position().regionID() == 12589 + && (game.localPlayer().isIdle() || isThreeTick()); + } + + @Override + public void execute() { + iObject rock = getRock(); + + if (isThreeTick() && !walking.isRunning()) { + walking.setRun(true); + } + + if (isThreeTick() && plugin.isStartedCycle()) { + InventoryItem knife = game.inventory().withId(ItemID.KNIFE).first(); + InventoryItem logs = game.inventory().withId(ItemID.TEAK_LOGS, ItemID.MAHOGANY_LOGS).first(); + if (knife == null || logs == null) return; + + knife.useOn(logs); + if (rock == null) { + game.walkUtils.sceneWalk(currentRockPosition.getInteractFrom(), 0, 0); + } + } + + while (rock == null && isThreeTick()) { + rock = game.objects().withPosition(currentRockPosition.getRock()).withId(11386, 11387).first(); + } + + if (rock == null || game.inventory().full()) return; + rock.interact(0); + + if (!isThreeTick()) { + iObject finalRock = rock; + game.waitUntil(() -> { + iObject emptyRock = game.objects().withPosition(finalRock.position()).withId(11390, 11391).first(); + return emptyRock != null; + }, 10); + } + } + + private iObject getRock() { + if (isThreeTick()) { + if (!plugin.isStartedCycle()) { + plugin.setStartedCycle(true); + resetQueue(); + } + + Position position; + + if (config.threeTickFourSandstone()) { + currentRockPosition = threeTickSandstonePositions.poll(); + threeTickSandstonePositions.add(currentRockPosition); + position = currentRockPosition.getRock(); + } else { + position = threeTickGranitePositions.poll(); + threeTickGranitePositions.add(position); + } + + return game.objects().withPosition(position).withId(11386, 11387).first(); + } else { + List rocks = game.objects().withId(11386, 11387).filter(o -> { + for (Position p : positions) { + if (p.equals(o.position())) return true; + } + + return false; + }).all(); + + if (rocks == null || rocks.isEmpty()) return null; + + int x = game.localPlayer().position().x; + + for (iObject r : rocks) { + if (r.position().x == x) { + return r; + } + } + + return rocks.get(calc.random(0, rocks.size())); + } + } + + private boolean isThreeTick() { + return config.threeTickFourGranite() || config.threeTickFourSandstone(); + } + + private void resetQueue() { + if (config.threeTickFourSandstone()) { + resetThreeTickSandstone(); + } else { + resetThreeTickGranite(); + } + } + + private void resetThreeTickGranite() { + threeTickGranitePositions.clear(); + threeTickGranitePositions.add(new Position(3165, 2908, 0)); + threeTickGranitePositions.add(new Position(3165, 2909, 0)); + threeTickGranitePositions.add(new Position(3165, 2910, 0)); + threeTickGranitePositions.add(new Position(3167, 2911, 0)); + } + + private void resetThreeTickSandstone() { + threeTickSandstonePositions.clear(); + threeTickSandstonePositions.add(new RockPosition(new Position(3166, 2913, 0), new Position(3166, 2914, 0))); + threeTickSandstonePositions.add(new RockPosition(new Position(3164, 2915, 0), new Position(3165, 2915, 0))); + threeTickSandstonePositions.add(new RockPosition(new Position(3164, 2914, 0), new Position(3165, 2914, 0))); + threeTickSandstonePositions.add(new RockPosition(new Position(3167, 2913, 0), new Position(3166, 2914, 0))); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareBirdhouseRun.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareBirdhouseRun.java new file mode 100644 index 0000000..90aedfa --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareBirdhouseRun.java @@ -0,0 +1,187 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.miner.Config; +import io.reisub.openosrs.miner.Miner; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.ItemQuantity; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; +import java.util.Set; + +public class PrepareBirdhouseRun extends Task { + @Inject + private Miner plugin; + + @Inject + private Config config; + + private Instant lastBank = Instant.EPOCH; + + private final Set PENDANTS = Set.of( + ItemID.DIGSITE_PENDANT_1, + ItemID.DIGSITE_PENDANT_2, + ItemID.DIGSITE_PENDANT_3, + ItemID.DIGSITE_PENDANT_4, + ItemID.DIGSITE_PENDANT_5 + ); + + private final Set BIRDHOUSE_SEEDS = Set.of( + ItemID.BARLEY_SEED, + ItemID.HAMMERSTONE_SEED, + ItemID.ASGARNIAN_SEED, + ItemID.JUTE_SEED, + ItemID.YANILLIAN_SEED, + ItemID.KRANDORIAN_SEED + ); + + private final Set RINGS = Set.of( + ItemID.RING_OF_DUELING1, + ItemID.RING_OF_DUELING2, + ItemID.RING_OF_DUELING3, + ItemID.RING_OF_DUELING4, + ItemID.RING_OF_DUELING5, + ItemID.RING_OF_DUELING6, + ItemID.RING_OF_DUELING7, + ItemID.RING_OF_DUELING8 + ); + + @Override + public String getStatus() { + return "Preparing for birdhouse run"; + } + + @Override + public boolean validate() { + return Duration.between(lastBank, Instant.now()).getSeconds() > 5 + && game.localPlayer().position().regionID() == 9776 + && ( + !game.inventory().withId(PENDANTS).exists() + || !game.inventory().withId(ItemID.CHISEL).exists() + || !game.inventory().withId(ItemID.HAMMER).exists() + || game.inventory().withId(config.birdhouseLogs().getId()).count() < 4 + || game.inventory().withId(BIRDHOUSE_SEEDS).quantity() < 40 + || (!game.inventory().withId(RINGS).exists() && !game.equipment().withId(RINGS).exists()) + ); + } + + @Override + public void execute() { + if (!bank.isOpen()) { + iObject bankObj = game.objects().withName("Bank chest", "Bank booth", "Bank Chest-wreck").nearest(); + if (bankObj == null) return; + + bankObj.interact(0); + game.waitUntil(() -> bank.isOpen(), 15); + } + + if (!game.inventory().all().isEmpty()) { + bank.depositInventory(); + game.sleepDelay(); + } + + withdrawPendant(); + withdrawRing(); + withdrawTools(); + withdrawLogs(); + withdrawSeeds(); + + bank.close(); + game.sleepDelay(); + + game.inventory().withId(RINGS).findFirst().ifPresent((ring) -> { + game.waitUntil(() -> !bank.isOpen(), 5); + ring.interact(0); + game.waitUntil(() -> !game.inventory().withId(RINGS).exists(), 5); + }); + + teleportToFossilIsland(); + + lastBank = Instant.now(); + } + + private void withdrawPendant() { + if (!game.inventory().withId(PENDANTS).exists()) { + + for (Integer id : PENDANTS) { + if (bank.contains(id)) { + bank.withdraw(id, 1, false); + game.sleepDelay(); + break; + } + } + } + } + + private void withdrawRing() { + if (!game.equipment().withId(RINGS).exists() && !game.inventory().withId(RINGS).exists()) { + for (Integer id : RINGS) { + if (bank.contains(id)) { + bank.withdraw(id, 1, false); + game.sleepDelay(); + break; + } + } + } + } + + private void withdrawTools() { + if (!game.inventory().withId(ItemID.CHISEL).exists()) { + bank.withdraw(ItemID.CHISEL, 1, false); + game.sleepDelay(); + } + + if (!game.inventory().withId(ItemID.HAMMER).exists()) { + bank.withdraw(ItemID.HAMMER, 1, false); + game.sleepDelay(); + } + } + + private void withdrawLogs() { + int requiredLogs = 4 - (int) game.inventory().withId(config.birdhouseLogs().getId()).count(); + + if (requiredLogs > 0) { + bank.withdraw(config.birdhouseLogs().getId(), requiredLogs, false); + } + + game.sleepDelay(); + } + + private void withdrawSeeds() { + if (game.inventory().withId(BIRDHOUSE_SEEDS).quantity() < 40) { + for (Integer id : BIRDHOUSE_SEEDS) { + if (bank.contains(new ItemQuantity(id, 40))) { + bank.withdraw(id, 40, false); + game.sleepDelay(); + break; + } + } + } + } + + private void teleportToFossilIsland() { + if (!game.waitUntil(() -> ( + game.inventory().withId(PENDANTS).exists() + && game.inventory().withId(ItemID.CHISEL).exists() + && game.inventory().withId(ItemID.HAMMER).exists() + && game.inventory().withId(config.birdhouseLogs().getId()).count() >= 4 + && game.inventory().withId(BIRDHOUSE_SEEDS).quantity() >= 40 + && (game.inventory().withId(RINGS).exists() || game.equipment().withId(RINGS).exists()) + ), 5)) return; + + game.inventory().withId(PENDANTS).findFirst().ifPresent((pendant) -> { + pendant.interact("Rub"); + game.tick(); + }); + + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT, 5); + + chatbox.chooseOption(2); + game.waitUntil(() -> game.localPlayer().position().regionID() == 14908, 15); + game.tick(); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareMining.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareMining.java new file mode 100644 index 0000000..4c86f3c --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/PrepareMining.java @@ -0,0 +1,41 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iObject; + +public class PrepareMining extends Task { + @Override + public String getStatus() { + return "Preparing for mining"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 14908 + && game.inventory().all().isEmpty() + && !bank.isOpen(); + } + + @Override + public void execute() { + if (!bank.isOpen()) { + iObject bankObj = game.objects().withName("Bank chest", "Bank booth", "Bank Chest-wreck").nearest(); + if (bankObj == null) return; + + bankObj.interact(0); + game.waitUntil(() -> bank.isOpen(), 15); + } + + bank.withdraw(ItemID.KNIFE, 1, false); + game.tick(); + + bank.withdraw(ItemID.TEAK_LOGS, 1, false); + + bank.withdraw(ItemID.WATERSKIN4, 10, false); + game.tick(); + + bank.close(); + game.waitUntil(() -> !bank.isOpen(), 5); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/TpAway.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/TpAway.java new file mode 100644 index 0000000..d376807 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/TpAway.java @@ -0,0 +1,38 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.miner.Config; +import io.reisub.openosrs.miner.Miner; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.EquipmentItem; + +import javax.inject.Inject; + +public class TpAway extends Task { + @Inject + private Miner plugin; + + @Inject + private Config config; + + @Override + public String getStatus() { + return "Teleporting away from quarry"; + } + + @Override + public boolean validate() { + return config.prepareBirdhouseRun() + && !plugin.hasWaterSkins() + && !game.inventory().withNamePart("Sandstone").exists() + && game.localPlayer().position().regionID() == 12589; + } + + @Override + public void execute() { + EquipmentItem ring = game.equipment().withNamePart("Ring of dueling").first(); + if (ring == null) return; + + ring.interact("Castle Wars"); + game.waitUntil(() -> game.localPlayer().position().regionID() == 9776); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/TpToQuarry.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/TpToQuarry.java new file mode 100644 index 0000000..2923da9 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/TpToQuarry.java @@ -0,0 +1,22 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.util.Task; + +public class TpToQuarry extends Task { + @Override + public String getStatus() { + return "Teleporting to Quarry or Al-Kharid"; + } + + @Override + public boolean validate() { + return game.localPlayer().position().regionID() == 14908 + && game.equipment().withNamePart("Ring of dueling").exists() + && game.equipment().withName("Camulet").exists(); + } + + @Override + public void execute() { + + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/Wait.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Wait.java new file mode 100644 index 0000000..96dec9e --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/Wait.java @@ -0,0 +1,21 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; + +public class Wait extends Task { + @Override + public String getStatus() { + return "Waiting"; + } + + @Override + public boolean validate() { + return !game.inventory().withId(ItemID.WATERSKIN1, ItemID.WATERSKIN2, ItemID.WATERSKIN3, ItemID.WATERSKIN4).exists(); + } + + @Override + public void execute() { + game.tick(3); + } +} diff --git a/miner/src/main/java/io/reisub/openosrs/miner/tasks/WalkToStartPosition.java b/miner/src/main/java/io/reisub/openosrs/miner/tasks/WalkToStartPosition.java new file mode 100644 index 0000000..f7b4d88 --- /dev/null +++ b/miner/src/main/java/io/reisub/openosrs/miner/tasks/WalkToStartPosition.java @@ -0,0 +1,43 @@ +package io.reisub.openosrs.miner.tasks; + +import io.reisub.openosrs.miner.Config; +import io.reisub.openosrs.miner.Miner; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +public class WalkToStartPosition extends Task { + @Inject + private Miner plugin; + + @Inject + private Config config; + + public static final Position sandstoneStartPosition = new Position(3165, 2914, 0); + + @Override + public String getStatus() { + return "Walking to starting position"; + } + + @Override + public boolean validate() { + return config.threeTickFourSandstone() + && !plugin.isStartedCycle() + && !game.localPlayer().position().equals(sandstoneStartPosition) + && game.localPlayer().position().regionID() == 12589 + && !game.inventory().withNamePart("Sandstone").exists(); + } + + @Override + public void execute() { + if (game.energy() > 25) { + walking.setRun(true); + game.sleepDelay(); + } + + walking.walkTo(sandstoneStartPosition); + game.waitUntil(() -> game.localPlayer().position().equals(sandstoneStartPosition)); + } +} diff --git a/plugins.json b/plugins.json new file mode 100644 index 0000000..596b111 --- /dev/null +++ b/plugins.json @@ -0,0 +1 @@ +[{"projectUrl":"https://github.com/yuri-moens/chaos-plugins/issues","provider":"ChaosEnergy","name":"Chaos Prayer Flicking","description":"Flicks prayer","id":"chaosprayerflicking-plugin","releases":[{"date":"2022-01-03","sha512sum":"B4772480C72620CEB631C8A1BF01D9CB8661722C9726EFBA573A4D0350E4A84040E784FA0563B6335C879BDA099A511D7087408830C6BA71B71C4B030D9BF72F","version":"1.1.0","url":"https://github.com/yuri-moens/chaos-plugins/blob/master/release/prayerflick-1.1.0.jar?raw=true","requires":"^1.0.0"}]},{"projectUrl":"https://github.com/yuri-moens/chaos-plugins/issues","provider":"ChaosEnergy","name":"Chaos Util","description":"Utilities for Chaos scripts","id":"chaosutil-plugin","releases":[{"date":"2022-01-03","sha512sum":"C16A9452D68B52E001DC113F97ACE263930C50ADC85E631CE4F7FF867727C011B134D4DDD18B9931C5D096CE725E83E919C4BF260B3E68B911B1CA446076E4AC","version":"1.0.0","url":"https://github.com/yuri-moens/chaos-plugins/blob/master/release/util-1.0.0.jar?raw=true","requires":"^1.0.0"}]}] diff --git a/prayerflick/prayerflick.gradle.kts b/prayerflick/prayerflick.gradle.kts new file mode 100644 index 0000000..fc97da3 --- /dev/null +++ b/prayerflick/prayerflick.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.1.0" + +project.extra["PluginName"] = "Chaos Prayer Flicking" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Flicks prayer" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Config.java b/prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Config.java new file mode 100644 index 0000000..9b5e051 --- /dev/null +++ b/prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Config.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.prayerflick; + +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Keybind; + +import java.awt.event.KeyEvent; + +@ConfigGroup("ChaosPrayerflickConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "hotkey", + name = "Flick hotkey", + description = "When you press this key prayer flicking will start", + position = 0 + ) + default Keybind hotkey() { + return new Keybind(KeyEvent.VK_BACK_SLASH, 0); + } + + @ConfigItem( + keyName = "deactivateAfterStopping", + name = "Deactivate prayers after stopping", + description = "Deactivate prayers after stopping prayer flicking.", + position = 10 + ) + default boolean deactivateAfterStopping() { + return true; + } + + @ConfigItem( + keyName = "hotkeyMelee", + name = "Melee hotkey", + description = "When you press this key protect from melee will be set as quickprayer", + position = 20 + ) + default Keybind hotkeyMelee() { + return new Keybind(KeyEvent.VK_1, 0); + } + + @ConfigItem( + keyName = "hotkeyMissiles", + name = "Missiles hotkey", + description = "When you press this key protect from missiles will be set as quickprayer", + position = 21 + ) + default Keybind hotkeyMissiles() { + return new Keybind(KeyEvent.VK_2, 0); + } + + @ConfigItem( + keyName = "hotkeyMagic", + name = "Magic hotkey", + description = "When you press this key protect from magic will be set as quickprayer", + position = 22 + ) + default Keybind hotkeyMagic() { + return new Keybind(KeyEvent.VK_3, 0); + } + + @ConfigItem( + keyName = "openInventory", + name = "Open inventory", + description = "Open inventory after swapping quickprayers", + position = 23 + ) + default boolean openInventory() { return true; } +} \ No newline at end of file diff --git a/prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Prayerflick.java b/prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Prayerflick.java new file mode 100644 index 0000000..88f092d --- /dev/null +++ b/prayerflick/src/main/java/io/reisub/openosrs/prayerflick/Prayerflick.java @@ -0,0 +1,170 @@ +package io.reisub.openosrs.prayerflick; + +import com.google.inject.Provides; +import io.reisub.openosrs.util.Calculations; +import io.reisub.openosrs.util.Util; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.MenuAction; +import net.runelite.api.events.GameTick; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.input.KeyListener; +import net.runelite.client.input.KeyManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.game.Game; +import net.runelite.client.plugins.iutils.game.iWidget; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.awt.event.KeyEvent; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Prayer Flicking", + description = "", + enabledByDefault = false +) +@Slf4j +public class Prayerflick extends Plugin implements KeyListener { + @Inject + private Game game; + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private KeyManager keyManager; + + @Inject + private Config config; + + @Inject + private Calculations calc; + + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + private ScheduledExecutorService executor; + private boolean toggleFlicking; + private boolean firstFlick; + private boolean toggledOff; + + @Override + protected void startUp() { + log.info("Starting Chaos Prayer Flicking"); + + keyManager.registerKeyListener(this); + executor = Executors.newSingleThreadScheduledExecutor(); + } + + @Override + protected void shutDown() { + log.info("Stopping Chaos Prayer Flicking"); + + keyManager.unregisterKeyListener(this); + executor.shutdownNow(); + } + + @Subscribe + private void onGameTick(GameTick event) { + if (client == null || client.getGameState() == null || client.getGameState() != GameState.LOGGED_IN || executor == null) return; + + iWidget quickPrayers = game.widget(WidgetInfo.MINIMAP_QUICK_PRAYER_ORB); + if (quickPrayers == null) return; + + if (toggleFlicking && quickPrayers.actions()!= null) { + boolean active = quickPrayers.actions().get(0).equals("Deactivate"); + + if (!active && !firstFlick) { + toggle(calc.random(1, 15), quickPrayers); + return; + } + + toggle(calc.random(1, 9), quickPrayers); + toggle(calc.random(90, 100), quickPrayers); + + if (firstFlick) { + firstFlick = false; + } + } else if (!toggleFlicking && toggledOff && config.deactivateAfterStopping()) { + toggledOff = false; + toggle(calc.random(90, 110), quickPrayers); + } + } + + private void toggle(int delay, iWidget widget) { + executor.schedule(() -> { + widget.interact(0); + }, delay, TimeUnit.MILLISECONDS); + } + + @Override + public void keyTyped(KeyEvent e) {} + + @Override + public void keyPressed(KeyEvent e) { + if (config.hotkey().matches(e)) { + if (toggleFlicking) { + toggledOff = true; + } else { + firstFlick = true; + } + + toggleFlicking = !toggleFlicking; + } else if (config.hotkeyMelee().matches(e)) { + setPrayer(14); + } else if (config.hotkeyMissiles().matches(e)) { + setPrayer(13); + } else if (config.hotkeyMagic().matches(e)) { + setPrayer(12); + } + } + + @Override + public void keyReleased(KeyEvent e) {} + + private void setPrayer(int childId) { + executor.schedule(() -> { + iWidget quickPrayers = game.widget(WidgetInfo.MINIMAP_QUICK_PRAYER_ORB); + if (quickPrayers == null) return; + + quickPrayers.interact(1); + game.waitUntil(() -> { + iWidget w = game.widget(77, 4); + return w != null && !w.hidden(); + }); + + iWidget protection = game.widget(77, 4, childId); + if (protection == null) return; + + protection.interact(0); + + iWidget update = game.widget(77, 5); + if (update == null) return; + + update.interact(0); + + if (config.openInventory()) { + game.openInterface(3); + } + }, 0, TimeUnit.MILLISECONDS); + } +} \ No newline at end of file diff --git a/release/agility-0.0.1.jar b/release/agility-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..3c601ae0eacb0dbc010b4fac5ac1289565d33e5b GIT binary patch literal 13052 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 literal 0 HcmV?d00001 diff --git a/release/autodropper-1.0.0.jar b/release/autodropper-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..426703e404676e6737b123624026edb93e8da83a GIT binary patch literal 4474 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 literal 0 HcmV?d00001 diff --git a/release/birdhouse-1.0.0.jar b/release/birdhouse-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..7b87b6a9ee08e490188497289188cb5d5909cc79 GIT binary patch literal 32708 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

C8>12C@-ikF~&`7>NN_p`NdvFV^nnwX<1|| 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 J9DjZK{{Zb!;YI)e literal 0 HcmV?d00001 diff --git a/release/blackjack-0.0.1.jar b/release/blackjack-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..d5f268250e41c7ebfbec2a2536a4be92ab72c858 GIT binary patch literal 8818 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 DmRRVW literal 0 HcmV?d00001 diff --git a/release/consume-0.0.1.jar b/release/consume-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..a08e17aef7229e9ec1b49e6b65d9db61b6f9828b GIT binary patch literal 9613 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 OI~(7UgrxoB+y4QrVyo-` literal 0 HcmV?d00001 diff --git a/release/cooker-0.0.1.jar b/release/cooker-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..93a4ab552ae488875fc653ab94024f0e0eaf770a GIT binary patch literal 9881 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 AmH+?% literal 0 HcmV?d00001 diff --git a/release/fisher-0.0.1.jar b/release/fisher-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..9b13c9ad101ceb984144417767f7ed7b51d7e098 GIT binary patch literal 10262 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 literal 0 HcmV?d00001 diff --git a/release/masterthiever-1.0.0.jar b/release/masterthiever-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..fb7e3d5ffebd11009e06bc3c875b59608aa23878 GIT binary patch literal 7252 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 P_*YllRRAN%zrXz-dOto* literal 0 HcmV?d00001 diff --git a/release/miner-1.0.0.jar b/release/miner-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..143df8a5048399ff15832c14250470d8763d309e GIT binary patch literal 24728 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 z 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 literal 0 HcmV?d00001 diff --git a/release/prayerflick-1.0.0.jar b/release/prayerflick-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..b51c71e3e6b973b0450b99e008a328c7ad764b73 GIT binary patch literal 5134 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 literal 0 HcmV?d00001 diff --git a/release/prayerflick-1.1.0.jar b/release/prayerflick-1.1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..208a3babb011f8e9d9f24ec62252248c8eecf96f GIT binary patch literal 5186 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 literal 0 HcmV?d00001 diff --git a/release/util-1.0.0.jar b/release/util-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..c9e824a330eaf6ad739e52e56272cfe1b5ad3016 GIT binary patch literal 21818 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 literal 0 HcmV?d00001 diff --git a/release/wintertodt-0.0.1.jar b/release/wintertodt-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..412b7dcff09434629dda88449f54f2a0ea4834fc GIT binary patch literal 46415 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 literal 0 HcmV?d00001 diff --git a/release/woodcutter-0.0.1.jar b/release/woodcutter-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..16b77c1a6acd74d43c156d82c4b545ca44e20e59 GIT binary patch literal 6136 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 literal 0 HcmV?d00001 diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..b1be5a9 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +rootProject.name = "Chaos Plugins" + +include(":agility") +include(":autobones") +include(":autodropper") +include(":birdhouse") +include(":blackjack") +include(":consume") +include(":cooker") +include(":fisher") +include(":glassblower") +include(":masterthiever") +include(":miner") +include(":prayerflick") +include(":shopper") +include(":smelter") +include(":tempoross") +include(":test") +include(":util") +include(":wintertodt") +include(":woodcutter") + +for (project in rootProject.children) { + project.apply { + projectDir = file(name) + buildFileName = "$name.gradle.kts" + + require(projectDir.isDirectory) { "Project '${project.path} must have a $projectDir directory" } + require(buildFile.isFile) { "Project '${project.path} must have a $buildFile build script" } + } +} diff --git a/shopper/shopper.gradle.kts b/shopper/shopper.gradle.kts new file mode 100644 index 0000000..05494b6 --- /dev/null +++ b/shopper/shopper.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Shopper" +project.extra["PluginDescription"] = "Hops worlds and buys stuff from NPCs" + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/shopper/src/main/java/io/reisub/openosrs/shopper/Config.java b/shopper/src/main/java/io/reisub/openosrs/shopper/Config.java new file mode 100644 index 0000000..a2c66aa --- /dev/null +++ b/shopper/src/main/java/io/reisub/openosrs/shopper/Config.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.shopper; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigSection; + +@ConfigGroup("ChaosShopperConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "npcName", + name = "NPC Name", + description = "Name of the NPC to buy from", + position = 0 + ) + default String npcName() { + return ""; + } + + @ConfigSection( + keyName = "itemOne", + name = "Item 1", + description = "Configure the item to buy", + position = 10 + ) + String itemOne = "itemOne"; + + @ConfigItem( + keyName = "itemOneEnabled", + name = "Enable", + description = "Enable the buying of this item", + position = 11 + ) + default boolean itemOneEnabled() { + return true; + } + + @ConfigItem( + keyName = "itemOneName", + name = "Name", + description = "Name of the item", + position = 12 + ) + default String itemOneName() { + return ""; + } + + @ConfigItem( + keyName = "itemOneAmount", + name = "Amount", + description = "Amount of the item to buy", + position = 13 + ) + default int itemOneAmount() { + return 0; + } + + @ConfigItem( + keyName = "itemOneMinInStore", + name = "Min in store", + description = "Amount to keep in store to prevent overpaying", + position = 14 + ) + default int itemOneMinInStore() { + return 0; + } + + @ConfigSection( + keyName = "itemTwo", + name = "Item 2", + description = "Configure the item to buy", + closedByDefault = true, + position = 20 + ) + String itemTwo = "itemTwo"; + + @ConfigItem( + keyName = "itemTwoEnabled", + name = "Enable", + description = "Enable the buying of this item", + position = 21 + ) + default boolean itemTwoEnabled() { + return false; + } + + @ConfigItem( + keyName = "itemTwoName", + name = "Name", + description = "Name of the item", + position = 22 + ) + default String itemTwoName() { + return ""; + } + + @ConfigItem( + keyName = "itemTwoAmount", + name = "Amount", + description = "Amount of the item to buy", + position = 23 + ) + default int itemTwoAmount() { + return 0; + } + + @ConfigItem( + keyName = "itemTwoMinInStore", + name = "Min in store", + description = "Amount to keep in store to prevent overpaying", + position = 24 + ) + default int itemTwoMinInStore() { + return 0; + } + + @ConfigSection( + keyName = "itemThree", + name = "Item 3", + description = "Configure the item to buy", + closedByDefault = true, + position = 30 + ) + String itemThree = "itemThree"; + + @ConfigItem( + keyName = "itemThreeEnabled", + name = "Enable", + description = "Enable the buying of this item", + position = 31 + ) + default boolean itemThreeEnabled() { + return false; + } + + @ConfigItem( + keyName = "itemThreeName", + name = "Name", + description = "Name of the item", + position = 32 + ) + default String itemThreeName() { + return ""; + } + + @ConfigItem( + keyName = "itemThreeAmount", + name = "Amount", + description = "Amount of the item to buy", + position = 33 + ) + default int itemThreeAmount() { + return 0; + } + + @ConfigItem( + keyName = "itemThreeMinInStore", + name = "Min in store", + description = "Amount to keep in store to prevent overpaying", + position = 34 + ) + default int itemThreeMinInStore() { + return 0; + } + + @ConfigSection( + keyName = "itemFour", + name = "Item 4", + description = "Configure the item to buy", + closedByDefault = true, + position = 40 + ) + String itemFour = "itemFour"; + + @ConfigItem( + keyName = "itemFourEnabled", + name = "Enable", + description = "Enable the buying of this item", + position = 41 + ) + default boolean itemFourEnabled() { + return false; + } + + @ConfigItem( + keyName = "itemFourName", + name = "Name", + description = "Name of the item", + position = 42 + ) + default String itemFourName() { + return ""; + } + + @ConfigItem( + keyName = "itemFourAmount", + name = "Amount", + description = "Amount of the item to buy", + position = 43 + ) + default int itemFourAmount() { + return 0; + } + + @ConfigItem( + keyName = "itemFourMinInStore", + name = "Min in store", + description = "Amount to keep in store to prevent overpaying", + position = 44 + ) + default int itemFourMinInStore() { + return 0; + } + + @ConfigSection( + keyName = "itemFive", + name = "Item 5", + description = "Configure the item to buy", + closedByDefault = true, + position = 50 + ) + String itemFive = "itemFive"; + + @ConfigItem( + keyName = "itemFiveEnabled", + name = "Enable", + description = "Enable the buying of this item", + position = 51 + ) + default boolean itemFiveEnabled() { + return false; + } + + @ConfigItem( + keyName = "itemFiveName", + name = "Name", + description = "Name of the item", + position = 52 + ) + default String itemFiveName() { + return ""; + } + + @ConfigItem( + keyName = "itemFiveAmount", + name = "Amount", + description = "Amount of the item to buy", + position = 53 + ) + default int itemFiveAmount() { + return 0; + } + + @ConfigItem( + keyName = "itemFiveMinInStore", + name = "Min in store", + description = "Amount to keep in store to prevent overpaying", + position = 54 + ) + default int itemFiveMinInStore() { + return 0; + } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/shopper/src/main/java/io/reisub/openosrs/shopper/Item.java b/shopper/src/main/java/io/reisub/openosrs/shopper/Item.java new file mode 100644 index 0000000..deba6bf --- /dev/null +++ b/shopper/src/main/java/io/reisub/openosrs/shopper/Item.java @@ -0,0 +1,16 @@ +package io.reisub.openosrs.shopper; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class Item { + @Getter + private final String name; + + @Getter + private final int amount; + + @Getter + private final int minInShop; +} diff --git a/shopper/src/main/java/io/reisub/openosrs/shopper/Shopper.java b/shopper/src/main/java/io/reisub/openosrs/shopper/Shopper.java new file mode 100644 index 0000000..0d086d7 --- /dev/null +++ b/shopper/src/main/java/io/reisub/openosrs/shopper/Shopper.java @@ -0,0 +1,109 @@ +package io.reisub.openosrs.shopper; + +import com.google.inject.Provides; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.mixins.Inject; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Shopper", + description = "Hops worlds and buys stuff from NPCs", + enabledByDefault = false +) +@Slf4j +public class Shopper extends iScript { + @Inject + private Config config; + + private List tasks; + + @Getter + private List items; + + @Getter + @Setter + private boolean hop; + + @Provides + @SuppressWarnings("unused") + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Shopper"); + + loadItems(); + + tasks = new ArrayList<>(); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Shopper"); + if (tasks != null) { + tasks.clear(); + } + } + + @Subscribe + @SuppressWarnings("unused") + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + private void loadItems() { + if (config.itemOneEnabled()) { + items.add(new Item(config.itemOneName(), config.itemOneAmount(), config.itemOneMinInStore())); + } + + if (config.itemTwoEnabled()) { + items.add(new Item(config.itemTwoName(), config.itemTwoAmount(), config.itemTwoMinInStore())); + } + + if (config.itemThreeEnabled()) { + items.add(new Item(config.itemThreeName(), config.itemThreeAmount(), config.itemThreeMinInStore())); + } + + if (config.itemFourEnabled()) { + items.add(new Item(config.itemFourName(), config.itemFourAmount(), config.itemFourMinInStore())); + } + + if (config.itemFiveEnabled()) { + items.add(new Item(config.itemFiveName(), config.itemFiveAmount(), config.itemFiveMinInStore())); + } + } +} \ No newline at end of file diff --git a/shopper/src/main/java/io/reisub/openosrs/shopper/tasks/Buy.java b/shopper/src/main/java/io/reisub/openosrs/shopper/tasks/Buy.java new file mode 100644 index 0000000..0c4579c --- /dev/null +++ b/shopper/src/main/java/io/reisub/openosrs/shopper/tasks/Buy.java @@ -0,0 +1,31 @@ +package io.reisub.openosrs.shopper.tasks; + +import io.reisub.openosrs.shopper.Shopper; +import io.reisub.openosrs.util.Task; +import net.runelite.api.mixins.Inject; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.iutils.game.iWidget; + +public class Buy extends Task { + @Inject + private Shopper plugin; + + @Override + public String getStatus() { + return "Buying"; + } + + @Override + public boolean validate() { + iWidget shop = game.widget(WidgetInfo.SHOP_ITEMS_CONTAINER); + + return shop != null + && !shop.hidden() + && !game.inventory().full(); + } + + @Override + public void execute() { + + } +} diff --git a/smelter/smelter.gradle.kts b/smelter/smelter.gradle.kts new file mode 100644 index 0000000..e635e70 --- /dev/null +++ b/smelter/smelter.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Smelter" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Hot stuff" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/Activity.java b/smelter/src/main/java/io/reisub/openosrs/smelter/Activity.java new file mode 100644 index 0000000..beadc35 --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/Activity.java @@ -0,0 +1,6 @@ +package io.reisub.openosrs.smelter; + +public enum Activity { + IDLE, + SMELTING; +} diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/Config.java b/smelter/src/main/java/io/reisub/openosrs/smelter/Config.java new file mode 100644 index 0000000..e6b8217 --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/Config.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.smelter; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosSmelterConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "targetProduct", + name = "Smelt", + description = "Choose what to smelt.", + position = 0 + ) + default Product targetProduct() { return Product.MOLTEN_GLASS; } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/Ingredient.java b/smelter/src/main/java/io/reisub/openosrs/smelter/Ingredient.java new file mode 100644 index 0000000..b56b545 --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/Ingredient.java @@ -0,0 +1,11 @@ +package io.reisub.openosrs.smelter; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class Ingredient { + private final int id; + private final int amount; +} diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/Product.java b/smelter/src/main/java/io/reisub/openosrs/smelter/Product.java new file mode 100644 index 0000000..810c636 --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/Product.java @@ -0,0 +1,18 @@ +package io.reisub.openosrs.smelter; + +import lombok.Getter; +import net.runelite.api.ItemID; + +@Getter +public enum Product { + MOLTEN_GLASS(ItemID.MOLTEN_GLASS, new Ingredient(ItemID.SODA_ASH, 14), new Ingredient(ItemID.BUCKET_OF_SAND, 14)), + CANNONBALLS(ItemID.CANNONBALL, new Ingredient(ItemID.STEEL_BAR, 27)); + + private final int id; + private final Ingredient[] ingredients; + + Product(int id, Ingredient... ingredients) { + this.id = id; + this.ingredients = ingredients; + } +} diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/Smelter.java b/smelter/src/main/java/io/reisub/openosrs/smelter/Smelter.java new file mode 100644 index 0000000..102a61d --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/Smelter.java @@ -0,0 +1,161 @@ +package io.reisub.openosrs.smelter; + +import com.google.inject.Provides; + +import io.reisub.openosrs.smelter.tasks.HandleBank; +import io.reisub.openosrs.smelter.tasks.Smelt; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Eat; +import io.reisub.openosrs.util.tasks.KittenTask; +import io.reisub.openosrs.util.tasks.Run; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.ChatMessageType; +import net.runelite.api.GameState; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import static net.runelite.api.AnimationID.IDLE; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Smelter", + description = "Hot stuff", + enabledByDefault = false +) +@Slf4j +public class Smelter extends iScript { + private List tasks; + private KittenTask kittenTask; + + @Getter + private Activity currentActivity; + + private Instant lastActionTime; + + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + checkActionTimeout(); + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Smelter"); + + Eat eatTask = injector.getInstance(Eat.class); + eatTask.setInterval(14, 24); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + kittenTask = KittenTask.getInstance(injector); + + currentActivity = Activity.IDLE; + + tasks = new ArrayList<>(); + tasks.add(eatTask); + tasks.add(runTask); + tasks.add(kittenTask); + tasks.add(injector.getInstance(HandleBank.class)); + tasks.add(injector.getInstance(Smelt.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Smelter"); + if (tasks != null) { + tasks.clear(); + } + + KittenTask.handleKitten = false; + } + + @SuppressWarnings("unused") + @Subscribe + private void onAnimationChanged(AnimationChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + if (event.getActor() != game.client().getLocalPlayer()) return; + + + int animId = game.localPlayer().animation(); + switch (animId) { + case AnimationID.SMITHING_SMELTING: + case AnimationID.SMITHING_CANNONBALL: + setActivity(Activity.SMELTING); + break; + } + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (kittenTask != null) { + kittenTask.onChatMessage(chatMessage); + } + + if (chatMessage.getType() == ChatMessageType.GAMEMESSAGE) { + if (chatMessage.getMessage().startsWith("Congratulations, you've just advanced your")) { + setActivity(Activity.IDLE); + } + } + } + + private void setActivity(Activity action) { + currentActivity = action; + + if (action != Activity.IDLE) { + lastActionTime = Instant.now(); + } + } + + private void checkActionTimeout() { + if (currentActivity == Activity.IDLE) return; + + int animId = game.localPlayer().animation(); + if (animId != IDLE || lastActionTime == null) return; + + Duration timeout = Duration.ofSeconds(5); + Duration sinceAction = Duration.between(lastActionTime, Instant.now()); + + if (sinceAction.compareTo(timeout) >= 0) { + setActivity(Activity.IDLE); + } + } +} \ No newline at end of file diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/tasks/HandleBank.java b/smelter/src/main/java/io/reisub/openosrs/smelter/tasks/HandleBank.java new file mode 100644 index 0000000..975d758 --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/tasks/HandleBank.java @@ -0,0 +1,67 @@ +package io.reisub.openosrs.smelter.tasks; + +import io.reisub.openosrs.smelter.Config; +import io.reisub.openosrs.smelter.Ingredient; +import io.reisub.openosrs.smelter.Product; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; + +public class HandleBank extends Task { + @Inject + private Config config; + + private Instant lastBanking = Instant.EPOCH; + + @Override + public String getStatus() { + return "Banking"; + } + + @Override + public boolean validate() { + boolean hasAllIngredients = true; + + for (Ingredient ingredient : config.targetProduct().getIngredients()) { + if (!game.inventory().withId(ingredient.getId()).exists()) { + hasAllIngredients = false; + } + } + + return !hasAllIngredients + && game.localPlayer().isIdle() + && Duration.between(lastBanking, Instant.now()).getSeconds() > 5; + } + + @Override + public void execute() { + if (!bank.isOpen()) { + iObject bankObj = game.objects().withName("Bank booth").nearest(); + if (bankObj == null) return; + + bankObj.interact("Bank"); + game.waitUntil(() -> bank.isOpen(), 15); + } + + if (config.targetProduct() == Product.CANNONBALLS) { + bank.depositExcept(false, ItemID.AMMO_MOULD); + } else { + bank.depositInventory(); + } + game.tick(); + game.sleepDelay(); + + for (Ingredient ingredient : config.targetProduct().getIngredients()) { + bank.withdraw(ingredient.getId(), ingredient.getAmount(), false); + game.sleepDelay(); + } + + bank.close(); + game.sleepDelay(); + lastBanking = Instant.now(); + } +} diff --git a/smelter/src/main/java/io/reisub/openosrs/smelter/tasks/Smelt.java b/smelter/src/main/java/io/reisub/openosrs/smelter/tasks/Smelt.java new file mode 100644 index 0000000..e6b4e4d --- /dev/null +++ b/smelter/src/main/java/io/reisub/openosrs/smelter/tasks/Smelt.java @@ -0,0 +1,58 @@ +package io.reisub.openosrs.smelter.tasks; + +import io.reisub.openosrs.smelter.Activity; +import io.reisub.openosrs.smelter.Config; +import io.reisub.openosrs.smelter.Ingredient; +import io.reisub.openosrs.smelter.Smelter; +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import javax.inject.Inject; + +public class Smelt extends Task { + @Inject + private Smelter plugin; + + @Inject + private Config config; + + @Override + public String getStatus() { + return "Smelting"; + } + + @Override + public boolean validate() { + boolean hasAllIngredients = true; + + for (Ingredient ingredient : config.targetProduct().getIngredients()) { + if (!game.inventory().withId(ingredient.getId()).exists()) { + hasAllIngredients = false; + } + } + + return hasAllIngredients && plugin.getCurrentActivity() == Activity.IDLE; + } + + @Override + public void execute() { + iObject furnace = game.objects().withName("Furnace").nearest(); + if (furnace == null) return; + + furnace.interact("Smelt"); + game.waitUntil(() -> chatbox.chatState() == Chatbox.ChatState.MAKE, 15); + + int quantity = 28; + for (Ingredient ingredient : config.targetProduct().getIngredients()) { + int count = (int) game.inventory().withId(ingredient.getId()).count(); + + if (quantity > count) { + quantity = count; + } + } + + chatbox.make(0, quantity); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.SMELTING, 5); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/Activity.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/Activity.java new file mode 100644 index 0000000..3fe731b --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/Activity.java @@ -0,0 +1,20 @@ +package io.reisub.openosrs.tempoross; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum Activity { + IDLE("Idle"), + FISHING("Fishing"), + COOKING("Cooking"), + ATTACKING("Attacking"), + DOUSING_FIRE("Dousing"), + REPAIRING_MAST("Repairing"), + TETHERING_MAST("Tethering"), + STOCKING_CANNON("Stocking"), + FILLING_BUCKETS("Filling"); + + private final String actionString; +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/Config.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/Config.java new file mode 100644 index 0000000..7662cd9 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/Config.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.tempoross; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosTemporossConfig") + +public interface Config extends net.runelite.client.config.Config { + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/Tempoross.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/Tempoross.java new file mode 100644 index 0000000..98afd5b --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/Tempoross.java @@ -0,0 +1,368 @@ +package io.reisub.openosrs.tempoross; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Provides; + +import io.reisub.openosrs.tempoross.tasks.*; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Run; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.*; +import net.runelite.api.events.*; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.game.iWidget; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static net.runelite.api.AnimationID.IDLE; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Tempoross", + description = "Plays the Tempoross minigame", + enabledByDefault = false +) +@Slf4j +public class Tempoross extends iScript { + private static final String WAVE_INCOMING_MESSAGE = "a colossal wave closes in..."; + private static final String WAVE_END_SAFE = "as the wave washes over you"; + private static final String WAVE_END_DANGEROUS = "the wave slams into you"; + private static final String TEMPOROSS_VULNERABLE_MESSAGE = "tempoross is vulnerable"; + + private static final int VARB_IS_TETHERED = 11895; + + private static final int TEMPOROSS_REGION = 12078; + private static final int UNKAH_REWARD_POOL_REGION = 12588; + private static final int UNKAH_BOAT_REGION = 12332; + + private static final int FIRE_SPREAD_MILLIS = 24000; + private static final int FIRE_SPAWN_MILLIS = 9600; + private static final int FIRE_SPREADING_SPAWN_MILLIS = 1200; + private static final int WAVE_IMPACT_MILLIS = 7800; + + public static final int FIRE_ID = 37582; + public static final int DOUSING_FIRE_ANIM_ID = 2771; + + private final Set TEMPOROSS_GAMEOBJECTS = ImmutableSet.of( + FIRE_ID, NullObjectID.NULL_41006, NullObjectID.NULL_41007, NullObjectID.NULL_41352, + NullObjectID.NULL_41353, NullObjectID.NULL_41354, NullObjectID.NULL_41355, ObjectID.DAMAGED_MAST_40996, + ObjectID.DAMAGED_MAST_40997, ObjectID.DAMAGED_TOTEM_POLE, ObjectID.DAMAGED_TOTEM_POLE_41011); + + //41006 = shadow before fire is burning + //41007 = shadow just before fire is jumping over to a next spot + //41354/41355 = a totem to grapple on to + //41352/41353 = a mast to grapple on to + //41010/41011 = a totem that is broken + //40996/40997 = a broken mast + + @Getter + private final Map temporossObjects = new HashMap<>(); + + @Getter + private boolean waveIncoming; + + @Getter + private int phase = 1; + + @Getter + private int playersReady; + + @Getter + private int energy; + + @Getter + private int essence; + + @Getter + private int stormIntensity; + + @Getter + private Activity currentActivity; + + @Getter + private Instant lastActionTime; + + private List tasks; + + @SuppressWarnings("unused") + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Tempoross"); + + Run runTask = injector.getInstance(Run.class); + runTask.setInterval(70, 95); + + tasks = new ArrayList<>(); + tasks.add(runTask); + tasks.add(injector.getInstance(EnterBoat.class)); + tasks.add(injector.getInstance(FillBuckets.class)); + tasks.add(injector.getInstance(LeaveBoat.class)); + tasks.add(injector.getInstance(Repair.class)); + tasks.add(injector.getInstance(Tether.class)); + tasks.add(injector.getInstance(Attack.class)); + tasks.add(injector.getInstance(Stock.class)); + tasks.add(injector.getInstance(Fish.class)); + tasks.add(injector.getInstance(Cook.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Tempoross"); + if (tasks != null) { + tasks.clear(); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onGameTick(GameTick event) { + if (isInDesert() || isOnBoat()) { + playersReady = parseWidget(687, 3); + } else if (isInTemperossArea()) { + energy = parseWidget(437, 35); + essence = parseWidget(437, 45); + stormIntensity = parseWidget(437, 55); + } + + checkActionTimeout(); + } + + @SuppressWarnings("unused") + @Subscribe + private void onGameObjectSpawned(GameObjectSpawned event) { + GameObject gameObject = event.getGameObject(); + + if (!TEMPOROSS_GAMEOBJECTS.contains(gameObject.getId())) return; + + TemporossObject temporossObject; + + switch (event.getGameObject().getId()) { + case FIRE_ID: + temporossObject = new TemporossObject(gameObject, Instant.now(), FIRE_SPREAD_MILLIS); + break; + case NullObjectID.NULL_41006: + temporossObject = new TemporossObject(gameObject, Instant.now(), FIRE_SPAWN_MILLIS); + break; + case NullObjectID.NULL_41007: + temporossObject = new TemporossObject(gameObject, Instant.now(), FIRE_SPREADING_SPAWN_MILLIS); + break; + default: + temporossObject = new TemporossObject(gameObject, Instant.now(), WAVE_IMPACT_MILLIS); + } + + temporossObjects.put(gameObject, temporossObject); + } + + @SuppressWarnings("unused") + @Subscribe + private void onGameObjectDespawned(GameObjectDespawned gameObjectDespawned) { + int id = gameObjectDespawned.getGameObject().getId(); + + if (id == FIRE_ID) { + if (currentActivity == Activity.DOUSING_FIRE) { + setActivity(Activity.IDLE); + } + } + + Set brokenMastsTotems = ImmutableSet.of( + ObjectID.DAMAGED_MAST_40996, + ObjectID.DAMAGED_MAST_40997, + ObjectID.DAMAGED_TOTEM_POLE, + ObjectID.DAMAGED_TOTEM_POLE_41011 + ); + + if (brokenMastsTotems.contains(id)) { + if (currentActivity == Activity.REPAIRING_MAST) { + setActivity(Activity.IDLE); + } + } + + temporossObjects.remove(gameObjectDespawned.getGameObject()); + } + + @SuppressWarnings("unused") + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (chatMessage.getType() != ChatMessageType.GAMEMESSAGE) return; + + String message = chatMessage.getMessage().toLowerCase(); + if (message.contains(WAVE_INCOMING_MESSAGE)) { + waveIncoming = true; + } else if (message.contains(WAVE_END_SAFE) || message.contains(WAVE_END_DANGEROUS)) { + if (message.contains(WAVE_END_SAFE)) { + setActivity(Activity.IDLE); + } + waveIncoming = false; + } else if (message.contains(TEMPOROSS_VULNERABLE_MESSAGE)) { + phase++; + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onAnimationChanged(AnimationChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN) return; + + if (event.getActor() != game.client().getLocalPlayer()) return; + + int animId = game.localPlayer().animation(); + switch (animId) { + case AnimationID.FISHING_BAREHAND: + case AnimationID.FISHING_BAREHAND_WINDUP_1: + case AnimationID.FISHING_BAREHAND_WINDUP_2: + setActivity(Activity.FISHING); + case AnimationID.COOKING_RANGE: + iObject shrine = game.objects().withName("Shrine").nearest(); + if (shrine != null && shrine.position().distanceTo(game.localPlayer().position()) <= 3) { + setActivity(Activity.COOKING); + } else { + setActivity(Activity.STOCKING_CANNON); + } + case DOUSING_FIRE_ANIM_ID: + setActivity(Activity.DOUSING_FIRE); + case AnimationID.CONSTRUCTION: + case AnimationID.CONSTRUCTION_IMCANDO: + setActivity(Activity.REPAIRING_MAST); + case AnimationID.USING_GILDED_ALTAR: + setActivity(Activity.TETHERING_MAST); + case AnimationID.LOOKING_INTO: + setActivity(Activity.FILLING_BUCKETS); + case 0: + setActivity(Activity.ATTACKING); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onItemContainerChanged(ItemContainerChanged event) { + final ItemContainer container = event.getItemContainer(); + + int rawFish = (int) game.inventory().withId(ItemID.RAW_HARPOONFISH).count(); + int cookedFish = (int) game.inventory().withId(ItemID.HARPOONFISH).count(); + int emptyBuckets = (int) game.inventory().withId(ItemID.BUCKET).count(); + + if (rawFish == 0 && currentActivity == Activity.COOKING) { + setActivity(Activity.IDLE); + } else if (game.inventory().full() && currentActivity == Activity.FISHING) { + setActivity(Activity.IDLE); + } else if (cookedFish == 0 && currentActivity == Activity.STOCKING_CANNON) { + setActivity(Activity.IDLE); + } else if (emptyBuckets == 0 && currentActivity == Activity.FILLING_BUCKETS) { + setActivity(Activity.IDLE); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onNpcSpawned(NpcSpawned npcSpawned) { + if (npcSpawned.getNpc().getId() == NpcID.FISHING_SPOT_10569 + && !game.inventory().full() + && (currentActivity == Activity.COOKING || currentActivity == Activity.FISHING)) { + setActivity(Activity.IDLE); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onVarbitChanged(VarbitChanged event) { + if (game.client().getVarbitValue(VARB_IS_TETHERED) > 0) { + setActivity(Activity.TETHERING_MAST); + } + + if (game.client().getVarbitValue(VARB_IS_TETHERED) <= 0 && currentActivity == Activity.TETHERING_MAST) { + setActivity(Activity.IDLE); + } + } + + public boolean isOnBoat() { + return game.localPlayer().position().regionID() == UNKAH_BOAT_REGION; + } + + public boolean isInDesert() { + return game.localPlayer().position().regionID() == UNKAH_REWARD_POOL_REGION; + } + + public boolean isInTemperossArea() { + return game.localPlayer().position().regionID() == TEMPOROSS_REGION; + } + + private int parseWidget(int group, int file) { + iWidget widget = game.widget(group, file); + if (widget == null || widget.text().equals("")) return 0; + + Pattern regex = Pattern.compile("\\d+|None"); + Matcher matcher = regex.matcher(widget.text()); + + if (matcher.find()) { + String match = matcher.group(0); + if (match.equals("None")) return 0; + + return Integer.parseInt(match); + } + + return 0; + } + + private void setActivity(Activity action) { + currentActivity = action; + + if (action != Activity.IDLE) { + lastActionTime = Instant.now(); + } + } + + private void checkActionTimeout() { + if (currentActivity == Activity.IDLE) return; + + int animId = game.localPlayer().animation(); + if (animId != IDLE || lastActionTime == null) return; + + Duration timeout = Duration.ofSeconds(3); + Duration sinceAction = Duration.between(lastActionTime, Instant.now()); + + if (sinceAction.compareTo(timeout) >= 0) { + setActivity(Activity.IDLE); + } + } +} \ No newline at end of file diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/TemporossObject.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/TemporossObject.java new file mode 100644 index 0000000..f884281 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/TemporossObject.java @@ -0,0 +1,15 @@ +package io.reisub.openosrs.tempoross; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.GameObject; + +import java.time.Instant; + +@AllArgsConstructor +@Getter +public class TemporossObject { + private GameObject object; + private Instant start; + private long duration; +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Attack.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Attack.java new file mode 100644 index 0000000..9318afe --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Attack.java @@ -0,0 +1,33 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Activity; +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; + +import javax.inject.Inject; + +public class Attack extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Attacking"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea()) return false; + + return plugin.getCurrentActivity() == Activity.IDLE + && game.npcs().withId().exists(); + } + + @Override + public void execute() { + game.npcs().withId().findFirst().ifPresent((pool) -> { + pool.interact(0); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.ATTACKING, 20); + }); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Cook.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Cook.java new file mode 100644 index 0000000..df6ca5a --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Cook.java @@ -0,0 +1,41 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Activity; +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +public class Cook extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Cooking"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea()) return false; + + if (!game.inventory().withId(ItemID.RAW_HARPOONFISH).exists()) return false; + + if (plugin.getCurrentActivity() == Activity.IDLE) { + return true; + } + + return false; + } + + @Override + public void execute() { + iObject shrine = game.objects().withName("Shrine").nearest(); + if (shrine == null) return; + + shrine.interact("Cook-at"); + game.tick(); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/DouseFire.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/DouseFire.java new file mode 100644 index 0000000..ce63a4e --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/DouseFire.java @@ -0,0 +1,35 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +public class DouseFire extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Dousing fire"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea()) return false; + + return game.inventory().withId(ItemID.BUCKET_OF_WATER).exists() + && game.objects().withName("Fire").exists(); + } + + @Override + public void execute() { + iObject fire = game.objects().withName("Fire").nearest(); + if (fire == null) return; + + fire.interact("Douse"); + game.tick(); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/EnterBoat.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/EnterBoat.java new file mode 100644 index 0000000..a3aff05 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/EnterBoat.java @@ -0,0 +1,30 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; + +import javax.inject.Inject; + +public class EnterBoat extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Entering boat"; + } + + @Override + public boolean validate() { + return plugin.isInDesert() + && plugin.getPlayersReady() == 0; + } + + @Override + public void execute() { + game.objects().withName("Rope ladder").findFirst().ifPresent((ladder) -> { + ladder.interact("Climb"); + game.waitUntil(() -> plugin.isOnBoat(), 15); + }); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/FillBuckets.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/FillBuckets.java new file mode 100644 index 0000000..cf0383a --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/FillBuckets.java @@ -0,0 +1,38 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Activity; +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.api.ObjectID; + +import javax.inject.Inject; + +public class FillBuckets extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Filling buckets"; + } + + @Override + public boolean validate() { + if (!game.inventory().withId(ItemID.BUCKET).exists()) return false; + + long filledBuckets = game.inventory().withId(ItemID.BUCKET_OF_WATER).count(); + // TODO limit to play area + long fires = game.objects().withName("Fire").withAction("Douse").count(); + + return plugin.isOnBoat() || (plugin.isInTemperossArea() && filledBuckets < fires); + } + + @Override + public void execute() { + game.objects().withName("Water pump").findFirst().ifPresent((pump) -> { + pump.interact("Use"); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.FILLING_BUCKETS, 20); + }); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Fish.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Fish.java new file mode 100644 index 0000000..b3b7d06 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Fish.java @@ -0,0 +1,69 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Activity; +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; +import net.runelite.api.NpcID; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +public class Fish extends Task { + @Inject + private Tempoross plugin; + + private final int normalSpotID = NpcID.FISHING_SPOT_10565; + private final int doubleSpotID = NpcID.FISHING_SPOT_10569; + + @Override + public String getStatus() { + return "Fishing"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea() || game.inventory().full()) return false; + + iNPC normalSpot = game.npcs().withId(normalSpotID).nearest(); + iNPC doubleSpot = game.npcs().withId(doubleSpotID).nearest(); + + // interrupt cooking when double spot exists + if (plugin.getCurrentActivity() == Activity.COOKING + && doubleSpot != null) { + return true; + } + + // change from normal spot to double spot + Position playerPos = game.localPlayer().position(); + if (plugin.getCurrentActivity() == Activity.FISHING + && doubleSpot != null + && doubleSpot.position().distanceTo(playerPos) > normalSpot.position().distanceTo(playerPos)) { + return true; + } + + // fish when idle and low on raw harpoonfish + if (plugin.getCurrentActivity() == Activity.IDLE + && game.inventory().withId(ItemID.RAW_HARPOONFISH).count() <= 8) { + return true; + } + + return false; + } + + @Override + public void execute() { + iObject spot = game.objects().withId(doubleSpotID).nearest(); + + if (spot == null) { + spot = game.objects().withId(normalSpotID).nearest(); + } + + if (spot == null) return; + + spot.interact(0); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.FISHING); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/LeaveBoat.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/LeaveBoat.java new file mode 100644 index 0000000..9abe118 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/LeaveBoat.java @@ -0,0 +1,30 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; + +import javax.inject.Inject; + +public class LeaveBoat extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Leaving boat"; + } + + @Override + public boolean validate() { + return plugin.isOnBoat() + && plugin.getPlayersReady() > 1; + } + + @Override + public void execute() { + game.objects().withName("Rope ladder").findFirst().ifPresent((ladder) -> { + ladder.interact("Climb"); + game.waitUntil(() -> plugin.isInDesert(), 15); + }); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Repair.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Repair.java new file mode 100644 index 0000000..c10e6d1 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Repair.java @@ -0,0 +1,41 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Activity; +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; +import net.runelite.api.NullObjectID; +import net.runelite.api.ObjectID; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +public class Repair extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Repairing"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea() || !plugin.isWaveIncoming()) return false; + + iObject tetherPole = game.objects().withId(NullObjectID.NULL_41352, NullObjectID.NULL_41353, NullObjectID.NULL_41354, NullObjectID.NULL_41355).nearest(); + iObject brokenPole = game.objects().withId(ObjectID.DAMAGED_MAST_40996, ObjectID.DAMAGED_MAST_40997, ObjectID.DAMAGED_TOTEM_POLE, ObjectID.DAMAGED_TOTEM_POLE_41011).nearest(); + Position playerPos = game.localPlayer().position(); + + return playerPos.distanceTo(tetherPole.position()) > playerPos.distanceTo(brokenPole.position()); + } + + @Override + public void execute() { + iObject brokenPole = game.objects().withId(ObjectID.DAMAGED_MAST_40996, ObjectID.DAMAGED_MAST_40997, ObjectID.DAMAGED_TOTEM_POLE, ObjectID.DAMAGED_TOTEM_POLE_41011).nearest(); + if (brokenPole == null) return; + + brokenPole.interact(0); + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.REPAIRING_MAST, 20); + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Stock.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Stock.java new file mode 100644 index 0000000..edf47bf --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Stock.java @@ -0,0 +1,64 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; +import net.runelite.api.ItemID; + +import javax.inject.Inject; + +public class Stock extends Task { + @Inject + private Tempoross plugin; + + private int stocked; + + @Override + public String getStatus() { + return "Stocking cannon"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea()) return false; + + long rawFish = game.inventory().withId(ItemID.RAW_HARPOONFISH).count(); + long cookedFish = game.inventory().withId(ItemID.HARPOONFISH).count(); + + @SuppressWarnings("unused") + long crystalFish = game.inventory().withId(ItemID.CRYSTALLISED_HARPOONFISH).count(); + + // stock cannon for the first time + // bring energy to 4% + if (plugin.getPhase() == 1 && cookedFish == 17 && plugin.getEnergy() == 100) return true; + + // stock cannon for the second time + // trigger first phase change + // bring energy to 4% or as close as possible + if (plugin.getPhase() == 1 && plugin.getEnergy() < 100 + && (plugin.getStormIntensity() >= 93 || cookedFish == 19)) return true; + + // stock cannon at phase 2 + // bring energy to 4% + if (plugin.getPhase() == 2 + && plugin.getEnergy() != 4 + && cookedFish >= 19 - stocked) return true; + + // stock cannon at phase 2 + // trigger second phase change + // bring energy to 4% + if (plugin.getPhase() == 2 + && (plugin.getStormIntensity() >= 93 || cookedFish == 19)) return true; + + // stock cannon at phase 3 + // trigger third phase change and potentially fourth phase change + if (plugin.getPhase() == 3 + && (plugin.getStormIntensity() >= 93 || (cookedFish > 0 && rawFish == 0 && game.inventory().full()))) return true; + + return false; + } + + @Override + public void execute() { + + } +} diff --git a/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Tether.java b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Tether.java new file mode 100644 index 0000000..7867ff3 --- /dev/null +++ b/tempoross/src/main/java/io/reisub/openosrs/tempoross/tasks/Tether.java @@ -0,0 +1,29 @@ +package io.reisub.openosrs.tempoross.tasks; + +import io.reisub.openosrs.tempoross.Activity; +import io.reisub.openosrs.tempoross.Tempoross; +import io.reisub.openosrs.util.Task; + +import javax.inject.Inject; + +public class Tether extends Task { + @Inject + private Tempoross plugin; + + @Override + public String getStatus() { + return "Tethering"; + } + + @Override + public boolean validate() { + if (!plugin.isInTemperossArea()) return false; + + return plugin.isWaveIncoming() && plugin.getCurrentActivity() != Activity.TETHERING_MAST; + } + + @Override + public void execute() { + + } +} diff --git a/tempoross/tempoross.gradle.kts b/tempoross/tempoross.gradle.kts new file mode 100644 index 0000000..4345ac4 --- /dev/null +++ b/tempoross/tempoross.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Tempoross" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Plays the Tempoross minigame" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/test/src/main/java/io/reisub/openosrs/test/TestConfig.java b/test/src/main/java/io/reisub/openosrs/test/TestConfig.java new file mode 100644 index 0000000..24ffb8d --- /dev/null +++ b/test/src/main/java/io/reisub/openosrs/test/TestConfig.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.test; + +import net.runelite.client.config.Button; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosTestConfig") + +public interface TestConfig extends Config { + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/test/src/main/java/io/reisub/openosrs/test/TestPlugin.java b/test/src/main/java/io/reisub/openosrs/test/TestPlugin.java new file mode 100644 index 0000000..11841ad --- /dev/null +++ b/test/src/main/java/io/reisub/openosrs/test/TestPlugin.java @@ -0,0 +1,107 @@ +package io.reisub.openosrs.test; + +import com.google.inject.Provides; +import io.reisub.openosrs.test.tasks.Test; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.*; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.game.iWidget; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Test", + description = "Plugin for testing", + enabledByDefault = false +) +@Slf4j +public class TestPlugin extends iScript { + private List tasks; + + @Provides + TestConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(TestConfig.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Test"); + + log.info("selected itemt slot: " + game.client.getSelectedItemSlot()); + log.info("is item selected: " + game.client().isItemSelected()); + + tasks = new ArrayList<>(); + tasks.add(injector.getInstance(Test.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Test"); + if (tasks != null) { + tasks.clear(); + } + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @Subscribe + private void onScriptPreFired(ScriptPreFired event) { +// int id = event.getScriptId(); +// if (id > 1000 || id == 900 || id == 100 || id == 44) return; +// if (event.getScriptEvent() == null) return; +// log.info("id: " + event.getScriptId()); + } + + @Subscribe + private void onAnimationChanged(AnimationChanged event) { +// if (event.getActor() != null) { +// log.info("actor: " + event.getActor().getName()); +// log.info("id: " + event.getActor().getAnimation()); +// } + } + + @Subscribe + private void onProjectileMoved(ProjectileMoved event) { +// if (event.getProjectile().getInteracting() != null) { +// log.info("projectile interacting: " + event.getProjectile().getInteracting().getName()); +// log.info("projectile id: " + event.getProjectile().getId()); +// } + } + + @Subscribe + private void onHitsplatApplied(HitsplatApplied event) { + + + } +} \ No newline at end of file diff --git a/test/src/main/java/io/reisub/openosrs/test/tasks/Test.java b/test/src/main/java/io/reisub/openosrs/test/tasks/Test.java new file mode 100644 index 0000000..2d400d0 --- /dev/null +++ b/test/src/main/java/io/reisub/openosrs/test/tasks/Test.java @@ -0,0 +1,36 @@ +package io.reisub.openosrs.test.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.GameState; +import net.runelite.client.plugins.iutils.scene.Position; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +public class Test extends Task { + private long last = System.currentTimeMillis(); + + @Override + public String getStatus() { + return "Running test task"; + } + + @Override + public boolean validate() { + //return false; + return System.currentTimeMillis() > last + 5000; + } + + @Override + public void execute() { + log("running test task"); + + last = System.currentTimeMillis(); + + log("state: " + chatbox.chatState()); + + if (chatbox.chatState() == Chatbox.ChatState.OPTIONS_CHAT) { + log("choosing option"); + chatbox.chooseOption(1); + game.tick(3); + } + } +} diff --git a/test/test.gradle.kts b/test/test.gradle.kts new file mode 100644 index 0000000..55ff219 --- /dev/null +++ b/test/test.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Test" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Plugin for testing" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/util/src/main/java/io/reisub/openosrs/util/CScript.java b/util/src/main/java/io/reisub/openosrs/util/CScript.java new file mode 100644 index 0000000..4a3d9b9 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/CScript.java @@ -0,0 +1,146 @@ +package io.reisub.openosrs.util; + +import io.reisub.openosrs.util.enums.Activity; +import io.reisub.openosrs.util.tasks.KittenTask; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.GameState; +import net.runelite.api.Skill; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.StatChanged; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.iutils.scripts.iScript; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; +import java.util.*; + +import static net.runelite.api.AnimationID.IDLE; + +@Slf4j +public abstract class CScript extends iScript { + @Inject + protected Calculations calc; + + protected final List tasks = new ArrayList<>(); + protected KittenTask kittenTask; + + @Getter + protected Activity currentActivity = Activity.IDLE; + + protected Instant lastLogin = Instant.EPOCH; + protected Instant lastActionTime = Instant.EPOCH; + protected Instant lastExperience = Instant.EPOCH; + protected final Map idleCheckSkills = new HashMap<>(); + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + checkActionTimeout(); + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting " + this.getName()); + kittenTask = KittenTask.getInstance(injector); + } + + @Override + protected void onStop() { + log.info("Stopping " + this.getName()); + tasks.clear(); + + KittenTask.handleKitten = false; + } + + protected final void addTask(Class type) { + tasks.add(injector.getInstance(type)); + } + + @SuppressWarnings("unused") + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked event) { + String name = this.getName().replaceAll(" ", "").toLowerCase(Locale.ROOT); + + if (event.getGroup().equals(name) && event.getKey().equals("startButton")) { + execute(); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onChatMessage(ChatMessage event) { + if (kittenTask != null) { + kittenTask.onChatMessage(event); + } + + if (event.getType() == ChatMessageType.GAMEMESSAGE) { + if (event.getMessage().startsWith("Congratulations, you've just advanced your")) { + setActivity(Activity.IDLE); + } + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onStatChanged(StatChanged event) { + for (Skill skill : idleCheckSkills.keySet()) { + if (event.getSkill() == skill) { + setActivity(idleCheckSkills.get(skill)); + lastExperience = Instant.now(); + } + } + } + + @Subscribe + @SuppressWarnings("unused") + private void onGameStateChanged(GameStateChanged event) { + if (event.getGameState() == GameState.LOGGED_IN) { + lastLogin = Instant.now(); + } + } + + protected void setActivity(Activity action) { + currentActivity = action; + + if (action != Activity.IDLE) { + lastActionTime = Instant.now(); + } + } + + protected void checkActionTimeout() { + if (currentActivity == Activity.IDLE) return; + + if (Duration.between(lastExperience, Instant.now()).getSeconds() < 5) return; + + int animId = game.localPlayer().animation(); + if (animId != IDLE || lastActionTime == null) return; + + Duration timeout = Duration.ofSeconds(5); + Duration sinceAction = Duration.between(lastActionTime, Instant.now()); + + if (sinceAction.compareTo(timeout) >= 0) { + setActivity(Activity.IDLE); + } + } + + public final boolean isLoggedIn() { + return game.client() != null && game.client().getGameState() == GameState.LOGGED_IN; + } + + public final boolean isLoggedInForLongerThan(int seconds) { + return Duration.between(lastLogin, Instant.now()).getSeconds() >= seconds; + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/Calculations.java b/util/src/main/java/io/reisub/openosrs/util/Calculations.java new file mode 100644 index 0000000..c65ea92 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/Calculations.java @@ -0,0 +1,95 @@ +package io.reisub.openosrs.util; + +import javax.inject.Singleton; +import java.util.Random; + +@Singleton +public class Calculations { + private final Random random = new Random(); + + /** + * Get a random number in the interval [min, max[. + * + * @param min inclusive minimum value + * @param max exclusive maximum value + * @return random number min <= n < max + */ + public int random(int min, int max) { + if (max <= min) return min; + return random.nextInt(max - min) + min; + } + + /** + * Get a random number in the interval [min, max[. + * + * @param min inclusive minimum value + * @param max exclusive maximum value + * @return random number min <= n < max + */ + public double random(double min, double max) { + if (max <= min) return min; + return min + random.nextDouble() * (max - min); + } + + /** + * Get a random Gaussian distributed number around a mean using deviation. + * + * @param mean mean value + * @param deviation deviation from the mean + * @param weighted if true the distribution will be weighted to the mean and right side (curve shifted to the right) + * @return random number min <= n <= max according to normal distribution + */ + public int randomGauss(int mean, int deviation, boolean weighted) { + double v; + + if (weighted) { + v = -Math.log(Math.abs(random.nextGaussian())) * deviation + mean; + } else { + v = random.nextGaussian() * deviation + mean; + } + + return (int) v; + } + + /** + * Get a random Gaussian distributed number around a mean using deviation. + * + * @param mean mean value + * @param deviation deviation from the mean + * @return random number min <= n <= max according to normal distribution + */ + public int randomGauss(int mean, int deviation) { + return randomGauss(mean, deviation, false); + } + + /** + * Get a random Gaussian distributed number within an interval [min, max] around a mean using deviation. + * + * @param min inclusive minimum value + * @param max inclusive maximum value + * @param mean mean value + * @param deviation deviation from the mean + * @param weighted if true the distribution will be weighted to the mean and right side (curve shifted to the right) + * @return random number min <= n <= max according to normal distribution + */ + public int randomGauss(int min, int max, int mean, int deviation, boolean weighted) { + return clamp(randomGauss(mean, deviation, weighted), min, max); + } + + /** + * Get a random Gaussian distributed number within an interval [min, max] around a mean using deviation. + * + * @param min inclusive minimum value + * @param max inclusive maximum value + * @param mean mean value + * @param deviation deviation from the mean + * @return random number min <= n <= max according to normal distribution + */ + public int randomGauss(int min, int max, int mean, int deviation) { + return randomGauss(min, max, deviation, mean, false); + } + + private int clamp(int v, int min, int max) { + return Math.max(min, Math.min(max, v)); + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/Config.java b/util/src/main/java/io/reisub/openosrs/util/Config.java new file mode 100644 index 0000000..064e7d5 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/Config.java @@ -0,0 +1,8 @@ +package io.reisub.openosrs.util; + +import net.runelite.client.config.*; + +@ConfigGroup("ChaosUtilConfig") +public interface Config extends net.runelite.client.config.Config { + +} diff --git a/util/src/main/java/io/reisub/openosrs/util/HopHelper.java b/util/src/main/java/io/reisub/openosrs/util/HopHelper.java new file mode 100644 index 0000000..a99ce67 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/HopHelper.java @@ -0,0 +1,67 @@ +package io.reisub.openosrs.util; + +import net.runelite.api.GameState; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.game.WorldService; +import net.runelite.client.plugins.iutils.game.Game; +import net.runelite.client.util.WorldUtil; +import net.runelite.http.api.worlds.World; +import net.runelite.http.api.worlds.WorldResult; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; + +public class HopHelper { + @Inject + private Game game; + + @Inject + private WorldService worldService; + + private Instant lastAttempt; + private net.runelite.api.World quickHopTargetWorld; + private int displaySwitcherAttempts = 0; + + public void prepare(int worldId) { + if (Duration.between(lastAttempt, Instant.now()).getSeconds() < 5) return; + + WorldResult worldResult = worldService.getWorlds(); + if (worldResult == null) return; + + World world = worldResult.findWorld(worldId); + if (world == null) return; + + final net.runelite.api.World rsWorld = game.client().createWorld(); + rsWorld.setActivity(world.getActivity()); + rsWorld.setAddress(world.getAddress()); + rsWorld.setId(world.getId()); + rsWorld.setPlayerCount(world.getPlayers()); + rsWorld.setLocation(world.getLocation()); + rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes())); + + if (game.client().getGameState() == GameState.LOGIN_SCREEN) { + game.client().changeWorld(rsWorld); + return; + } + + quickHopTargetWorld = rsWorld; + displaySwitcherAttempts = 0; + lastAttempt = Instant.now(); + } + + public void onGameTick() { + if (quickHopTargetWorld == null) return; + + if (game.client().getWidget(WidgetInfo.WORLD_SWITCHER_LIST) == null) { + game.client().openWorldHopper(); + if (++displaySwitcherAttempts >= 5) { + displaySwitcherAttempts = 0; + quickHopTargetWorld = null; + } + } else { + game.client().hopToWorld(quickHopTargetWorld); + quickHopTargetWorld = null; + } + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/ParentTask.java b/util/src/main/java/io/reisub/openosrs/util/ParentTask.java new file mode 100644 index 0000000..728dbe6 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/ParentTask.java @@ -0,0 +1,40 @@ +package io.reisub.openosrs.util; + +import java.util.ArrayList; +import java.util.List; + +public abstract class ParentTask extends Task { + protected final List children = new ArrayList<>(); + protected Task current; + + @Override + public String getStatus() { + if (current != null) return current.getStatus(); + + return ""; + } + + public void addChildren(Task... children) { + this.children.addAll(List.of(children)); + } + + @Override + public boolean validate() { + for (Task t : children) { + if (t.validate()) { + current = t; + return true; + } + } + + return false; + } + + @Override + public void execute() { + if (current != null) { + current.execute(); + current = null; + } + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/Task.java b/util/src/main/java/io/reisub/openosrs/util/Task.java new file mode 100644 index 0000000..f204acd --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/Task.java @@ -0,0 +1,70 @@ +package io.reisub.openosrs.util; + +import com.google.inject.Injector; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.iutils.api.Combat; +import net.runelite.client.plugins.iutils.api.Prayers; +import net.runelite.client.plugins.iutils.game.Game; +import net.runelite.client.plugins.iutils.ui.Bank; +import net.runelite.client.plugins.iutils.ui.Chatbox; +import net.runelite.client.plugins.iutils.ui.Equipment; +import net.runelite.client.plugins.iutils.walking.Walking; + +import javax.inject.Inject; +import java.util.List; + +@Slf4j +public abstract class Task { + @Inject + protected Game game; + + @Inject + protected Injector injector; + + @Inject + protected Calculations calc; + + @Inject + protected Walking walking; + + @Inject + protected Equipment equipment; + + @Inject + protected Combat combat; + + @Inject + protected Prayers prayers; + + @Inject + protected Bank bank; + + @Inject + protected Chatbox chatbox; + + protected void log(String msg) { + log.info(msg); + } + + protected void logTrace(String msg) { + log.trace(msg); + } + + protected void logDebug(String msg) { + log.debug(msg); + } + + protected void logWarn(String msg) { + log.warn(msg); + } + + protected void logError(String msg) { + log.error(msg); + } + + public abstract String getStatus(); + + public abstract boolean validate(); + + public abstract void execute(); +} diff --git a/util/src/main/java/io/reisub/openosrs/util/Util.java b/util/src/main/java/io/reisub/openosrs/util/Util.java new file mode 100644 index 0000000..aae9f10 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/Util.java @@ -0,0 +1,38 @@ +package io.reisub.openosrs.util; + +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import org.pf4j.Extension; + +import javax.inject.Singleton; + +@Extension +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Util", + description = "Utilities for Chaos scripts" +) +@Slf4j +@Singleton +public class Util extends Plugin { + @Provides + Config provideConfig(ConfigManager configManager) + { + return configManager.getConfig(Config.class); + } + + @Override + protected void startUp() { + log.info(this.getName() + " started"); + } + + @Override + protected void shutDown() { + log.info(this.getName() + " stopped"); + } +} \ No newline at end of file diff --git a/util/src/main/java/io/reisub/openosrs/util/enums/Activity.java b/util/src/main/java/io/reisub/openosrs/util/enums/Activity.java new file mode 100644 index 0000000..406d0af --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/enums/Activity.java @@ -0,0 +1,6 @@ +package io.reisub.openosrs.util.enums; + +public enum Activity { + IDLE, + GLASSBLOWING; +} diff --git a/util/src/main/java/io/reisub/openosrs/util/enums/Log.java b/util/src/main/java/io/reisub/openosrs/util/enums/Log.java new file mode 100644 index 0000000..87791e5 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/enums/Log.java @@ -0,0 +1,21 @@ +package io.reisub.openosrs.util.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +@AllArgsConstructor +@Getter +public enum Log { + NORMAL(ItemID.LOGS), + OAK(ItemID.OAK_LOGS), + WILLOW(ItemID.WILLOW_LOGS), + TEAK(ItemID.TEAK_LOGS), + MAPLE(ItemID.MAPLE_LOGS), + MAHOGANY(ItemID.MAHOGANY_LOGS), + YEW(ItemID.YEW_LOGS), + MAGIC(ItemID.MAGIC_LOGS), + REDWOOD(ItemID.REDWOOD_LOGS); + + private final int id; +} diff --git a/util/src/main/java/io/reisub/openosrs/util/tasks/CutFoodForKitten.java b/util/src/main/java/io/reisub/openosrs/util/tasks/CutFoodForKitten.java new file mode 100644 index 0000000..f17a499 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/tasks/CutFoodForKitten.java @@ -0,0 +1,33 @@ +package io.reisub.openosrs.util.tasks; + +import io.reisub.openosrs.util.Task; + +public class CutFoodForKitten extends Task { + @Override + public String getStatus() { + return "Cutting food for kitten"; + } + + @Override + public boolean validate() { + return KittenTask.handleKitten + && !game.inventory().withName("Roe", "Raw karambwanji").exists() + && game.inventory().withNamePart("Leaping").exists() + && game.inventory().withName("Knife").exists(); + } + + @Override + public void execute() { + if (game.inventory().full()) { + game.inventory().withNamePart("Leaping").first().interact("Drop"); + game.tick(); + } + + long count = game.inventory().withNamePart("Leaping").count(); + + game.inventory().withName("Knife").first().useOn(game.inventory().withNamePart("Leaping").first()); + game.tick(); + + game.waitUntil(() -> game.inventory().withNamePart("Leaping").count() < count, 5); + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/tasks/Eat.java b/util/src/main/java/io/reisub/openosrs/util/tasks/Eat.java new file mode 100644 index 0000000..e900e9a --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/tasks/Eat.java @@ -0,0 +1,64 @@ +package io.reisub.openosrs.util.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.api.Skill; + +public class Eat extends Task { + private long last = 0; + private boolean wait = true; + private int threshold = 5; + private int mean = 5; + private int sigma = 1; + + public void setInterval(int min, int max) { + mean = (min + max) / 2; + sigma = mean - min; + + if (sigma <= 0) threshold = min; + + threshold = getNewThreshold(); + } + + public void setWait(boolean wait) { + this.wait = wait; + } + + @Override + public boolean validate() { + return game.modifiedLevel(Skill.HITPOINTS) <= threshold + && game.inventory().withAction("Eat", "Drink").findAny().isPresent() + && System.currentTimeMillis() > last + 1800; + } + + @Override + public void execute() { + int hp = game.modifiedLevel(Skill.HITPOINTS); + + game.inventory().withAction("Eat", "Drink").findFirst().ifPresent(inventoryItem -> { + if (inventoryItem.actions().contains("Eat")) { + inventoryItem.interact("Eat"); + } else { + inventoryItem.interact("Drink"); + } + last = System.currentTimeMillis(); + }); + if (wait) game.tick(); + + threshold = getNewThreshold(); + + if (wait) game.waitUntil(() -> hp < game.modifiedLevel(Skill.HITPOINTS), 10); + } + + private int getNewThreshold() { + if (sigma <= 0) { + return threshold; + } + + return calc.randomGauss(1, 99, sigma, mean, false); + } + + @Override + public String getStatus() { + return "Eating"; + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/tasks/HandleKitten.java b/util/src/main/java/io/reisub/openosrs/util/tasks/HandleKitten.java new file mode 100644 index 0000000..508bd66 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/tasks/HandleKitten.java @@ -0,0 +1,92 @@ +package io.reisub.openosrs.util.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.InventoryItem; +import net.runelite.client.plugins.iutils.game.iNPC; +import net.runelite.client.plugins.iutils.ui.Chatbox; + +import java.time.Duration; +import java.time.Instant; + +public class HandleKitten extends Task { + private iNPC kitten; + + private Instant lastAte; + private Instant lastStroke; + private int strokesDone; + + @Override + public String getStatus() { + return "Feeding kitten"; + } + + @Override + public boolean validate() { + kitten = game.npcs().filter((npc) -> npc != null + && npc.name() != null && npc.name().equals("Kitten") + && npc.target() != null && npc.target().name() != null && npc.target().name().equals(game.localPlayer().name()) + && npc.actions() != null && npc.actions().contains("Pick-up")).nearest(); + + return KittenTask.handleKitten + && kitten != null + && game.inventory().withName("Roe", "Raw karambwanji").exists() + && kitten.position().reachable(game.localPlayer().position()); + } + + @Override + public void execute() { + int count = getCount(); + + if (lastAte == null || Duration.between(lastAte, Instant.now()).getSeconds() > 60) { + game.inventory().withName("Roe", "Raw karambwanji").first().useOn(kitten); + game.tick(); + if (!game.waitUntil(() -> getCount() < count && game.localPlayer().isIdle(), 10)) { + logWarn("Timed out trying to feed our kitten"); + return; + } + + lastAte = Instant.now(); + } + + if (lastStroke == null || Duration.between(lastStroke, Instant.now()).getSeconds() > 60) { + int strokesToDo = strokesDone == 0 ? 2 : 1; + + for (int i = 0; i < strokesToDo; i++) { + kitten.interact("Interact"); + game.tick(); + if (!game.waitUntil(() -> chatbox.chatState().equals(Chatbox.ChatState.OPTIONS_CHAT), 10)) { + logWarn("Timed out trying to stroke our kitten"); + return; + } + + chatbox.chooseOption("Stroke"); + game.tick(); + if (!game.waitUntil(() -> chatbox.chatState().equals(Chatbox.ChatState.PLAYER_CHAT), 10)) { + logWarn("Timed out trying to stroke our kitten"); + return; + } + + strokesDone++; + } + + if (strokesDone == 2) { + strokesDone = 0; + lastStroke = Instant.now(); + } + } + + KittenTask.handleKitten = false; + log("Finished taking care of our kitten"); + } + + private int getCount() { + long count = game.inventory().withName("Roe").count(); + + InventoryItem karambwanji = game.inventory().withName("Raw karambwanji").first(); + if (karambwanji != null) { + count =+karambwanji.quantity(); + } + + return (int) count; + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/tasks/KittenTask.java b/util/src/main/java/io/reisub/openosrs/util/tasks/KittenTask.java new file mode 100644 index 0000000..28534c8 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/tasks/KittenTask.java @@ -0,0 +1,28 @@ +package io.reisub.openosrs.util.tasks; + +import com.google.inject.Injector; +import io.reisub.openosrs.util.ParentTask; +import net.runelite.api.events.ChatMessage; + +public class KittenTask extends ParentTask { + public static boolean handleKitten; + + public static KittenTask getInstance(Injector injector) { + KittenTask instance = injector.getInstance(KittenTask.class); + + instance.addChildren( + injector.getInstance(CutFoodForKitten.class), + injector.getInstance(HandleKitten.class), + injector.getInstance(PickupCat.class) + ); + + return instance; + } + + public void onChatMessage(ChatMessage chatMessage) { + if (chatMessage.getMessage().contains("Your kitten wants attention.") + || chatMessage.getMessage().contains("Your kitten is hungry.")) { + handleKitten = true; + } + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/tasks/PickupCat.java b/util/src/main/java/io/reisub/openosrs/util/tasks/PickupCat.java new file mode 100644 index 0000000..78bd3cc --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/tasks/PickupCat.java @@ -0,0 +1,31 @@ +package io.reisub.openosrs.util.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iNPC; + +public class PickupCat extends Task { + private iNPC cat; + + @Override + public String getStatus() { + return "Picking up cat"; + } + + @Override + public boolean validate() { + cat = game.npcs().filter((npc) -> npc != null + && npc.name() != null && npc.name().equals("Cat") + && npc.target() != null && npc.target().name().equals(game.localPlayer().name()) + && npc.actions() != null && npc.actions().contains("Pick-up")).nearest(); + + return cat != null && !game.inventory().full(); + } + + @Override + public void execute() { + cat.interact("Pick-up"); + game.tick(); + + game.waitUntil(() -> game.inventory().withName("Cat").exists(), 10); + } +} diff --git a/util/src/main/java/io/reisub/openosrs/util/tasks/Run.java b/util/src/main/java/io/reisub/openosrs/util/tasks/Run.java new file mode 100644 index 0000000..79b7144 --- /dev/null +++ b/util/src/main/java/io/reisub/openosrs/util/tasks/Run.java @@ -0,0 +1,50 @@ +package io.reisub.openosrs.util.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.walking.Walking; + +import javax.inject.Inject; + +public class Run extends Task { + @Inject + private Walking walking; + + private int threshold = 90; + private int mean = 85; + private int sigma = 5; + + public void setInterval(int min, int max) { + mean = (min + max) / 2; + sigma = mean - min; + + if (sigma <= 0) threshold = min; + + threshold = getNewThreshold(); + } + + @Override + public String getStatus() { + return "Enabling run"; + } + + @Override + public boolean validate() { + return !walking.isRunning() && game.energy() > threshold; + } + + @Override + public void execute() { + walking.setRun(true); + game.tick(); + + threshold = getNewThreshold(); + } + + private int getNewThreshold() { + if (sigma <= 0) { + return threshold; + } + + return calc.randomGauss(1, 99, sigma, mean, false); + } +} diff --git a/util/util.gradle.kts b/util/util.gradle.kts new file mode 100644 index 0000000..cf2d3c5 --- /dev/null +++ b/util/util.gradle.kts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "Chaos Util" +project.extra["PluginDescription"] = "Utilities for Chaos scripts" + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Activity.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Activity.java new file mode 100644 index 0000000..e28879d --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Activity.java @@ -0,0 +1,17 @@ +package io.reisub.openosrs.wintertodt; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum Activity { + IDLE("Idle"), + WOODCUTTING("Woodcutting"), + FLETCHING("Fletching"), + FEEDING_BRAZIER("Feeding"), + FIXING_BRAZIER("Fixing"), + LIGHTING_BRAZIER("Lighting"); + + private final String actionString; +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/BossData.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/BossData.java new file mode 100644 index 0000000..c01f983 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/BossData.java @@ -0,0 +1,22 @@ +package io.reisub.openosrs.wintertodt; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import javax.annotation.concurrent.Immutable; + +@AllArgsConstructor +@Immutable +public class BossData { + @Getter + private final int health; + + @Getter + private final int world; + + @Getter + private final long time; + + @Getter + private final int timer; +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Config.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Config.java new file mode 100644 index 0000000..5e90700 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Config.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.wintertodt; + +import net.runelite.client.config.Button; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; + +@ConfigGroup("ChaosWintertodtConfig") + +public interface Config extends net.runelite.client.config.Config { + @Range(min = 1, max = 99) + @ConfigItem( + keyName = "minEatHP", + name = "Minimum Eat HP", + description = "Minimum HP to eat at. i.e. will always eat", + position = 0 + ) + default int minEatHP() { + return 10; + } + + @Range(min = 1, max = 100) + @ConfigItem( + keyName = "maxEatHP", + name = "Maximum Eat HP", + description = "Highest HP to consider eating. Value MUST be higher than minimum HP config. If HP drops below this value bot may randomly decide to eat.", + position = 1 + ) + default int maxEatHP() { + return 20; + } + + @ConfigItem( + position = 10, + keyName = "sideSelection", + name = "Select side", + description = "Choose which side you want to play the game at." + ) + default Side sideSelection() { return Side.EAST; } + + @Range(min = 1, max = 100) + @ConfigItem( + position = 11, + keyName = "sideTimeout", + name = "Side timeout", + description = "Seconds to wait before switching side because of an incapacitated pyromancer." + ) + default int sideTimeout() { return 15; } + + @ConfigItem( + keyName = "fletchNearBrazier", + name = "Fletch near brazier", + description = "Fletch near brazier so we can quickly fix/light the brazier for more experience and points at the cost of taking more damage.", + position = 20 + ) + default boolean fletchNearBrazier() { + return true; + } + + @ConfigItem( + keyName = "dodgeProjectiles", + name = "Dodge projectiles", + description = "Attempts to dodge snow fall and exploding braziers.", + position = 21 + ) + default boolean dodgeProjectiles() { + return true; + } + + @ConfigItem( + keyName = "openCrates", + name = "Open crates", + description = "Enable to open crates.", + position = 22 + ) + default boolean openCrates() { + return true; + } + + @ConfigItem( + keyName = "hop", + name = "Hop", + description = "Hop to different worlds after finishing game.", + position = 30 + ) + default boolean hop() { + return true; + } + + @Range(min = 40, max = 100) + @ConfigItem( + position = 31, + keyName = "hopPercentage", + name = "Hop percentage", + description = "Minimum percentage the boss' health should be at before hopping. Low values risk not getting enough points. 70 is safe, 60 should work, anything lower is very risky." + ) + default int hopPercentage() { return 70; } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/InterruptType.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/InterruptType.java new file mode 100644 index 0000000..b7079b6 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/InterruptType.java @@ -0,0 +1,21 @@ +package io.reisub.openosrs.wintertodt; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum InterruptType { + COLD("Damaged by Wintertodt Cold"), + SNOWFALL("Damaged by Wintertodt Snowfall"), + BRAZIER("Brazier Shattered"), + INVENTORY_FULL("Inventory full of Bruma Roots"), + OUT_OF_ROOTS("Out of Bruma Roots"), + FIXED_BRAZIER("Fixed Brazier"), + LIT_BRAZIER("Lit Brazier"), + BRAZIER_WENT_OUT("Brazier went out"), + EAT("You ate"), + TOO_COLD("Your hands are too cold"); + + private final String interruptSourceString; +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Scouter.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Scouter.java new file mode 100644 index 0000000..811d7a9 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Scouter.java @@ -0,0 +1,91 @@ +package io.reisub.openosrs.wintertodt; + +import com.google.gson.*; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.iutils.game.Game; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Slf4j +@Singleton +public class Scouter { + @Inject + public Wintertodt plugin; + + @Inject + public Game game; + + @Inject + public OkHttpClient okHttpClient; + + @Getter + private final List data = new ArrayList<>(4); + + private Instant lastUpdate; + + public void onGameTick() { + if (lastUpdate == null || Duration.between(lastUpdate, Instant.now()).getSeconds() > 2) { + lastUpdate = Instant.now(); + + getRemoteData(); + } + } + + public BossData getLocalData() { + return new BossData( + plugin.getBossHealth(), + game.client().getWorld(), + System.currentTimeMillis() / 1000, + plugin.getRespawnTimer() + ); + } + + private void getRemoteData() { + try { + Request r = new Request.Builder() + .url("https://www.wintertodt.com/scouter/") + .addHeader("User-Agent", "RuneLite") + .addHeader("Authorization", "2") + .build(); + + okHttpClient.newCall(r).enqueue(new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + log.debug("Error retrieving Wintertodt boss data", e); + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + try { + JsonArray jsonArray = new Gson().fromJson(Objects.requireNonNull(response.body()).string(), JsonArray.class); + parseData(jsonArray); + } catch (IOException | JsonSyntaxException ignored) {} + } + } + }); + } catch (IllegalArgumentException ignored) {} + } + + private void parseData(JsonArray jsonArray) { + for (JsonElement jsonElement : jsonArray) { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + try { + BossData bossData = new BossData(jsonObject.get("a").getAsInt(), jsonObject.get("b").getAsInt(), jsonObject.get("c").getAsLong(), jsonObject.get("d").getAsInt()); + if (bossData.getWorld() != game.client().getWorld()) { + data.add(bossData); + } + } catch (UnsupportedOperationException ignored) {} + } + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Side.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Side.java new file mode 100644 index 0000000..45915fd --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Side.java @@ -0,0 +1,15 @@ +package io.reisub.openosrs.wintertodt; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.client.plugins.iutils.scene.Position; + +@AllArgsConstructor +@Getter +public enum Side { + EAST(new Position(1638, 3996, 0), new Position(1638, 3988, 0)), + WEST(new Position(1622, 3996, 0), new Position(1622, 3988, 0)); + + private final Position positionNearBrazier; + private final Position positionNearRoots; +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Wintertodt.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Wintertodt.java new file mode 100644 index 0000000..e1ad43b --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/Wintertodt.java @@ -0,0 +1,472 @@ +package io.reisub.openosrs.wintertodt; + +import com.google.inject.Inject; +import com.google.inject.Provides; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.util.tasks.Eat; +import io.reisub.openosrs.util.tasks.KittenTask; +import io.reisub.openosrs.wintertodt.tasks.*; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.*; +import net.runelite.api.events.*; +import net.runelite.api.widgets.Widget; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scene.Position; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static net.runelite.api.AnimationID.*; +import static net.runelite.api.ItemID.BRUMA_KINDLING; +import static net.runelite.api.ItemID.BRUMA_ROOT; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Wintertodt", + description = "Plays the Wintertodt minigame", + enabledByDefault = false +) +@Slf4j +public class Wintertodt extends iScript { + @Inject + public Config config; + + public static final int WINTERTODT_REGION = 6462; + public static final int WINTERTODT_HEALTH_PACKED_ID = 25952277; + public static final int WINTERTODT_GAME_TIMER_ID = 25952259; + + @Getter + private Activity currentActivity = Activity.IDLE; + + @Getter + private Activity previousActivity = Activity.IDLE; + + @Getter + private int respawnTimer; + + @Getter + private int bossHealth; + + @Getter + @Setter + private boolean tooCold; + + @Getter + @Setter + private Instant lastHop; + + @Getter + private final List projectiles = new ArrayList<>(); + + private List tasks; + private KittenTask kittenTask; + private Hop hopTask; + private Scouter scouter; + private int fmLevel, wcLevel, fletchLevel; + private Instant lastActionTime; + private int previousTimerValue; + + @SuppressWarnings("unused") + @Provides + Config provideConfig(ConfigManager configManager) { + return configManager.getConfig(Config.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + log.info(t.getStatus()); + t.execute(); + break; + } + } + + game.sleepDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Wintertodt"); + + fmLevel = game.baseLevel(Skill.FIREMAKING); + wcLevel = game.baseLevel(Skill.WOODCUTTING); + fletchLevel = game.baseLevel(Skill.FLETCHING); + + if (config.hop()) scouter = injector.getInstance(Scouter.class); + + Eat eatTask = injector.getInstance(Eat.class); + eatTask.setInterval(config.minEatHP(), config.maxEatHP()); + + kittenTask = KittenTask.getInstance(injector); + + tasks = new ArrayList<>(); + tasks.add(eatTask); + tasks.add(kittenTask); + if (config.dodgeProjectiles()) { + tasks.add(injector.getInstance(DodgeProjectile.class)); + } + tasks.add(injector.getInstance(OpenInventory.class)); + tasks.add(injector.getInstance(EatWhileWaiting.class)); + tasks.add(injector.getInstance(OpenCrates.class)); + tasks.add(injector.getInstance(GoToBank.class)); + tasks.add(injector.getInstance(HandleBank.class)); + tasks.add(injector.getInstance(GoToWintertodt.class)); + if (config.hop()) { + hopTask = injector.getInstance(Hop.class); + tasks.add(hopTask); + } + tasks.add(injector.getInstance(MoveToBrazier.class)); + tasks.add(injector.getInstance(Fix.class)); + tasks.add(injector.getInstance(Light.class)); + tasks.add(injector.getInstance(Fletch.class)); + tasks.add(injector.getInstance(ChangeSide.class)); + tasks.add(injector.getInstance(Burn.class)); + tasks.add(injector.getInstance(Chop.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Wintertodt"); + if (tasks != null) { + tasks.clear(); + } + + KittenTask.handleKitten = false; + scouter = null; + } + + @SuppressWarnings("unused") + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onStatChanged(StatChanged event) { + Skill skill = event.getSkill(); + int level = event.getLevel(); + + if (skill == Skill.FIREMAKING && level > fmLevel) { + setActivity(Activity.IDLE); + fmLevel = event.getLevel(); + } else if (skill == Skill.WOODCUTTING && level > wcLevel) { + setActivity(Activity.IDLE); + wcLevel = event.getLevel(); + } else if (skill == Skill.FLETCHING && level > fletchLevel) { + setActivity(Activity.IDLE); + fletchLevel = event.getLevel(); + } else if (skill == Skill.FIREMAKING && currentActivity == Activity.LIGHTING_BRAZIER) { + setActivity(Activity.IDLE); + } else if (skill == Skill.CONSTRUCTION && currentActivity == Activity.FIXING_BRAZIER) { + setActivity(Activity.IDLE); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onGameTick(GameTick event) { + if (!isInWintertodtRegion() || game.client().getGameState() != GameState.LOGGED_IN) return; + + if (scouter != null) { + scouter.onGameTick(); + } + + if (hopTask != null) { + hopTask.onGameTick(); + } + + parseBossHealth(); + //parseRespawnTime(); + + checkActionTimeout(); + } + + @SuppressWarnings("unused") + @Subscribe + private void onChatMessage(ChatMessage chatMessage) { + if (kittenTask != null) { + kittenTask.onChatMessage(chatMessage); + } + + if (!isInWintertodtRegion()) return; + + ChatMessageType chatMessageType = chatMessage.getType(); + if (chatMessageType != ChatMessageType.GAMEMESSAGE && chatMessageType != ChatMessageType.SPAM) return; + + MessageNode messageNode = chatMessage.getMessageNode(); + final InterruptType interruptType; + + if (messageNode.getValue().startsWith("You carefully fletch the root")) { + setActivity(Activity.FLETCHING); + return; + } + + if (messageNode.getValue().startsWith("The cold of")) { + interruptType = InterruptType.COLD; + } else if (messageNode.getValue().startsWith("The freezing cold attack")) { + interruptType = InterruptType.SNOWFALL; + } else if (messageNode.getValue().startsWith("The brazier is broken and shrapnel")) { + interruptType = InterruptType.BRAZIER; + } else if (messageNode.getValue().startsWith("You have run out of bruma roots")) { + interruptType = InterruptType.OUT_OF_ROOTS; + } else if (messageNode.getValue().startsWith("Your inventory is too full")) { + interruptType = InterruptType.INVENTORY_FULL; + } else if (messageNode.getValue().startsWith("You fix the brazier")) { + interruptType = InterruptType.FIXED_BRAZIER; + } else if (messageNode.getValue().startsWith("You light the brazier.")) { + interruptType = InterruptType.LIT_BRAZIER; + } else if (messageNode.getValue().startsWith("The brazier has gone out.")) { + interruptType = InterruptType.BRAZIER_WENT_OUT; + } else if (messageNode.getValue().startsWith("You eat")) { + interruptType = InterruptType.EAT; + } else if (messageNode.getValue().startsWith("Your hands are too cold")) { + interruptType = InterruptType.TOO_COLD; + tooCold = true; + } else { + return; + } + + boolean wasInterrupted = false; + + switch (interruptType) { + case EAT: + case LIT_BRAZIER: + case INVENTORY_FULL: + case OUT_OF_ROOTS: + case BRAZIER_WENT_OUT: + case FIXED_BRAZIER: + wasInterrupted = true; + break; + case COLD: + case BRAZIER: + case SNOWFALL: + if (currentActivity != Activity.WOODCUTTING && currentActivity != Activity.IDLE) { + wasInterrupted = true; + } + break; + } + + if (wasInterrupted) { + setActivity(Activity.IDLE); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onAnimationChanged(AnimationChanged event) { + if (game.client().getGameState() != GameState.LOGGED_IN || !isInWintertodtRegion()) return; + + if (event.getActor() != game.client().getLocalPlayer()) return; + + int animId = game.localPlayer().animation(); + switch (animId) { + case WOODCUTTING_BRONZE: + case WOODCUTTING_IRON: + case WOODCUTTING_STEEL: + case WOODCUTTING_BLACK: + case WOODCUTTING_MITHRIL: + case WOODCUTTING_ADAMANT: + case WOODCUTTING_RUNE: + case WOODCUTTING_GILDED: + case WOODCUTTING_DRAGON: + case WOODCUTTING_DRAGON_OR: + case WOODCUTTING_INFERNAL: + case WOODCUTTING_3A_AXE: + case WOODCUTTING_CRYSTAL: + case WOODCUTTING_TRAILBLAZER: + setActivity(Activity.WOODCUTTING); + break; + case FLETCHING_BOW_CUTTING: + setActivity(Activity.FLETCHING); + break; + case LOOKING_INTO: + setActivity(Activity.FEEDING_BRAZIER); + break; + case FIREMAKING: + setActivity(Activity.LIGHTING_BRAZIER); + break; + case CONSTRUCTION: + case CONSTRUCTION_IMCANDO: + setActivity(Activity.FIXING_BRAZIER); + break; + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onItemContainerChanged(ItemContainerChanged event) { + final ItemContainer container = event.getItemContainer(); + + if (!isInWintertodtRegion() || container != game.client().getItemContainer(InventoryID.INVENTORY)) return; + + int logs = (int) game.inventory().withId(BRUMA_ROOT).count(); + int kindling = (int) game.inventory().withId(BRUMA_KINDLING).count(); + + if (logs == 0 && currentActivity == Activity.FLETCHING) { + setActivity(Activity.IDLE); + } else if (logs == 0 && kindling == 0 && currentActivity == Activity.FEEDING_BRAZIER) { + setActivity(Activity.IDLE); + } else if (shouldStartFeeding() && (currentActivity == Activity.WOODCUTTING || currentActivity == Activity.FLETCHING)) { + setActivity(Activity.IDLE); + } else if (shouldStartFletching() && currentActivity == Activity.WOODCUTTING) { + setActivity(Activity.IDLE); + } + } + + @SuppressWarnings("unused") + @Subscribe + private void onVarbitChanged(VarbitChanged varbitChanged) { + int timerValue = game.client().getVar(Varbits.WINTERTODT_TIMER); + if (timerValue != previousTimerValue) { + respawnTimer = timerValue * 30 / 50; + previousTimerValue = timerValue; + } + } + + @Subscribe + private void onProjectileMoved(ProjectileMoved event) { + Projectile projectile = event.getProjectile(); + + if (projectile.getInteracting() != null) return; + + if (projectile.getId() == 501) { + int x = game.client().getBaseX() + event.getPosition().getSceneX(); + int y = game.client().getBaseY() + event.getPosition().getSceneY(); + int cycles = projectile.getEndCycle() - projectile.getStartMovementCycle(); + + // we don't care about any of the projectiles that don't go to our play area + if (y >= 4001) return; + + if (cycles == 200) { + projectiles.add(new WintertodtProjectile(x, y, true, Instant.now())); + } else if ((x == 1638 && y == 3997) || (x == 1619 && y == 3997)) { + if (cycles == 120) { + projectiles.add(new WintertodtProjectile(x, y, false, Instant.now())); + } + } + } + } + + public boolean isInWintertodtRegion() { + return game.localPlayer().position().regionID() == WINTERTODT_REGION; + } + + public boolean bossIsUp() { + return isInWintertodtRegion() + && getBossHealth() > 0 + && getRespawnTimer() <= 0; + } + + public boolean shouldStartFletching() { + long rootCount = game.inventory().withName("Bruma root").count(); + long kindlingCount = game.inventory().withName("Bruma kindling").count(); + + return rootCount * 2 + kindlingCount >= getBossHealth(); + } + + public boolean shouldStartFeeding() { + long burnablesCount = game.inventory().withNamePart("Bruma").count(); + + return burnablesCount >= getBossHealth(); + } + + public Side getNearestSide() { + Position playerPos = game.localPlayer().position(); + if (playerPos.distanceTo(Side.EAST.getPositionNearBrazier()) > playerPos.distanceTo(Side.WEST.getPositionNearBrazier())) { + return Side.WEST; + } else { + return Side.EAST; + } + } + + public Side getFurthestSide() { + Position playerPos = game.localPlayer().position(); + if (playerPos.distanceTo(Side.EAST.getPositionNearBrazier()) < playerPos.distanceTo(Side.WEST.getPositionNearBrazier())) { + return Side.WEST; + } else { + return Side.EAST; + } + } + + private void setActivity(Activity action) { + if (action == Activity.IDLE && currentActivity != Activity.IDLE) { + previousActivity = currentActivity; + } + + currentActivity = action; + + if (action != Activity.IDLE) { + lastActionTime = Instant.now(); + } + } + + private void checkActionTimeout() { + if (currentActivity == Activity.IDLE) return; + + int animId = game.localPlayer().animation(); + if (animId != IDLE || lastActionTime == null) return; + + Duration timeout = Duration.ofSeconds(3); + Duration sinceAction = Duration.between(lastActionTime, Instant.now()); + + if (sinceAction.compareTo(timeout) >= 0) { + setActivity(Activity.IDLE); + } + } + + private void parseBossHealth() { + Widget healthWidget = game.client.getWidget(WINTERTODT_HEALTH_PACKED_ID); + + if (healthWidget != null) { + Pattern regex = Pattern.compile("\\d+"); + Matcher bossHealthMatcher = regex.matcher(healthWidget.getText()); + + if (bossHealthMatcher.find()) { + bossHealth = Integer.parseInt(bossHealthMatcher.group(0)); + if (bossHealth > 0) { + respawnTimer = -1; + } + } else { + bossHealth = -1; + } + } + } + + private void parseRespawnTime() { + Widget timerWidget = game.client.getWidget(WINTERTODT_GAME_TIMER_ID); + + if (timerWidget != null) { + Pattern regex = Pattern.compile("\\d:\\d+"); + Matcher timerMatcher = regex.matcher(timerWidget.getText()); + + if (timerMatcher.find()) { + String[] time = timerMatcher.group(0).split(":"); + String minutes = time[0]; + String seconds = time[1]; + + respawnTimer = Integer.parseInt(minutes) * 60 + Integer.parseInt(seconds); + } else { + respawnTimer = -1; + } + } + } +} \ No newline at end of file diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/WintertodtProjectile.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/WintertodtProjectile.java new file mode 100644 index 0000000..ca50ccb --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/WintertodtProjectile.java @@ -0,0 +1,30 @@ +package io.reisub.openosrs.wintertodt; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.client.plugins.iutils.scene.Area; +import net.runelite.client.plugins.iutils.scene.RectangularArea; + +import java.time.Instant; + +@AllArgsConstructor +public class WintertodtProjectile { + private final int x; + private final int y; + private final boolean aoe; + + @Getter + private final Instant start; + + public Area getDamageArea() { + if (aoe) { + return new RectangularArea(x-1, y-1, x+1, y+1); + } else { + if (x == 1638) { + return new RectangularArea(x,y-1, x+3, y+2); + } else { + return new RectangularArea(x-1, y-1, x+2, y+2); + } + } + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Burn.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Burn.java new file mode 100644 index 0000000..adf5e46 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Burn.java @@ -0,0 +1,49 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Side; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +import static net.runelite.api.ItemID.BRUMA_KINDLING; +import static net.runelite.api.ItemID.BRUMA_ROOT; + +public class Burn extends Task { + @Inject + public Wintertodt plugin; + + @Override + public boolean validate() { + if (!plugin.bossIsUp()) return false; + + if (plugin.getPreviousActivity() == Activity.WOODCUTTING && !game.inventory().full() && !plugin.shouldStartFeeding()) return false; + + return plugin.getCurrentActivity() == Activity.IDLE + && game.inventory().withId(BRUMA_ROOT, BRUMA_KINDLING).exists(); + } + + @Override + public void execute() { + if (plugin.getNearestSide() == Side.WEST + && !game.localPlayer().position().equals(new Position(1622, 3996, 0))) { + walking.walkTo(new Position(1622, 3996, 0)); + game.tick(); + game.sleepDelay(); + } + + game.objects().withName("Burning brazier").within(15).findFirst().ifPresent((brazier) -> { + brazier.interact("Feed"); + + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.FEEDING_BRAZIER || !game.objects().withName("Burning brazier").within(10).exists(), 10); + game.tick(); + }); + } + + @Override + public String getStatus() { + return "Feeding brazier"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/ChangeSide.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/ChangeSide.java new file mode 100644 index 0000000..6844ef9 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/ChangeSide.java @@ -0,0 +1,57 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Config; +import io.reisub.openosrs.wintertodt.Wintertodt; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; + +import static net.runelite.api.NpcID.INCAPACITATED_PYROMANCER; + +public class ChangeSide extends Task { + @Inject + public Wintertodt plugin; + + @Inject + public Config config; + + private Instant lastIncap; + + @Override + public String getStatus() { + return "Changing side"; + } + + @Override + public boolean validate() { + if (!plugin.bossIsUp()) { + lastIncap = null; + return false; + } + + if (game.npcs().within(6).withId(INCAPACITATED_PYROMANCER).exists() + && !game.objects().withName("Burning brazier").within(15).exists()) { + if (lastIncap == null) { + lastIncap = Instant.now(); + } + } else { + lastIncap = null; + return false; + } + + return lastIncap != null + && Duration.between(lastIncap, Instant.now()).getSeconds() > config.sideTimeout() + && plugin.getCurrentActivity() == Activity.IDLE; + } + + @Override + public void execute() { + lastIncap = null; + + walking.walkTo(plugin.getFurthestSide().getPositionNearBrazier()); + game.tick(); + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Chop.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Chop.java new file mode 100644 index 0000000..c9ca711 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Chop.java @@ -0,0 +1,52 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +public class Chop extends Task { + @Inject + public Wintertodt plugin; + + @Override + public boolean validate() { + if (!plugin.bossIsUp()) return false; + + return plugin.getCurrentActivity() == Activity.IDLE + && !plugin.shouldStartFeeding() + && !plugin.shouldStartFletching() + && plugin.getBossHealth() > 4 + && !game.inventory().full(); + } + + @Override + public void execute() { + Position nearRoots = plugin.getNearestSide().getPositionNearRoots(); + Position nearRoots2 = new Position(nearRoots.x, nearRoots.y+1, nearRoots.z); + + if (!game.localPlayer().position().equals(nearRoots) && !game.localPlayer().position().equals(nearRoots2)) { + if (calc.random(0, 10) == 5) { + walking.walkTo(nearRoots2); + } else { + walking.walkTo(nearRoots); + } + game.tick(); + game.sleepDelay(); + } + + game.objects().withName("Bruma roots").nearestFirst().findFirst().ifPresent(roots -> { + roots.interact("Chop"); + }); + + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.WOODCUTTING, 20); + game.tick(); + } + + @Override + public String getStatus() { + return "Chopping bruma roots"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/DodgeProjectile.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/DodgeProjectile.java new file mode 100644 index 0000000..fb38e5a --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/DodgeProjectile.java @@ -0,0 +1,89 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Wintertodt; +import io.reisub.openosrs.wintertodt.WintertodtProjectile; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; +import java.time.Instant; +import java.util.Iterator; + +public class DodgeProjectile extends Task { + @Inject + public Wintertodt plugin; + + private WintertodtProjectile projectile; + + @Override + public String getStatus() { + return "Dodging projectile"; + } + + @Override + public boolean validate() { + if (plugin.getProjectiles().isEmpty()) return false; + + Instant now = Instant.now(); + for (Iterator it = plugin.getProjectiles().listIterator(); it.hasNext();) { + WintertodtProjectile p = it.next(); + + if (now.isAfter(p.getStart().plusSeconds(4))) { + it.remove(); + continue; + } + + if (p.getDamageArea().contains(game.localPlayer().position())) { + projectile = p; + return true; + } + } + + return false; + } + + @Override + public void execute() { + Position safePosition = findSafePosition(game.localPlayer().position()); + + if (safePosition != null) { + walking.walkTo(safePosition); + game.tick(); + } + + game.waitUntil(() -> Instant.now().isAfter(projectile.getStart().plusSeconds(4))); + game.sleepDelay(); + } + + private Position findSafePosition(Position startPosition) { + Position safePosition = startPosition; + + while (projectile.getDamageArea().contains(safePosition)) { + safePosition = safePosition.south(); + } + if (walking.reachable(safePosition)) return safePosition; + + safePosition = startPosition; + + while (projectile.getDamageArea().contains(safePosition)) { + safePosition = safePosition.north(); + } + if (walking.reachable(safePosition)) return safePosition; + + safePosition = startPosition; + + while (projectile.getDamageArea().contains(safePosition)) { + safePosition = safePosition.west(); + } + if (walking.reachable(safePosition)) return safePosition; + + safePosition = startPosition; + + while (projectile.getDamageArea().contains(safePosition)) { + safePosition = safePosition.east(); + } + if (walking.reachable(safePosition)) return safePosition; + + return null; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/EatWhileWaiting.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/EatWhileWaiting.java new file mode 100644 index 0000000..1cad5d2 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/EatWhileWaiting.java @@ -0,0 +1,41 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import com.google.inject.Inject; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.api.Skill; + +import static net.runelite.api.ItemID.BRUMA_KINDLING; +import static net.runelite.api.ItemID.BRUMA_ROOT; + +public class EatWhileWaiting extends Task { + @Inject + public Wintertodt plugin; + + @Override + public String getStatus() { + return "Eating"; + } + + @Override + public boolean validate() { + int missingHP = game.baseLevel(Skill.HITPOINTS) - game.modifiedLevel(Skill.HITPOINTS); + + return plugin.isInWintertodtRegion() + && (plugin.getRespawnTimer() > 0 || plugin.getBossHealth() <= 4 && !game.inventory().withId(BRUMA_ROOT, BRUMA_KINDLING).exists()) + && game.inventory().withAction("Eat", "Drink").exists() + && missingHP > calc.random(9, 12); + } + + @Override + public void execute() { + game.inventory().withAction("Eat", "Drink").findFirst().ifPresent(inventoryItem -> { + if (inventoryItem.actions().contains("Eat")) { + inventoryItem.interact("Eat"); + } else { + inventoryItem.interact("Drink"); + } + game.tick(calc.random(3, 5)); + }); + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fix.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fix.java new file mode 100644 index 0000000..eb511e1 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fix.java @@ -0,0 +1,43 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Wintertodt; + +import javax.inject.Inject; + +public class Fix extends Task { + @Inject + public Wintertodt plugin; + + @Override + public String getStatus() { + return "Fixing brazier"; + } + + @Override + public boolean validate() { + if (!plugin.bossIsUp()) return false; + + int distance = plugin.getCurrentActivity() == Activity.IDLE ? 8 : 2; + + return game.objects().within(distance).withAction("Fix").exists() + && plugin.getCurrentActivity() != Activity.FIXING_BRAZIER; + } + + @Override + public void execute() { + int distance = plugin.getCurrentActivity() == Activity.IDLE ? 8 : 2; + + game.objects().within(distance).withAction("Fix").findFirst().ifPresent((brazier) -> { + brazier.interact("Fix"); + game.tick(); + }); + + if (distance == 2) { + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.FIXING_BRAZIER || !game.objects().within(distance).withAction("Fix").exists(), 3); + } else { + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.FIXING_BRAZIER || !game.objects().within(distance).withAction("Fix").exists(), 6); + } + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fletch.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fletch.java new file mode 100644 index 0000000..4639297 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Fletch.java @@ -0,0 +1,55 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Config; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +import static net.runelite.api.ItemID.BRUMA_ROOT; +import static net.runelite.api.ItemID.KNIFE; + +public class Fletch extends Task { + @Inject + public Wintertodt plugin; + + @Inject + public Config config; + + @Override + public boolean validate() { + if (!plugin.bossIsUp()) return false; + + if (plugin.getPreviousActivity() == Activity.WOODCUTTING && !game.inventory().full() && !plugin.shouldStartFletching()) return false; + + return plugin.getCurrentActivity() == Activity.IDLE + && !plugin.shouldStartFeeding() + && game.inventory().withId(BRUMA_ROOT).exists(); + + } + + @Override + public void execute() { + if (config.fletchNearBrazier()) { + Position nearBrazier = plugin.getNearestSide().getPositionNearBrazier(); + if (!game.localPlayer().position().equals(nearBrazier) && !game.localPlayer().position().equals(nearBrazier.south())) { + walking.walkTo(nearBrazier); + } + } + + game.tick(); + game.inventory().withId(KNIFE).findFirst().ifPresent((knife) -> { + game.inventory().withId(BRUMA_ROOT).findFirst().ifPresent(knife::useOn); + }); + + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.FLETCHING, 5); + game.tick(); + } + + @Override + public String getStatus() { + return "Cutting roots into kindlings"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToBank.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToBank.java new file mode 100644 index 0000000..9f207b6 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToBank.java @@ -0,0 +1,56 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.api.Skill; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +import static net.runelite.api.ItemID.BRUMA_KINDLING; +import static net.runelite.api.ItemID.BRUMA_ROOT; +import static net.runelite.api.ObjectID.DOORS_OF_DINH; + +public class GoToBank extends Task { + @Inject + public Wintertodt plugin; + + @Override + public boolean validate() { + if (!plugin.isInWintertodtRegion()) return false; + + if (game.inventory().withAction("Eat", "Drink").findAny().isEmpty() && game.modifiedLevel(Skill.HITPOINTS) <= 10) return true; + + return game.inventory().withAction("Eat", "Drink").count() <= 1 + && (plugin.getRespawnTimer() > 0 || (plugin.getBossHealth() <= 4 && !game.inventory().withId(BRUMA_ROOT, BRUMA_KINDLING).exists())); + } + + @Override + public void execute() { + iObject doorsOfDinh = game.objects().withId(DOORS_OF_DINH).first(); + if (doorsOfDinh == null) return; + + Position target = new Position(1630, 3970, 0); + + if (game.localPlayer().position().y > doorsOfDinh.position().y && game.localPlayer().position().distanceTo(doorsOfDinh.position()) > 8) { + walking.walkTo(target.areaWithin(calc.random(1, 9))); + game.tick(); + } + + game.waitUntil(() -> !plugin.bossIsUp(), 200); + + doorsOfDinh.interact("Enter"); + game.tick(); + game.waitUntil(() -> game.localPlayer().position().y < 3965, 20); + + final Position bank = new Position(1639, 3944, 0); + walking.walkTo(bank.areaWithin(calc.random(1, 4))); + game.tick(); + } + + @Override + public String getStatus() { + return "Going to the bank"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToWintertodt.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToWintertodt.java new file mode 100644 index 0000000..18bbf43 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/GoToWintertodt.java @@ -0,0 +1,60 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Config; +import io.reisub.openosrs.wintertodt.Side; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.client.plugins.iutils.game.iObject; +import net.runelite.client.plugins.iutils.scene.Area; +import net.runelite.client.plugins.iutils.scene.Position; + +import javax.inject.Inject; + +import static net.runelite.api.ObjectID.DOORS_OF_DINH; + +public class GoToWintertodt extends Task { + @Inject + public Wintertodt plugin; + + @Inject + public Config config; + + @Override + public boolean validate() { + return !plugin.isInWintertodtRegion() + && game.inventory().withAction("Eat", "Drink").count() > 1; + } + + @Override + public void execute() { + iObject doorsOfDinh = game.objects().withId(DOORS_OF_DINH).first(); + if (doorsOfDinh == null) return; + + Position target = new Position(1630, 3962, 0); + + if (game.localPlayer().position().y < doorsOfDinh.position().y && game.localPlayer().position().distanceTo(doorsOfDinh.position()) > 7) { + walking.walkTo(target.areaWithin(calc.random(1, 9))); + game.tick(); + } + + doorsOfDinh.interact("Enter"); + game.tick(); + game.waitUntil(() -> game.localPlayer().position().y > 3965, 20); + + Side side = config.sideSelection(); + + if (plugin.getBossHealth() > 0) { + iObject roots = game.objects().withName("Bruma roots").nearest(game.localPlayer().position()); + roots.interact("Chop"); + } else { + final Area wintertodtSpot = plugin.getRespawnTimer() >= 5 ? side.getPositionNearBrazier() : side.getPositionNearRoots().areaWithin(1); + walking.walkTo(wintertodtSpot); + } + game.tick(); + } + + @Override + public String getStatus() { + return "Going to Wintertodt area"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/HandleBank.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/HandleBank.java new file mode 100644 index 0000000..013f4db --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/HandleBank.java @@ -0,0 +1,71 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Config; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.api.Skill; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +import static net.runelite.api.ItemID.*; + +public class HandleBank extends Task { + @Inject + public Wintertodt plugin; + + @Inject + public Config config; + + private iObject bankObj; + + @Override + public boolean validate() { + bankObj = game.objects().withAction("Bank").nearest(); + + return !plugin.isInWintertodtRegion() + && bank != null + && game.localPlayer().position().distanceTo(bankObj.position()) < calc.random(5, 7) + && (!config.openCrates() || !game.inventory().withId(SUPPLY_CRATE).exists()) + && game.inventory().withAction("Eat", "Drink").count() <= 1; + } + + @Override + public void execute() { + if (!bank.isOpen()) { + bankObj.interact("Bank"); + game.tick(); + + game.waitUntil(() -> bank.isOpen(), 15); + } + + bank.depositExcept(false, + BRONZE_AXE, + IRON_AXE, + STEEL_AXE, + BLACK_AXE, + MITHRIL_AXE, + ADAMANT_AXE, + RUNE_AXE, + DRAGON_AXE, + HAMMER, + KNIFE, + TINDERBOX, + CAKE, + _23_CAKE, + SLICE_OF_CAKE, + SALMON, + RAW_KARAMBWANJI); + game.tick(); + + int quantity = 10 + (game.baseLevel(Skill.HITPOINTS) - game.modifiedLevel(Skill.HITPOINTS)) / 12; + + bank.withdraw(SALMON, quantity, false); + game.waitUntil(() -> game.inventory().withAction("Eat", "Drink").count() > 1, 6); + } + + @Override + public String getStatus() { + return "Banking"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Hop.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Hop.java new file mode 100644 index 0000000..3fcfd53 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Hop.java @@ -0,0 +1,122 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.BossData; +import io.reisub.openosrs.wintertodt.Config; +import io.reisub.openosrs.wintertodt.Scouter; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.api.GameState; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.game.WorldService; +import net.runelite.client.util.WorldUtil; +import net.runelite.http.api.worlds.World; +import net.runelite.http.api.worlds.WorldResult; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; +import java.util.List; + +public class Hop extends Task { + @Inject + public Wintertodt plugin; + + @Inject + public Config config; + + @Inject + public Scouter scouter; + + @Inject + public WorldService worldService; + + private Instant lastAttempt; + + @Override + public String getStatus() { + return "Hopping to other world"; + } + + private BossData best; + private net.runelite.api.World quickHopTargetWorld; + private int displaySwitcherAttempts = 0; + + @Override + public boolean validate() { + if (!plugin.isInWintertodtRegion()) return false; + if (plugin.bossIsUp()) return false; + + best = findBestWorld(); + + return best.getWorld() != game.client().getWorld() + && (lastAttempt == null || Duration.between(lastAttempt, Instant.now()).getSeconds() >= 5); + } + + @Override + public void execute() { + WorldResult worldResult = worldService.getWorlds(); + if (worldResult == null) return; + + World world = worldResult.findWorld(best.getWorld()); + if (world == null) return; + + final net.runelite.api.World rsWorld = game.client().createWorld(); + rsWorld.setActivity(world.getActivity()); + rsWorld.setAddress(world.getAddress()); + rsWorld.setId(world.getId()); + rsWorld.setPlayerCount(world.getPlayers()); + rsWorld.setLocation(world.getLocation()); + rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes())); + + if (game.client().getGameState() == GameState.LOGIN_SCREEN) { + game.client().changeWorld(rsWorld); + return; + } + + quickHopTargetWorld = rsWorld; + displaySwitcherAttempts = 0; + lastAttempt = Instant.now(); + } + + public void onGameTick() { + if (quickHopTargetWorld == null) return; + + if (game.client().getWidget(WidgetInfo.WORLD_SWITCHER_LIST) == null) { + game.client().openWorldHopper(); + if (++displaySwitcherAttempts >= 5) { + logWarn("Failed to hop after 5 attempts"); + displaySwitcherAttempts = 0; + quickHopTargetWorld = null; + } + } else { + game.client().hopToWorld(quickHopTargetWorld); + plugin.setLastHop(Instant.now()); + quickHopTargetWorld = null; + } + } + + private BossData findBestWorld() { + List dataList = scouter.getData(); + + BossData best = scouter.getLocalData(); + + for (BossData data : dataList) { + // ignore data older than 5 seconds + if (System.currentTimeMillis() - (data.getTime() * 1000) >= 5000) continue; + + if (best.getHealth() > 0) { + if (data.getHealth() >= config.hopPercentage() && data.getHealth() < best.getHealth()) { + best = data; + } + } else { + if (data.getHealth() >= config.hopPercentage()) { + best = data; + } else if (data.getHealth() <= 0 && data.getTimer() < best.getTimer()) { + best = data; + } + } + } + + return best; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Light.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Light.java new file mode 100644 index 0000000..1733a0f --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/Light.java @@ -0,0 +1,47 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Wintertodt; + +import javax.inject.Inject; + +import static net.runelite.api.NpcID.INCAPACITATED_PYROMANCER; + +public class Light extends Task { + @Inject + public Wintertodt plugin; + + @Override + public String getStatus() { + return "Lighting the brazier"; + } + + @Override + public boolean validate() { + if (!plugin.isInWintertodtRegion()) return false; + + int distance = plugin.getCurrentActivity() == Activity.IDLE ? 8 : 2; + + return game.objects().within(distance).withAction("Light").exists() + && (plugin.getBossHealth() > 0 || plugin.getRespawnTimer() == 0) + && plugin.getCurrentActivity() != Activity.LIGHTING_BRAZIER + && !game.npcs().within(6).withId(INCAPACITATED_PYROMANCER).exists(); + } + + @Override + public void execute() { + int distance = plugin.getCurrentActivity() == Activity.IDLE ? 8 : 2; + + game.objects().within(distance).withAction("Light").findFirst().ifPresent((brazier) -> { + brazier.interact("Light"); + game.tick(); + }); + + if (distance == 2) { + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.LIGHTING_BRAZIER || !game.objects().within(distance).withAction("Light").exists(), 3); + } else { + game.waitUntil(() -> plugin.getCurrentActivity() == Activity.LIGHTING_BRAZIER || !game.objects().within(distance).withAction("Light").exists(), 6); + } + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/MoveToBrazier.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/MoveToBrazier.java new file mode 100644 index 0000000..965179b --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/MoveToBrazier.java @@ -0,0 +1,38 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import com.google.inject.Inject; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Activity; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.client.plugins.iutils.scene.Position; + +public class MoveToBrazier extends Task { + @Inject + public Wintertodt plugin; + + @Override + public String getStatus() { + return "Moving near brazier"; + } + + @Override + public boolean validate() { + if (!plugin.isInWintertodtRegion()) return false; + + if (plugin.isTooCold() + || (game.localPlayer().position().equals(new Position(1634, 3987, 0)) && plugin.getPreviousActivity() == Activity.FLETCHING)) { + return true; + } + + return plugin.getRespawnTimer() > 0 + && !game.localPlayer().position().equals(plugin.getNearestSide().getPositionNearBrazier()) + && !game.localPlayer().isMoving(); + } + + @Override + public void execute() { + plugin.setTooCold(false); + walking.walkTo(plugin.getNearestSide().getPositionNearBrazier()); + game.tick(); + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenCrates.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenCrates.java new file mode 100644 index 0000000..9211aef --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenCrates.java @@ -0,0 +1,48 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Config; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.client.plugins.iutils.game.iObject; + +import javax.inject.Inject; + +import static net.runelite.api.ItemID.*; + +public class OpenCrates extends Task { + @Inject + public Wintertodt plugin; + + @Inject + public Config config; + + @Override + public boolean validate() { + iObject bankObj = game.objects().withAction("Bank").nearest(); + + return !plugin.isInWintertodtRegion() + && game.localPlayer().position().distanceTo(bankObj.position()) < calc.random(5, 7) + && config.openCrates() + && game.inventory().withId(SUPPLY_CRATE).exists(); + } + + @Override + public void execute() { + game.inventory().withId(SUPPLY_CRATE).forEach((crate) -> { + crate.interact("Open"); + game.tick(); + game.sleepDelay(); + }); + + game.inventory().withId(PYROMANCER_BOOTS, PYROMANCER_GARB, PYROMANCER_HOOD, PYROMANCER_ROBE).forEach((outfit) -> { + outfit.interact("Wear"); + game.tick(calc.random(2, 4)); + game.sleepDelay(); + }); + } + + @Override + public String getStatus() { + return "Opening supply crates"; + } +} diff --git a/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenInventory.java b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenInventory.java new file mode 100644 index 0000000..7ea3962 --- /dev/null +++ b/wintertodt/src/main/java/io/reisub/openosrs/wintertodt/tasks/OpenInventory.java @@ -0,0 +1,32 @@ +package io.reisub.openosrs.wintertodt.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.wintertodt.Wintertodt; +import net.runelite.api.GameState; +import net.runelite.api.widgets.WidgetInfo; + +import javax.inject.Inject; + +public class OpenInventory extends Task { + @Inject + public Wintertodt plugin; + + @Override + public String getStatus() { + return "Opening inventory"; + } + + @Override + public boolean validate() { + return game.client().getGameState() == GameState.LOGGED_IN + && !bank.isOpen() + && (game.widget(WidgetInfo.INVENTORY) == null || game.widget(WidgetInfo.INVENTORY).hidden()); + } + + @Override + public void execute() { + game.sleepDelay(); + game.openInterface(3); + game.tick(); + } +} diff --git a/wintertodt/wintertodt.gradle.kts b/wintertodt/wintertodt.gradle.kts new file mode 100644 index 0000000..94330fc --- /dev/null +++ b/wintertodt/wintertodt.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Wintertodt" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Plays the Wintertodt game" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterConfig.java b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterConfig.java new file mode 100644 index 0000000..edd9150 --- /dev/null +++ b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterConfig.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, Andrew EP | ElPinche256 + * 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 io.reisub.openosrs.woodcutter; + +import net.runelite.client.config.Button; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("ChaosWoodcutterConfig") + +public interface WoodcutterConfig extends Config { + @ConfigItem( + keyName = "burnLogs", + name = "Burn logs", + description = "Enable to burn logs", + position = 90 + ) + default boolean burnLogs() { + return true; + } + + @ConfigItem( + keyName = "enableUI", + name = "Enable UI", + description = "Enable to turn on in game UI", + position = 95 + ) + default boolean enableUI() { + return true; + } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Start the script", + position = 100 + ) + default Button startButton() { + return new Button(); + } +} \ No newline at end of file diff --git a/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterPlugin.java b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterPlugin.java new file mode 100644 index 0000000..57e24be --- /dev/null +++ b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/WoodcutterPlugin.java @@ -0,0 +1,75 @@ +package io.reisub.openosrs.woodcutter; + +import com.google.inject.Provides; +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.util.Util; +import io.reisub.openosrs.woodcutter.tasks.Chop; +import io.reisub.openosrs.woodcutter.tasks.Drop; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.plugins.iutils.scripts.iScript; +import org.pf4j.Extension; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Extension +@PluginDependency(Util.class) +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "Chaos Woodcutter", + description = "Cuts wood", + enabledByDefault = false +) +@Slf4j +public class WoodcutterPlugin extends iScript { + @Inject + private Client client; + + private List tasks; + + @Provides + WoodcutterConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(WoodcutterConfig.class); + } + + @Override + protected void loop() { + for (Task t : tasks) { + if (t.validate()) { + t.execute(); + } + } + + game.tickDelay(); + } + + @Override + protected void onStart() { + log.info("Starting Chaos Woodcutter"); + + tasks = new ArrayList<>(); + tasks.add(injector.getInstance(Chop.class)); + tasks.add(injector.getInstance(Drop.class)); + } + + @Override + protected void onStop() { + log.info("Stopping Chaos Woodcutter"); + tasks.clear(); + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) { + if (configButtonClicked.getKey().equals("startButton")) { + execute(); + } + } +} \ No newline at end of file diff --git a/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Chop.java b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Chop.java new file mode 100644 index 0000000..303f81b --- /dev/null +++ b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Chop.java @@ -0,0 +1,39 @@ +package io.reisub.openosrs.woodcutter.tasks; + +import io.reisub.openosrs.util.Task; +import net.runelite.client.plugins.iutils.game.iObject; + +public class Chop extends Task { + private iObject tree; + + @Override + public String getStatus() { + return "Chopping"; + } + + @Override + public boolean validate() { + if (!game.localPlayer().isIdle() || game.inventory().full()) { + return false; + } + + tree = game.objects().withName("Tree").within(9).nearestPath(); + + if (tree == null) { + tree = game.objects().withName("Tree").nearest(); + } + + return tree != null; + } + + @Override + public void execute() { + tree.interact("Chop down"); + + if (!game.waitUntil(() -> !game.localPlayer().isIdle(), 5)) { + logWarn("Failed to start chopping tree"); + } + + tree = null; + } +} diff --git a/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Drop.java b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Drop.java new file mode 100644 index 0000000..e336a5a --- /dev/null +++ b/woodcutter/src/main/java/io/reisub/openosrs/woodcutter/tasks/Drop.java @@ -0,0 +1,31 @@ +package io.reisub.openosrs.woodcutter.tasks; + +import io.reisub.openosrs.util.Task; +import io.reisub.openosrs.woodcutter.WoodcutterConfig; + +import javax.inject.Inject; + +public class Drop extends Task { + @Inject + private WoodcutterConfig config; + + @Override + public String getStatus() { + return "Dropping"; + } + + @Override + public boolean validate() { + return !config.burnLogs() + && game.inventory().full() + && game.inventory().withNamePart("logs", "Logs").findAny().isPresent(); + } + + @Override + public void execute() { + game.inventory().withNamePart("logs", "Logs").forEach((item -> { + item.interact("Drop"); + game.sleepDelay(); + })); + } +} diff --git a/woodcutter/woodcutter.gradle.kts b/woodcutter/woodcutter.gradle.kts new file mode 100644 index 0000000..150f034 --- /dev/null +++ b/woodcutter/woodcutter.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "0.0.1" + +project.extra["PluginName"] = "Chaos Woodcutter" // This is the name that is used in the external plugin manager panel +project.extra["PluginDescription"] = "Chops wood" // This is the description that is used in the external plugin manager panel + +dependencies { + compileOnly(project(":util")) +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-Dependencies" to + arrayOf( + nameToId("Chaos Util"), + nameToId("iUtils") + ).joinToString(), + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file