commit 57ba5a58580a65f19ad2855a608bbff5ec4dbfb1 Author: Yuri Moens Date: Sat Jan 8 19:46:18 2022 +0100 Initial commit 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 0000000..f3d88b1 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ 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 0000000..3c601ae Binary files /dev/null and b/release/agility-0.0.1.jar differ diff --git a/release/autobones-1.0.0.jar b/release/autobones-1.0.0.jar new file mode 100644 index 0000000..a48bb3c Binary files /dev/null and b/release/autobones-1.0.0.jar differ diff --git a/release/autodropper-1.0.0.jar b/release/autodropper-1.0.0.jar new file mode 100644 index 0000000..426703e Binary files /dev/null and b/release/autodropper-1.0.0.jar differ diff --git a/release/birdhouse-1.0.0.jar b/release/birdhouse-1.0.0.jar new file mode 100644 index 0000000..7b87b6a Binary files /dev/null and b/release/birdhouse-1.0.0.jar differ diff --git a/release/blackjack-0.0.1.jar b/release/blackjack-0.0.1.jar new file mode 100644 index 0000000..d5f2682 Binary files /dev/null and b/release/blackjack-0.0.1.jar differ diff --git a/release/consume-0.0.1.jar b/release/consume-0.0.1.jar new file mode 100644 index 0000000..a08e17a Binary files /dev/null and b/release/consume-0.0.1.jar differ diff --git a/release/consume-1.0.0.jar b/release/consume-1.0.0.jar new file mode 100644 index 0000000..64c23b7 Binary files /dev/null and b/release/consume-1.0.0.jar differ diff --git a/release/cooker-0.0.1.jar b/release/cooker-0.0.1.jar new file mode 100644 index 0000000..93a4ab5 Binary files /dev/null and b/release/cooker-0.0.1.jar differ diff --git a/release/fisher-0.0.1.jar b/release/fisher-0.0.1.jar new file mode 100644 index 0000000..9b13c9a Binary files /dev/null and b/release/fisher-0.0.1.jar differ diff --git a/release/glassblower-1.0.0.jar b/release/glassblower-1.0.0.jar new file mode 100644 index 0000000..5e49601 Binary files /dev/null and b/release/glassblower-1.0.0.jar differ diff --git a/release/masterthiever-1.0.0.jar b/release/masterthiever-1.0.0.jar new file mode 100644 index 0000000..fb7e3d5 Binary files /dev/null and b/release/masterthiever-1.0.0.jar differ diff --git a/release/miner-1.0.0.jar b/release/miner-1.0.0.jar new file mode 100644 index 0000000..143df8a Binary files /dev/null and b/release/miner-1.0.0.jar differ diff --git a/release/prayerflick-1.0.0.jar b/release/prayerflick-1.0.0.jar new file mode 100644 index 0000000..b51c71e Binary files /dev/null and b/release/prayerflick-1.0.0.jar differ diff --git a/release/prayerflick-1.1.0.jar b/release/prayerflick-1.1.0.jar new file mode 100644 index 0000000..208a3ba Binary files /dev/null and b/release/prayerflick-1.1.0.jar differ diff --git a/release/shopper-1.0.0.jar b/release/shopper-1.0.0.jar new file mode 100644 index 0000000..921dd74 Binary files /dev/null and b/release/shopper-1.0.0.jar differ diff --git a/release/smelter-1.0.0.jar b/release/smelter-1.0.0.jar new file mode 100644 index 0000000..1314e6a Binary files /dev/null and b/release/smelter-1.0.0.jar differ diff --git a/release/tempoross-1.0.0.jar b/release/tempoross-1.0.0.jar new file mode 100644 index 0000000..1c40daa Binary files /dev/null and b/release/tempoross-1.0.0.jar differ diff --git a/release/test-0.0.1.jar b/release/test-0.0.1.jar new file mode 100644 index 0000000..dd529e8 Binary files /dev/null and b/release/test-0.0.1.jar differ diff --git a/release/util-1.0.0.jar b/release/util-1.0.0.jar new file mode 100644 index 0000000..c9e824a Binary files /dev/null and b/release/util-1.0.0.jar differ diff --git a/release/wintertodt-0.0.1.jar b/release/wintertodt-0.0.1.jar new file mode 100644 index 0000000..412b7dc Binary files /dev/null and b/release/wintertodt-0.0.1.jar differ diff --git a/release/woodcutter-0.0.1.jar b/release/woodcutter-0.0.1.jar new file mode 100644 index 0000000..16b77c1 Binary files /dev/null and b/release/woodcutter-0.0.1.jar differ 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